diff --git a/.github/workflows/build_and_run_chain_simulator_and_execute_system_test.yml b/.github/workflows/build_and_run_chain_simulator_and_execute_system_test.yml new file mode 100644 index 00000000000..f766ccddbf5 --- /dev/null +++ b/.github/workflows/build_and_run_chain_simulator_and_execute_system_test.yml @@ -0,0 +1,316 @@ +name: Chain Simulator Build and Integration Test + +on: + pull_request: + branches: + - 'main' + - 'master' + - 'rc/*' + workflow_dispatch: + issue_comment: + types: [created] + +permissions: + issues: write + pull-requests: write + contents: read + +jobs: + build-and-test: + if: | + github.event_name == 'pull_request' || + (github.event_name == 'issue_comment' && contains(github.event.comment.body, 'Run Tests:')) || + github.event_name == 'workflow_dispatch' + + strategy: + matrix: + #TODO Include Macos support later on + runs-on: [ubuntu-latest] + runs-on: ${{ matrix.runs-on }} + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + TARGET_BRANCH: "" + MX_CHAIN_GO_TARGET_BRANCH: "" + MX_CHAIN_SIMULATOR_TARGET_BRANCH: "" + MX_CHAIN_TESTING_SUITE_TARGET_BRANCH: "" + + steps: + - name: Determine Target Branches + id: target_branch + run: | + echo "CURRENT_BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" >> $GITHUB_ENV + + # Default target branches based on the PR base branch + if [[ "${{ github.event.pull_request.base.ref }}" == "main" ]]; then + echo "MX_CHAIN_SIMULATOR_TARGET_BRANCH=main" >> $GITHUB_ENV + echo "MX_CHAIN_TESTING_SUITE_TARGET_BRANCH=main" >> $GITHUB_ENV + elif [[ "${{ github.event.pull_request.base.ref }}" == "master" ]]; then + echo "MX_CHAIN_SIMULATOR_TARGET_BRANCH=main" >> $GITHUB_ENV + echo "MX_CHAIN_TESTING_SUITE_TARGET_BRANCH=main" >> $GITHUB_ENV + else + echo "MX_CHAIN_SIMULATOR_TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV + echo "MX_CHAIN_TESTING_SUITE_TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV + fi + + # Always set MX_CHAIN_GO_TARGET_BRANCH based on the PR base branch + echo "MX_CHAIN_GO_TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV + + + - name: Fetch and Parse Last Comment for Branches + uses: actions/github-script@v7 + id: fetch_and_parse_last_comment + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Get the latest comment + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const lastComment = comments.data.pop(); // Get the last comment + + if (lastComment && lastComment.body.includes('Run Tests:')) { + const body = lastComment.body.trim(); + core.setOutput('latest_comment', body); + + // Parse the branches from the last comment + const simulatorBranchMatch = body.match(/mx-chain-simulator-go:\s*(\S+)/); + const testingSuiteBranchMatch = body.match(/mx-chain-testing-suite:\s*(\S+)/); + + // Override the target branches if specified + if (simulatorBranchMatch) { + core.exportVariable('MX_CHAIN_SIMULATOR_TARGET_BRANCH', simulatorBranchMatch[1]); + } + if (testingSuiteBranchMatch) { + core.exportVariable('MX_CHAIN_TESTING_SUITE_TARGET_BRANCH', testingSuiteBranchMatch[1]); + } + } else { + core.info('The last comment does not contain "Run Tests:". Skipping branch override.'); + } + + + - name: Print Target Branches + run: | + echo "Current branch mx-chain-go: ${{ env.CURRENT_BRANCH }}" + echo "mx-chain-go target branch: ${{ env.MX_CHAIN_GO_TARGET_BRANCH }}" + echo "mx-chain-simulator-go target branch: ${{ env.MX_CHAIN_SIMULATOR_TARGET_BRANCH }}" + echo "mx-chain-testing-suite target branch: ${{ env.MX_CHAIN_TESTING_SUITE_TARGET_BRANCH }}" + + - name: Set up Go 1.20.7 + uses: actions/setup-go@v3 + with: + go-version: 1.20.7 + id: go + + - name: Checkout mx-chain-go + uses: actions/checkout@v4 + with: + repository: 'multiversx/mx-chain-go' + ref: ${{ github.head_ref }} + fetch-depth: 0 + path: 'mx-chain-go' + + - name: Get Latest mx-chain-go Commit Hash + run: | + cd mx-chain-go + current_branch=$(git symbolic-ref --short HEAD) + echo "CURRENT_BRANCH=${current_branch}" >> $GITHUB_ENV + git fetch origin ${current_branch} --prune + latest_commit_hash=$(git rev-parse origin/${current_branch}) + echo "LATEST_COMMIT_HASH=${latest_commit_hash}" >> $GITHUB_ENV + echo "Latest commit hash: ${latest_commit_hash}" + + - name: Checkout mx-chain-simulator-go + uses: actions/checkout@v4 + with: + repository: 'multiversx/mx-chain-simulator-go' + ref: ${{ env.MX_CHAIN_SIMULATOR_TARGET_BRANCH || github.event.pull_request.base.ref }} + path: 'mx-chain-simulator-go' + + - name: Set up Python 3.10 + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Install Python Dependencies and Update go.mod + run: | + cd mx-chain-simulator-go + pip install -r scripts/update-go-mod/requirements.txt + python scripts/update-go-mod/update-go-mod.py $LATEST_COMMIT_HASH + + - name: Run go build + run: | + cd mx-chain-simulator-go/cmd/chainsimulator + go build + echo "CHAIN_SIMULATOR_BUILD_PATH=$(pwd)" >> $GITHUB_ENV + + - name: Checkout mx-chain-testing-suite + uses: actions/checkout@v4 + with: + repository: 'multiversx/mx-chain-testing-suite' + path: 'mx-chain-testing-suite' + fetch-depth: 0 + ref: ${{ env.MX_CHAIN_TESTING_SUITE_TARGET_BRANCH || github.event.pull_request.base.ref }} + token: ${{ secrets.MVX_TESTER_GH_TOKEN }} + + - name: Install Dependencies + run: | + pip install -r mx-chain-testing-suite/requirements.txt + echo "PYTHONPATH=mx-chain-testing-suite" >> $GITHUB_ENV + + + - name: Run tests and generate HTML report + run: | + set +e + pytest mx-chain-testing-suite/scenarios/ --html=report.html --self-contained-html --continue-on-collection-errors + PYTEST_EXIT_CODE=$? + set -e + echo "PYTEST_EXIT_CODE=$PYTEST_EXIT_CODE" >> $GITHUB_ENV + echo "Pytest exit code: $PYTEST_EXIT_CODE" + if [ -f "report.html" ]; then + echo "Report generated successfully." + mkdir -p ./reports + mv report.html ./reports/ + else + echo "Report not found." + fi + + - name: Upload test report + if: always() + uses: actions/upload-artifact@v3 + with: + name: pytest-report-${{ github.run_id }} + path: reports/report.html + + - name: Deploy Report to GitHub Pages + if: always() + id: deploy_report + run: | + # Navigate to the mx-chain-testing-suite directory + cd mx-chain-testing-suite + + # Configure Git user + git config user.name "GitHub Action" + git config user.email "action@github.com" + + # Check if the report exists + if [ -f "../reports/report.html" ]; then + # Ensure we're on the 'gh-pages' branch and up to date + git fetch --all + git checkout gh-pages || git checkout --orphan gh-pages + + # Create a new directory for the report based on the current timestamp + TIMESTAMP=$(date +'%d%m%Y-%H%M%S') + echo "TIMESTAMP=$TIMESTAMP" >> $GITHUB_ENV + REPORT_DIR="reports/${BRANCH_NAME}/${TIMESTAMP}" + mkdir -p $REPORT_DIR + + # Move the report into the new directory + cp ../reports/report.html $REPORT_DIR/index.html + + # Add and commit only the new report + git add $REPORT_DIR/index.html + git commit -m "Deploy Test Report at $BRANCH_NAME/$TIMESTAMP" + + # Set remote URL with authentication token + git remote set-url origin https://x-access-token:${{ secrets.MVX_TESTER_GH_TOKEN }}@github.com/multiversx/mx-chain-testing-suite.git + + # Push changes to the remote 'gh-pages' branch + git push --force origin gh-pages + else + echo "Report file not found, skipping deployment." + fi + + + - name: Update Index Page + if: always() + run: | + cd mx-chain-testing-suite + git fetch --all + git checkout gh-pages || git checkout --orphan gh-pages + if [ -d "docs" ]; then + cd docs + echo "

Test Reports

" >> index.html + git add index.html + git commit -m "Update Index of Reports" + git push origin gh-pages --force + else + mkdir -p docs + cd docs + echo "

Test Reports

" >> index.html + echo "Docs directory was not found and has been created." + fi + + - name: Comment PR with report link or error message + if: always() + uses: actions/github-script@v7 + env: + TIMESTAMP: ${{ env.TIMESTAMP }} + BRANCH_NAME: ${{ env.BRANCH_NAME }} + CURRENT_BRANCH: ${{ env.CURRENT_BRANCH }} + MX_CHAIN_GO_TARGET_BRANCH: ${{ env.MX_CHAIN_GO_TARGET_BRANCH }} + MX_CHAIN_SIMULATOR_TARGET_BRANCH: ${{ env.MX_CHAIN_SIMULATOR_TARGET_BRANCH }} + MX_CHAIN_TESTING_SUITE_TARGET_BRANCH: ${{ env.MX_CHAIN_TESTING_SUITE_TARGET_BRANCH }} + LATEST_COMMIT_HASH: ${{ env.LATEST_COMMIT_HASH }} + PYTEST_EXIT_CODE: ${{ env.PYTEST_EXIT_CODE }} + + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const timestamp = process.env.TIMESTAMP; + const branchName = process.env.BRANCH_NAME; + const currentBranch = process.env.CURRENT_BRANCH; + const goTargetBranch = process.env.MX_CHAIN_GO_TARGET_BRANCH; + const simulatorTargetBranch = process.env.MX_CHAIN_SIMULATOR_TARGET_BRANCH; + const testingSuiteTargetBranch = process.env.MX_CHAIN_TESTING_SUITE_TARGET_BRANCH; + const commitHash = process.env.LATEST_COMMIT_HASH; + const exitCode = process.env.PYTEST_EXIT_CODE; + const issue_number = context.issue.number; + const owner = context.repo.owner; + const repo = context.repo.repo; + let message; + + if (timestamp && branchName && timestamp !== "" && branchName !== "") { + const reportUrl = `https://multiversx.github.io/mx-chain-testing-suite/reports/${branchName}/${timestamp}/index.html`; + message = ` + 📊 **MultiversX Automated Test Report:** [View Report](${reportUrl}) + + 🔄 **Build Details:** + - **mx-chain-go Commit Hash:** \`${commitHash}\` + - **Current Branch:** \`${currentBranch}\` + - **mx-chain-go Target Branch:** \`${goTargetBranch}\` + - **mx-chain-simulator-go Target Branch:** \`${simulatorTargetBranch}\` + - **mx-chain-testing-suite Target Branch:** \`${testingSuiteTargetBranch}\` + + 🚀 **Environment Variables:** + - **TIMESTAMP:** \`${timestamp}\` + - **PYTEST_EXIT_CODE:** \`${exitCode}\` + 🎉 **MultiversX CI/CD Workflow Complete!** + `; + } else { + message = "⚠️ No report was generated due to an error or cancellation of the process.\nPlease checkout gh action logs for details"; + } + + github.rest.issues.createComment({ + issue_number: issue_number, + owner: owner, + repo: repo, + body: message + }); + + - name: Fail job if tests failed + if: always() + run: | + if [ "${{ env.PYTEST_EXIT_CODE }}" != "0" ]; then + echo "Tests failed with exit code ${{ env.PYTEST_EXIT_CODE }}" + exit 1 + else + echo "Tests passed successfully." + fi \ No newline at end of file diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index b43adf3ef0e..8f8985811cc 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -9,7 +9,7 @@ jobs: build: strategy: matrix: - runs-on: [ubuntu-latest, macos-13-xlarge] + runs-on: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.runs-on }} name: Build steps: diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 81fd087a704..bac2bab26b7 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -15,7 +15,7 @@ jobs: build: strategy: matrix: - runs-on: [ubuntu-latest, macos-13-xlarge] + runs-on: [ubuntu-latest] runs-on: ${{ matrix.runs-on }} name: Build steps: @@ -129,7 +129,7 @@ jobs: zip -r -j ${ARCHIVE} ${BUILD_DIR} - name: Save artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.ARCHIVE }} path: ${{ env.ARCHIVE }} @@ -145,7 +145,7 @@ jobs: # https://docs.github.com/en/free-pro-team@latest/actions/guides/storing-workflow-data-as-artifacts#downloading-or-deleting-artifacts # A directory for each artifact is created using its name - name: Download all workflow run artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: path: assets diff --git a/.github/workflows/docker-keygenerator.yaml b/.github/workflows/docker-keygenerator.yaml index 5e4d9d44a32..c3cca7fc279 100644 --- a/.github/workflows/docker-keygenerator.yaml +++ b/.github/workflows/docker-keygenerator.yaml @@ -2,7 +2,6 @@ name: Build & push keygenerator docker image on: workflow_dispatch: - pull_request: jobs: build-docker-image: @@ -19,7 +18,6 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log into Docker Hub - if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} @@ -32,5 +30,5 @@ jobs: context: . file: ./docker/keygenerator/Dockerfile platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} + push: true tags: multiversx/chain-keygenerator:latest diff --git a/api/errors/errors.go b/api/errors/errors.go index b01cec657ca..3f4e495b9d2 100644 --- a/api/errors/errors.go +++ b/api/errors/errors.go @@ -64,6 +64,9 @@ var ErrTxGenerationFailed = errors.New("transaction generation failed") // ErrValidationEmptyTxHash signals that an empty tx hash was provided var ErrValidationEmptyTxHash = errors.New("TxHash is empty") +// ErrValidationEmptySCRHash signals that provided smart contract result hash is empty +var ErrValidationEmptySCRHash = errors.New("SCRHash is empty") + // ErrInvalidBlockNonce signals that an invalid block nonce was provided var ErrInvalidBlockNonce = errors.New("invalid block nonce") @@ -79,6 +82,9 @@ var ErrValidationEmptyBlockHash = errors.New("block hash is empty") // ErrGetTransaction signals an error happening when trying to fetch a transaction var ErrGetTransaction = errors.New("getting transaction failed") +// ErrGetSmartContractResults signals an error happening when trying to fetch smart contract results +var ErrGetSmartContractResults = errors.New("getting smart contract results failed") + // ErrGetBlock signals an error happening when trying to fetch a block var ErrGetBlock = errors.New("getting block failed") @@ -174,3 +180,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") diff --git a/api/groups/blockGroup.go b/api/groups/blockGroup.go index 26d9c05b000..f9f0ed81fd8 100644 --- a/api/groups/blockGroup.go +++ b/api/groups/blockGroup.go @@ -26,6 +26,7 @@ const ( urlParamTokensFilter = "tokens" urlParamWithTxs = "withTxs" urlParamWithLogs = "withLogs" + urlParamForHyperblock = "forHyperblock" ) // blockFacadeHandler defines the methods to be implemented by a facade for handling block requests @@ -219,7 +220,12 @@ func parseBlockQueryOptions(c *gin.Context) (api.BlockQueryOptions, error) { return api.BlockQueryOptions{}, err } - options := api.BlockQueryOptions{WithTransactions: withTxs, WithLogs: withLogs} + forHyperBlock, err := parseBoolUrlParam(c, urlParamForHyperblock) + if err != nil { + return api.BlockQueryOptions{}, err + } + + options := api.BlockQueryOptions{WithTransactions: withTxs, WithLogs: withLogs, ForHyperblock: forHyperBlock} return options, nil } diff --git a/api/groups/blockGroup_test.go b/api/groups/blockGroup_test.go index b190c2f0561..a2ebc61548b 100644 --- a/api/groups/blockGroup_test.go +++ b/api/groups/blockGroup_test.go @@ -90,7 +90,7 @@ func TestBlockGroup_getBlockByNonce(t *testing.T) { t.Parallel() providedNonce := uint64(37) - expectedOptions := api.BlockQueryOptions{WithTransactions: true} + expectedOptions := api.BlockQueryOptions{WithTransactions: true, ForHyperblock: true} expectedBlock := api.Block{ Nonce: 37, Round: 39, @@ -107,7 +107,7 @@ func TestBlockGroup_getBlockByNonce(t *testing.T) { loadBlockGroupResponse( t, facade, - fmt.Sprintf("/block/by-nonce/%d?withTxs=true", providedNonce), + fmt.Sprintf("/block/by-nonce/%d?withTxs=true&forHyperblock=true", providedNonce), "GET", nil, response, diff --git a/api/groups/networkGroup.go b/api/groups/networkGroup.go index ae0cca6846d..8a8e7854f9d 100644 --- a/api/groups/networkGroup.go +++ b/api/groups/networkGroup.go @@ -117,7 +117,7 @@ func NewNetworkGroup(facade networkFacadeHandler) (*networkGroup, error) { { Path: getNFTsPath, Method: http.MethodGet, - Handler: ng.getHandlerFuncForEsdt(core.NonFungibleESDT), + Handler: ng.getHandlerFuncForEsdt(core.NonFungibleESDTv2), }, { Path: directStakedInfoPath, diff --git a/api/groups/networkGroup_test.go b/api/groups/networkGroup_test.go index 3eb52a4a0c0..c809a632b56 100644 --- a/api/groups/networkGroup_test.go +++ b/api/groups/networkGroup_test.go @@ -206,6 +206,36 @@ func TestNetworkConfigMetrics_GasLimitGuardedTxShouldWork(t *testing.T) { assert.True(t, keyAndValueFoundInResponse) } +func TestNetworkConfigMetrics_GasLimitRelayedTxShouldWork(t *testing.T) { + t.Parallel() + + statusMetricsProvider := statusHandler.NewStatusMetrics() + key := common.MetricExtraGasLimitRelayedTx + val := uint64(123) + statusMetricsProvider.SetUInt64Value(key, val) + + facade := mock.FacadeStub{} + facade.StatusMetricsHandler = func() external.StatusMetricsHandler { + return statusMetricsProvider + } + + networkGroup, err := groups.NewNetworkGroup(&facade) + require.NoError(t, err) + + ws := startWebServer(networkGroup, "network", getNetworkRoutesConfig()) + + req, _ := http.NewRequest("GET", "/network/config", nil) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + respBytes, _ := io.ReadAll(resp.Body) + respStr := string(respBytes) + assert.Equal(t, resp.Code, http.StatusOK) + + keyAndValueFoundInResponse := strings.Contains(respStr, key) && strings.Contains(respStr, fmt.Sprintf("%d", val)) + assert.True(t, keyAndValueFoundInResponse) +} + func TestNetworkStatusMetrics_ShouldWork(t *testing.T) { t.Parallel() diff --git a/api/groups/transactionGroup.go b/api/groups/transactionGroup.go index 3c62221d121..86f04dfca22 100644 --- a/api/groups/transactionGroup.go +++ b/api/groups/transactionGroup.go @@ -26,11 +26,13 @@ const ( simulateTransactionEndpoint = "/transaction/simulate" sendMultipleTransactionsEndpoint = "/transaction/send-multiple" getTransactionEndpoint = "/transaction/:hash" + getScrsByTxHashEndpoint = "/transaction/scrs-by-tx-hash/:txhash" sendTransactionPath = "/send" simulateTransactionPath = "/simulate" costPath = "/cost" sendMultiplePath = "/send-multiple" getTransactionPath = "/:txhash" + getScrsByTxHashPath = "/scrs-by-tx-hash/:txhash" getTransactionsPool = "/pool" queryParamWithResults = "withResults" @@ -39,6 +41,7 @@ const ( queryParamFields = "fields" queryParamLastNonce = "last-nonce" queryParamNonceGaps = "nonce-gaps" + queryParameterScrHash = "scrHash" ) // transactionFacadeHandler defines the methods to be implemented by a facade for transaction requests @@ -49,6 +52,7 @@ type transactionFacadeHandler interface { SendBulkTransactions([]*transaction.Transaction) (uint64, error) SimulateTransactionExecution(tx *transaction.Transaction) (*txSimData.SimulationResultsWithVMOutput, error) GetTransaction(hash string, withResults bool) (*transaction.ApiTransactionResult, error) + GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) GetTransactionsPool(fields string) (*common.TransactionsPoolAPIResponse, error) GetTransactionsPoolForSender(sender, fields string) (*common.TransactionsPoolForSenderApiResponse, error) GetLastPoolNonceForSender(sender string) (uint64, error) @@ -137,6 +141,17 @@ func NewTransactionGroup(facade transactionFacadeHandler) (*transactionGroup, er }, }, }, + { + Path: getScrsByTxHashPath, + Method: http.MethodGet, + Handler: tg.getScrsByTxHash, + AdditionalMiddlewares: []shared.AdditionalMiddleware{ + { + Middleware: middleware.CreateEndpointThrottlerFromFacade(getScrsByTxHashEndpoint, facade), + Position: shared.Before, + }, + }, + }, } tg.endpoints = endpoints @@ -182,27 +197,7 @@ 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, - } - start := time.Now() - tx, txHash, err := tg.getFacade().CreateTransaction(txArgs) - logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction") - + tx, txHash, err := tg.createTransaction(&ftx) if err != nil { c.JSON( http.StatusBadRequest, @@ -215,7 +210,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 { @@ -272,26 +267,7 @@ 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, - } - start := time.Now() - tx, txHash, err := tg.getFacade().CreateTransaction(txArgs) - logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction") + tx, txHash, err := tg.createTransaction(&ftx) if err != nil { c.JSON( http.StatusBadRequest, @@ -304,7 +280,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 { @@ -370,25 +346,7 @@ 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, - } - tx, txHash, err = tg.getFacade().CreateTransaction(txArgs) - logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction") + tx, txHash, err = tg.createTransaction(&receivedTx) if err != nil { continue } @@ -430,6 +388,57 @@ func (tg *transactionGroup) sendMultipleTransactions(c *gin.Context) { ) } +func (tg *transactionGroup) getScrsByTxHash(c *gin.Context) { + txhash := c.Param("txhash") + if txhash == "" { + c.JSON( + http.StatusBadRequest, + shared.GenericAPIResponse{ + Data: nil, + Error: fmt.Sprintf("%s: %s", errors.ErrValidation.Error(), errors.ErrValidationEmptyTxHash.Error()), + Code: shared.ReturnCodeRequestError, + }, + ) + return + } + scrHashStr := c.Request.URL.Query().Get(queryParameterScrHash) + if scrHashStr == "" { + c.JSON( + http.StatusBadRequest, + shared.GenericAPIResponse{ + Data: nil, + Error: fmt.Sprintf("%s: %s", errors.ErrValidation.Error(), errors.ErrValidationEmptySCRHash.Error()), + Code: shared.ReturnCodeRequestError, + }, + ) + return + } + + start := time.Now() + scrs, err := tg.getFacade().GetSCRsByTxHash(txhash, scrHashStr) + if err != nil { + c.JSON( + http.StatusInternalServerError, + shared.GenericAPIResponse{ + Data: nil, + Error: fmt.Sprintf("%s: %s", errors.ErrGetSmartContractResults.Error(), err.Error()), + Code: shared.ReturnCodeInternalError, + }, + ) + return + } + logging.LogAPIActionDurationIfNeeded(start, "API call: GetSCRsByTxHash") + + c.JSON( + http.StatusOK, + shared.GenericAPIResponse{ + Data: gin.H{"scrs": scrs}, + Error: "", + Code: shared.ReturnCodeSuccess, + }, + ) +} + // getTransaction returns transaction details for a given txhash func (tg *transactionGroup) getTransaction(c *gin.Context) { txhash := c.Param("txhash") @@ -499,26 +508,7 @@ 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, - } - start := time.Now() - tx, _, err := tg.getFacade().CreateTransaction(txArgs) - logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction") + tx, _, err := tg.createTransaction(&ftx) if err != nil { c.JSON( http.StatusInternalServerError, @@ -531,7 +521,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 { @@ -728,6 +718,33 @@ func (tg *transactionGroup) getTransactionsPoolNonceGapsForSender(sender string, ) } +func (tg *transactionGroup) createTransaction(receivedTx *transaction.FrontendTransaction) (*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.RelayerAddr, + RelayerSignatureHex: receivedTx.RelayerSignature, + } + 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 diff --git a/api/groups/transactionGroup_test.go b/api/groups/transactionGroup_test.go index 22085956fe9..9f603412ae0 100644 --- a/api/groups/transactionGroup_test.go +++ b/api/groups/transactionGroup_test.go @@ -342,6 +342,76 @@ func TestTransactionGroup_sendTransaction(t *testing.T) { }) } +func TestTransactionsGroup_getSCRsByTxHash(t *testing.T) { + t.Parallel() + + t.Run("get SCRsByTxHash empty scr hash should error", func(t *testing.T) { + facade := &mock.FacadeStub{} + + transactionGroup, err := groups.NewTransactionGroup(facade) + require.NoError(t, err) + + ws := startWebServer(transactionGroup, "transaction", getTransactionRoutesConfig()) + + req, _ := http.NewRequest(http.MethodGet, "/transaction/scrs-by-tx-hash/txHash", bytes.NewBuffer([]byte{})) + 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.ErrValidationEmptySCRHash.Error())) + assert.Empty(t, txResp.Data) + }) + t.Run("get scrs facade error", func(t *testing.T) { + localErr := fmt.Errorf("error") + facade := &mock.FacadeStub{ + GetSCRsByTxHashCalled: func(txHash string, scrHash string) ([]*dataTx.ApiSmartContractResult, error) { + return nil, localErr + }, + } + + transactionGroup, err := groups.NewTransactionGroup(facade) + require.NoError(t, err) + + ws := startWebServer(transactionGroup, "transaction", getTransactionRoutesConfig()) + + req, _ := http.NewRequest(http.MethodGet, "/transaction/scrs-by-tx-hash/txhash?scrHash=hash", bytes.NewBuffer([]byte{})) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + txResp := shared.GenericAPIResponse{} + loadResponse(resp.Body, &txResp) + + assert.Equal(t, http.StatusInternalServerError, resp.Code) + assert.True(t, strings.Contains(txResp.Error, localErr.Error())) + assert.Empty(t, txResp.Data) + }) + t.Run("get scrs should work", func(t *testing.T) { + facade := &mock.FacadeStub{ + GetSCRsByTxHashCalled: func(txHash string, scrHash string) ([]*dataTx.ApiSmartContractResult, error) { + return []*dataTx.ApiSmartContractResult{}, nil + }, + } + + transactionGroup, err := groups.NewTransactionGroup(facade) + require.NoError(t, err) + + ws := startWebServer(transactionGroup, "transaction", getTransactionRoutesConfig()) + + req, _ := http.NewRequest(http.MethodGet, "/transaction/scrs-by-tx-hash/txhash?scrHash=hash", bytes.NewBuffer([]byte{})) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + txResp := shared.GenericAPIResponse{} + loadResponse(resp.Body, &txResp) + + assert.Equal(t, http.StatusOK, resp.Code) + assert.Equal(t, "", txResp.Error) + }) +} + func TestTransactionGroup_sendMultipleTransactions(t *testing.T) { t.Parallel() @@ -1122,6 +1192,7 @@ func getTransactionRoutesConfig() config.ApiRoutesConfig { {Name: "/:txhash", Open: true}, {Name: "/:txhash/status", Open: true}, {Name: "/simulate", Open: true}, + {Name: "/scrs-by-tx-hash/:txhash", Open: true}, }, }, }, diff --git a/api/mock/facadeStub.go b/api/mock/facadeStub.go index e40645c1ac3..62de2febc81 100644 --- a/api/mock/facadeStub.go +++ b/api/mock/facadeStub.go @@ -97,6 +97,16 @@ type FacadeStub struct { GetWaitingEpochsLeftForPublicKeyCalled func(publicKey string) (uint32, error) P2PPrometheusMetricsEnabledCalled func() bool AuctionListHandler func() ([]*common.AuctionListValidatorAPIResponse, error) + GetSCRsByTxHashCalled func(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) +} + +// GetSCRsByTxHash - +func (f *FacadeStub) GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) { + if f.GetSCRsByTxHashCalled != nil { + return f.GetSCRsByTxHashCalled(txHash, scrHash) + } + + return nil, nil } // GetTokenSupply - diff --git a/api/shared/interface.go b/api/shared/interface.go index 4b775ebdd39..206cea6ee30 100644 --- a/api/shared/interface.go +++ b/api/shared/interface.go @@ -135,6 +135,7 @@ type FacadeHandler interface { GetEligibleManagedKeys() ([]string, error) GetWaitingManagedKeys() ([]string, error) GetWaitingEpochsLeftForPublicKey(publicKey string) (uint32, error) + GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) P2PPrometheusMetricsEnabled() bool IsInterfaceNil() bool } diff --git a/cmd/node/config/api.toml b/cmd/node/config/api.toml index a10ec049554..fcf9cf7fc0b 100644 --- a/cmd/node/config/api.toml +++ b/cmd/node/config/api.toml @@ -221,6 +221,9 @@ # /transaction/:txhash will return the transaction in JSON format based on its hash { Name = "/:txhash", Open = true }, + + # /transaction/scrs-by-tx-hash/:txhash will return the smart contract results generated by the provided transaction hash + { Name = "/scrs-by-tx-hash/:txhash", Open = true }, ] [APIPackages.block] diff --git a/cmd/node/config/config.toml b/cmd/node/config/config.toml index 16277a259f1..2f884b596e0 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -42,7 +42,8 @@ # ChainParametersByEpoch defines chain operation configurable values that can be modified based on epochs ChainParametersByEpoch = [ - { EnableEpoch = 0, RoundDuration = 6000, ShardConsensusGroupSize = 7, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false } + { EnableEpoch = 0, RoundDuration = 6000, ShardConsensusGroupSize = 7, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false }, + { EnableEpoch = 8, RoundDuration = 6000, ShardConsensusGroupSize = 10, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false } ] [HardwareRequirements] @@ -394,9 +395,9 @@ [TxDataPool] Name = "TxDataPool" Capacity = 600000 - SizePerSender = 20000 + SizePerSender = 5001 SizeInBytes = 419430400 #400MB - SizeInBytesPerSender = 12288000 + SizeInBytesPerSender = 12288000 #12MB Type = "TxCache" Shards = 16 diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 4307c976acc..b2fc119d6a4 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -309,14 +309,32 @@ # AlwaysMergeContextsInEEIEnableEpoch represents the epoch in which the EEI will always merge the contexts AlwaysMergeContextsInEEIEnableEpoch = 1 + # UseGasBoundedShouldFailExecutionEnableEpoch represents the epoch when use bounded gas function should fail execution in case of error + UseGasBoundedShouldFailExecutionEnableEpoch = 1 + # DynamicESDTEnableEpoch represents the epoch when dynamic NFT feature is enabled - DynamicESDTEnableEpoch = 4 + DynamicESDTEnableEpoch = 1 # EGLDInMultiTransferEnableEpoch represents the epoch when EGLD in multitransfer is enabled - EGLDInMultiTransferEnableEpoch = 4 + EGLDInMultiTransferEnableEpoch = 1 # CryptoOpcodesV2EnableEpoch represents the epoch when BLSMultiSig, Secp256r1 and other opcodes are enabled - CryptoOpcodesV2EnableEpoch = 4 + CryptoOpcodesV2EnableEpoch = 1 + + # UnjailCleanupEnableEpoch represents the epoch when the cleanup of the unjailed nodes is enabled + UnJailCleanupEnableEpoch = 1 + + # FixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost will be enabled + FixRelayedBaseCostEnableEpoch = 1 + + # MultiESDTNFTTransferAndExecuteByUserEnableEpoch represents the epoch when enshrined sovereign cross chain opcodes are enabled + MultiESDTNFTTransferAndExecuteByUserEnableEpoch = 9999999 + + # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled + FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 1 + + # RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3EnableEpoch = 1 # EquivalentMessagesEnableEpoch represents the epoch when the equivalent messages are enabled EquivalentMessagesEnableEpoch = 8 # the chain simulator tests for staking v4 fail if this is set earlier, as they test the transition in epochs 4-7 diff --git a/cmd/node/config/external.toml b/cmd/node/config/external.toml index 1058e0c3fb7..6fbbbb195c6 100644 --- a/cmd/node/config/external.toml +++ b/cmd/node/config/external.toml @@ -51,7 +51,7 @@ # URL for the WebSocket client/server connection # This value represents the IP address and port number that the WebSocket client or server will use to establish a connection. - URL = "127.0.0.1:22111" + URL = "ws://127.0.0.1:22111" # After a message will be sent it will wait for an ack message if this flag is enabled WithAcknowledge = true diff --git a/cmd/node/config/gasSchedules/gasScheduleV1.toml b/cmd/node/config/gasSchedules/gasScheduleV1.toml index 5e715a2d466..7fca1d6a7d2 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV1.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV1.toml @@ -112,6 +112,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV2.toml b/cmd/node/config/gasSchedules/gasScheduleV2.toml index e0d1c4e366e..bfc53d1b91d 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV2.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV2.toml @@ -112,6 +112,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV3.toml b/cmd/node/config/gasSchedules/gasScheduleV3.toml index 8c3a763363e..eb88204bf5e 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV3.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV3.toml @@ -112,6 +112,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV4.toml b/cmd/node/config/gasSchedules/gasScheduleV4.toml index 4d178ff0fd5..f41a7a8d940 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV4.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV4.toml @@ -112,6 +112,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV5.toml b/cmd/node/config/gasSchedules/gasScheduleV5.toml index e5f5035bb17..34b4336b32c 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV5.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV5.toml @@ -112,6 +112,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV6.toml b/cmd/node/config/gasSchedules/gasScheduleV6.toml index f41c5002b85..99ff15c8482 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV6.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV6.toml @@ -112,6 +112,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV7.toml b/cmd/node/config/gasSchedules/gasScheduleV7.toml index 6b580c893cc..250d89117cf 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV7.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV7.toml @@ -113,6 +113,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/config/gasSchedules/gasScheduleV8.toml b/cmd/node/config/gasSchedules/gasScheduleV8.toml index 424c07e79f2..7a0c11de4e9 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV8.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV8.toml @@ -113,6 +113,7 @@ GetCallbackClosure = 10000 GetCodeMetadata = 10000 IsBuiltinFunction = 10000 + IsReservedFunctionName = 10000 [EthAPICost] UseGas = 100 diff --git a/cmd/node/main.go b/cmd/node/main.go index 2ce7fb6c626..c75dd40a393 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -299,6 +299,7 @@ func attachFileLogger(log logger.Logger, flagsConfig *config.ContextFlagsConfig) logger.ToggleCorrelation(flagsConfig.EnableLogCorrelation) logger.ToggleLoggerName(flagsConfig.EnableLogName) logLevelFlagValue := flagsConfig.LogLevel + err = logger.SetLogLevel(logLevelFlagValue) if err != nil { return nil, err diff --git a/common/common.go b/common/common.go index 08bedb8b87f..97132f1e298 100644 --- a/common/common.go +++ b/common/common.go @@ -6,10 +6,31 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" - "github.com/multiversx/mx-chain-go/storage" - "github.com/multiversx/mx-chain-vm-v1_2-go/ipc/marshaling" ) +// IsValidRelayedTxV3 returns true if the provided transaction is a valid transaction of type relayed v3 +func IsValidRelayedTxV3(tx data.TransactionHandler) bool { + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if !isRelayedV3 { + return false + } + hasValidRelayer := len(relayedTx.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTx.GetRelayerAddr()) > 0 + hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 + return hasValidRelayer && hasValidRelayerSignature +} + +// IsRelayedTxV3 returns true if the provided transaction is a transaction of type relayed v3, without any further checks +func IsRelayedTxV3(tx data.TransactionHandler) bool { + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if !isRelayedV3 { + return false + } + + hasRelayer := len(relayedTx.GetRelayerAddr()) > 0 + hasRelayerSignature := len(relayedTx.GetRelayerSignature()) > 0 + return hasRelayer || hasRelayerSignature +} + // IsEpochChangeBlockForFlagActivation returns true if the provided header is the first one after the specified flag's activation func IsEpochChangeBlockForFlagActivation(header data.HeaderHandler, enableEpochsHandler EnableEpochsHandler, flag core.EnableEpochFlag) bool { isStartOfEpochBlock := header.IsStartOfEpochBlock() @@ -54,28 +75,3 @@ func VerifyProofAgainstHeader(proof data.HeaderProofHandler, header data.HeaderH return nil } - -// GetHeader tries to get the header from pool first and if not found, searches for it through storer -func GetHeader( - headerHash []byte, - headersPool HeadersPool, - headersStorer storage.Storer, - marshaller marshaling.Marshalizer, -) (data.HeaderHandler, error) { - header, err := headersPool.GetHeaderByHash(headerHash) - if err == nil { - return header, nil - } - - headerBytes, err := headersStorer.SearchFirst(headerHash) - if err != nil { - return nil, err - } - - err = marshaller.Unmarshal(header, headerBytes) - if err != nil { - return nil, err - } - - return header, nil -} diff --git a/common/common_test.go b/common/common_test.go new file mode 100644 index 00000000000..5a0ec53a21f --- /dev/null +++ b/common/common_test.go @@ -0,0 +1,70 @@ +package common + +import ( + "math/big" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/smartContractResult" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/stretchr/testify/require" +) + +func TestIsValidRelayedTxV3(t *testing.T) { + t.Parallel() + + scr := &smartContractResult.SmartContractResult{} + require.False(t, IsValidRelayedTxV3(scr)) + require.False(t, IsRelayedTxV3(scr)) + + notRelayedTxV3 := &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender0"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + } + require.False(t, IsValidRelayedTxV3(notRelayedTxV3)) + require.False(t, IsRelayedTxV3(notRelayedTxV3)) + + invalidRelayedTxV3 := &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender0"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + RelayerAddr: []byte("relayer"), + } + require.False(t, IsValidRelayedTxV3(invalidRelayedTxV3)) + require.True(t, IsRelayedTxV3(invalidRelayedTxV3)) + + invalidRelayedTxV3 = &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender0"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + } + require.False(t, IsValidRelayedTxV3(invalidRelayedTxV3)) + require.True(t, IsRelayedTxV3(invalidRelayedTxV3)) + + relayedTxV3 := &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender1"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + RelayerAddr: []byte("relayer"), + RelayerSignature: []byte("signature"), + } + require.True(t, IsValidRelayedTxV3(relayedTxV3)) + require.True(t, IsRelayedTxV3(relayedTxV3)) +} diff --git a/common/constants.go b/common/constants.go index 4f9ac681316..755a07ecab8 100644 --- a/common/constants.go +++ b/common/constants.go @@ -343,6 +343,9 @@ const MetricMinGasLimit = "erd_min_gas_limit" // MetricExtraGasLimitGuardedTx specifies the extra gas limit required for guarded transactions const MetricExtraGasLimitGuardedTx = "erd_extra_gas_limit_guarded_tx" +// MetricExtraGasLimitRelayedTx specifies the extra gas limit required for relayed v3 transactions +const MetricExtraGasLimitRelayedTx = "erd_extra_gas_limit_relayed_tx" + // MetricRewardsTopUpGradientPoint is the metric that specifies the rewards top up gradient point const MetricRewardsTopUpGradientPoint = "erd_rewards_top_up_gradient_point" @@ -498,6 +501,9 @@ const ( // MetricRelayedTransactionsV2EnableEpoch represents the epoch when the relayed transactions v2 is enabled MetricRelayedTransactionsV2EnableEpoch = "erd_relayed_transactions_v2_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" @@ -731,6 +737,15 @@ const ( // MetricCryptoOpcodesV2EnableEpoch represents the epoch when crypto opcodes v2 feature is enabled MetricCryptoOpcodesV2EnableEpoch = "erd_crypto_opcodes_v2_enable_epoch" + // MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch represents the epoch when enshrined sovereign opcodes are enabled + MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch = "erd_multi_esdt_transfer_execute_by_user_enable_epoch" + + // MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non-payable sc is enabled + MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch = "erd_fix_relayed_move_balance_to_non_payable_sc_enable_epoch" + + // MetricRelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 are enabled + MetricRelayedTransactionsV3EnableEpoch = "erd_relayed_transactions_v3_enable_epoch" + // MetricMaxNodesChangeEnableEpoch holds configuration for changing the maximum number of nodes and the enabling epoch MetricMaxNodesChangeEnableEpoch = "erd_max_nodes_change_enable_epoch" @@ -1224,9 +1239,15 @@ const ( CleanupAuctionOnLowWaitingListFlag core.EnableEpochFlag = "CleanupAuctionOnLowWaitingListFlag" StakingV4StartedFlag core.EnableEpochFlag = "StakingV4StartedFlag" AlwaysMergeContextsInEEIFlag core.EnableEpochFlag = "AlwaysMergeContextsInEEIFlag" + UseGasBoundedShouldFailExecutionFlag core.EnableEpochFlag = "UseGasBoundedShouldFailExecutionFlag" DynamicESDTFlag core.EnableEpochFlag = "DynamicEsdtFlag" EGLDInESDTMultiTransferFlag core.EnableEpochFlag = "EGLDInESDTMultiTransferFlag" CryptoOpcodesV2Flag core.EnableEpochFlag = "CryptoOpcodesV2Flag" + UnJailCleanupFlag core.EnableEpochFlag = "UnJailCleanupFlag" + FixRelayedBaseCostFlag core.EnableEpochFlag = "FixRelayedBaseCostFlag" + MultiESDTNFTTransferAndExecuteByUserFlag core.EnableEpochFlag = "MultiESDTNFTTransferAndExecuteByUserFlag" + FixRelayedMoveBalanceToNonPayableSCFlag core.EnableEpochFlag = "FixRelayedMoveBalanceToNonPayableSCFlag" + RelayedTransactionsV3Flag core.EnableEpochFlag = "RelayedTransactionsV3Flag" EquivalentMessagesFlag core.EnableEpochFlag = "EquivalentMessagesFlag" FixedOrderInConsensusFlag core.EnableEpochFlag = "FixedOrderInConsensusFlag" // all new flags must be added to createAllFlagsMap method, as part of enableEpochsHandler allFlagsDefined diff --git a/common/converters.go b/common/converters.go index 036cce7d070..83ccbc084e1 100644 --- a/common/converters.go +++ b/common/converters.go @@ -23,7 +23,7 @@ func ProcessDestinationShardAsObserver(destinationShardIdAsObserver string) (uin val, err := strconv.ParseUint(destShard, 10, 32) if err != nil { - return 0, fmt.Errorf("error parsing DestinationShardAsObserver option: " + err.Error()) + return 0, fmt.Errorf("error parsing DestinationShardAsObserver option: %s", err.Error()) } return uint32(val), err diff --git a/common/enablers/enableEpochsHandler.go b/common/enablers/enableEpochsHandler.go index dac7e1aba6b..c7826d86c5a 100644 --- a/common/enablers/enableEpochsHandler.go +++ b/common/enablers/enableEpochsHandler.go @@ -732,6 +732,12 @@ func (handler *enableEpochsHandler) createAllFlagsMap() { }, activationEpoch: handler.enableEpochsConfig.AlwaysMergeContextsInEEIEnableEpoch, }, + common.UseGasBoundedShouldFailExecutionFlag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.UseGasBoundedShouldFailExecutionEnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.UseGasBoundedShouldFailExecutionEnableEpoch, + }, common.DynamicESDTFlag: { isActiveInEpoch: func(epoch uint32) bool { return epoch >= handler.enableEpochsConfig.DynamicESDTEnableEpoch @@ -750,6 +756,36 @@ func (handler *enableEpochsHandler) createAllFlagsMap() { }, activationEpoch: handler.enableEpochsConfig.CryptoOpcodesV2EnableEpoch, }, + common.UnJailCleanupFlag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.UnJailCleanupEnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.UnJailCleanupEnableEpoch, + }, + common.FixRelayedBaseCostFlag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.FixRelayedBaseCostEnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.FixRelayedBaseCostEnableEpoch, + }, + common.MultiESDTNFTTransferAndExecuteByUserFlag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.MultiESDTNFTTransferAndExecuteByUserEnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.MultiESDTNFTTransferAndExecuteByUserEnableEpoch, + }, + common.FixRelayedMoveBalanceToNonPayableSCFlag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.FixRelayedMoveBalanceToNonPayableSCEnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.FixRelayedMoveBalanceToNonPayableSCEnableEpoch, + }, + common.RelayedTransactionsV3Flag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.RelayedTransactionsV3EnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.RelayedTransactionsV3EnableEpoch, + }, common.EquivalentMessagesFlag: { isActiveInEpoch: func(epoch uint32) bool { return epoch >= handler.enableEpochsConfig.EquivalentMessagesEnableEpoch diff --git a/common/enablers/enableEpochsHandler_test.go b/common/enablers/enableEpochsHandler_test.go index a1b47200647..7bd3673b786 100644 --- a/common/enablers/enableEpochsHandler_test.go +++ b/common/enablers/enableEpochsHandler_test.go @@ -119,8 +119,13 @@ func createEnableEpochsConfig() config.EnableEpochs { DynamicESDTEnableEpoch: 102, EGLDInMultiTransferEnableEpoch: 103, CryptoOpcodesV2EnableEpoch: 104, - EquivalentMessagesEnableEpoch: 105, - FixedOrderInConsensusEnableEpoch: 106, + FixRelayedBaseCostEnableEpoch: 105, + MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 106, + FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 107, + UseGasBoundedShouldFailExecutionEnableEpoch: 108, + RelayedTransactionsV3EnableEpoch: 109, + EquivalentMessagesEnableEpoch: 110, + FixedOrderInConsensusEnableEpoch: 111, } } @@ -321,6 +326,8 @@ func TestEnableEpochsHandler_IsFlagEnabled(t *testing.T) { require.True(t, handler.IsFlagEnabled(common.StakingV4StartedFlag)) require.True(t, handler.IsFlagEnabled(common.AlwaysMergeContextsInEEIFlag)) require.True(t, handler.IsFlagEnabled(common.DynamicESDTFlag)) + require.True(t, handler.IsFlagEnabled(common.FixRelayedBaseCostFlag)) + require.True(t, handler.IsFlagEnabled(common.FixRelayedMoveBalanceToNonPayableSCFlag)) require.True(t, handler.IsFlagEnabled(common.EquivalentMessagesFlag)) require.True(t, handler.IsFlagEnabled(common.FixedOrderInConsensusFlag)) } @@ -439,9 +446,14 @@ func TestEnableEpochsHandler_GetActivationEpoch(t *testing.T) { require.Equal(t, cfg.CleanupAuctionOnLowWaitingListEnableEpoch, handler.GetActivationEpoch(common.CleanupAuctionOnLowWaitingListFlag)) require.Equal(t, cfg.StakingV4Step1EnableEpoch, handler.GetActivationEpoch(common.StakingV4StartedFlag)) require.Equal(t, cfg.AlwaysMergeContextsInEEIEnableEpoch, handler.GetActivationEpoch(common.AlwaysMergeContextsInEEIFlag)) + require.Equal(t, cfg.UseGasBoundedShouldFailExecutionEnableEpoch, handler.GetActivationEpoch(common.UseGasBoundedShouldFailExecutionFlag)) require.Equal(t, cfg.DynamicESDTEnableEpoch, handler.GetActivationEpoch(common.DynamicESDTFlag)) require.Equal(t, cfg.EGLDInMultiTransferEnableEpoch, handler.GetActivationEpoch(common.EGLDInESDTMultiTransferFlag)) require.Equal(t, cfg.CryptoOpcodesV2EnableEpoch, handler.GetActivationEpoch(common.CryptoOpcodesV2Flag)) + require.Equal(t, cfg.FixRelayedBaseCostEnableEpoch, handler.GetActivationEpoch(common.FixRelayedBaseCostFlag)) + require.Equal(t, cfg.MultiESDTNFTTransferAndExecuteByUserEnableEpoch, handler.GetActivationEpoch(common.MultiESDTNFTTransferAndExecuteByUserFlag)) + require.Equal(t, cfg.FixRelayedMoveBalanceToNonPayableSCEnableEpoch, handler.GetActivationEpoch(common.FixRelayedMoveBalanceToNonPayableSCFlag)) + require.Equal(t, cfg.RelayedTransactionsV3EnableEpoch, handler.GetActivationEpoch(common.RelayedTransactionsV3Flag)) require.Equal(t, cfg.EquivalentMessagesEnableEpoch, handler.GetActivationEpoch(common.EquivalentMessagesFlag)) require.Equal(t, cfg.FixedOrderInConsensusEnableEpoch, handler.GetActivationEpoch(common.FixedOrderInConsensusFlag)) } diff --git a/common/reflectcommon/structFieldsUpdate.go b/common/reflectcommon/structFieldsUpdate.go index 94ad6002c07..7f8940b0400 100644 --- a/common/reflectcommon/structFieldsUpdate.go +++ b/common/reflectcommon/structFieldsUpdate.go @@ -123,6 +123,10 @@ func trySetTheNewValue(value *reflect.Value, newValue interface{}) error { structVal := reflect.ValueOf(newValue) return trySetStructValue(value, structVal) + case reflect.Map: + mapValue := reflect.ValueOf(newValue) + + return trySetMapValue(value, mapValue) default: return fmt.Errorf("unsupported type <%s> when trying to set the value '%v' of type <%s>", valueKind, newValue, reflect.TypeOf(newValue)) } @@ -137,7 +141,7 @@ func trySetSliceValue(value *reflect.Value, newValue interface{}) error { item := sliceVal.Index(i) newItem := reflect.New(value.Type().Elem()).Elem() - err := trySetStructValue(&newItem, item) + err := trySetTheNewValue(&newItem, item.Interface()) if err != nil { return err } @@ -163,6 +167,31 @@ func trySetStructValue(value *reflect.Value, newValue reflect.Value) error { } } +func trySetMapValue(value *reflect.Value, newValue reflect.Value) error { + if value.IsNil() { + value.Set(reflect.MakeMap(value.Type())) + } + + switch newValue.Kind() { + case reflect.Map: + for _, key := range newValue.MapKeys() { + item := newValue.MapIndex(key) + newItem := reflect.New(value.Type().Elem()).Elem() + + err := trySetTheNewValue(&newItem, item.Interface()) + if err != nil { + return err + } + + value.SetMapIndex(key, newItem) + } + default: + return fmt.Errorf("unsupported type <%s> when trying to add value in type <%s>", newValue.Kind(), value.Kind()) + } + + return nil +} + func updateStructFromMap(value *reflect.Value, newValue reflect.Value) error { for _, key := range newValue.MapKeys() { fieldName := key.String() diff --git a/common/reflectcommon/structFieldsUpdate_test.go b/common/reflectcommon/structFieldsUpdate_test.go index d2145ca8fa0..27a30ea9d00 100644 --- a/common/reflectcommon/structFieldsUpdate_test.go +++ b/common/reflectcommon/structFieldsUpdate_test.go @@ -5,9 +5,10 @@ import ( "reflect" "testing" - "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/testscommon/toml" + + "github.com/multiversx/mx-chain-core-go/core" "github.com/stretchr/testify/require" ) @@ -447,10 +448,10 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { expectedNewValue["first"] = 1 expectedNewValue["second"] = 2 - path := "TestMap.Value" + path := "TestInterface.Value" err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) - require.Equal(t, "unsupported type when trying to set the value 'map[first:1 second:2]' of type ", err.Error()) + require.Equal(t, "unsupported type when trying to set the value 'map[first:1 second:2]' of type ", err.Error()) }) t.Run("should error fit signed for target type not int", func(t *testing.T) { @@ -517,11 +518,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI8.Int8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[0].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[0].Path, overrideConfig.OverridableConfigTomlValues[0].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[0].Value, int64(testConfig.Int8.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[0].Value, int64(testConfig.Int8.Number)) }) t.Run("should error int8 value", func(t *testing.T) { @@ -533,9 +532,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI8.Int8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[1].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[1].Path, overrideConfig.OverridableConfigTomlValues[1].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '128' of type to type ", err.Error()) }) @@ -549,11 +546,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI8.Int8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[2].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[2].Path, overrideConfig.OverridableConfigTomlValues[2].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[2].Value, int64(testConfig.Int8.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[2].Value, int64(testConfig.Int8.Number)) }) t.Run("should error int8 negative value", func(t *testing.T) { @@ -565,9 +560,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI8.Int8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[3].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[3].Path, overrideConfig.OverridableConfigTomlValues[3].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-129' of type to type ", err.Error()) }) @@ -581,11 +574,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI16.Int16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[4].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[4].Path, overrideConfig.OverridableConfigTomlValues[4].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[4].Value, int64(testConfig.Int16.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[4].Value, int64(testConfig.Int16.Number)) }) t.Run("should error int16 value", func(t *testing.T) { @@ -597,9 +588,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI16.Int16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[5].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[5].Path, overrideConfig.OverridableConfigTomlValues[5].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '32768' of type to type ", err.Error()) }) @@ -613,11 +602,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI16.Int16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[6].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[6].Path, overrideConfig.OverridableConfigTomlValues[6].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[6].Value, int64(testConfig.Int16.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[6].Value, int64(testConfig.Int16.Number)) }) t.Run("should error int16 negative value", func(t *testing.T) { @@ -629,9 +616,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI16.Int16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[7].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[7].Path, overrideConfig.OverridableConfigTomlValues[7].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-32769' of type to type ", err.Error()) }) @@ -645,11 +630,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI32.Int32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[17].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[8].Path, overrideConfig.OverridableConfigTomlValues[8].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[17].Value, int64(testConfig.Int32.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[8].Value, int64(testConfig.Int32.Number)) }) t.Run("should work and override int32 value with uint16", func(t *testing.T) { @@ -660,11 +643,11 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { expectedNewValue := uint16(10) - path := "TestConfigI32.Int32.Value" + path := "TestConfigI32.Int32.Number" err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) require.NoError(t, err) - require.Equal(t, int32(expectedNewValue), testConfig.Int32.Value) + require.Equal(t, int32(expectedNewValue), testConfig.Int32.Number) }) t.Run("should error int32 value", func(t *testing.T) { @@ -676,9 +659,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI32.Int32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[9].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[9].Path, overrideConfig.OverridableConfigTomlValues[9].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '2147483648' of type to type ", err.Error()) }) @@ -692,11 +673,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI32.Int32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[10].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[10].Path, overrideConfig.OverridableConfigTomlValues[10].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[10].Value, int64(testConfig.Int32.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[10].Value, int64(testConfig.Int32.Number)) }) t.Run("should error int32 negative value", func(t *testing.T) { @@ -708,9 +687,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI32.Int32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[11].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[11].Path, overrideConfig.OverridableConfigTomlValues[11].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-2147483649' of type to type ", err.Error()) }) @@ -724,11 +701,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI64.Int64.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[12].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[12].Path, overrideConfig.OverridableConfigTomlValues[12].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[12].Value, int64(testConfig.Int64.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[12].Value, int64(testConfig.Int64.Number)) }) t.Run("should work and override int64 negative value", func(t *testing.T) { @@ -740,11 +715,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigI64.Int64.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[13].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[13].Path, overrideConfig.OverridableConfigTomlValues[13].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[13].Value, int64(testConfig.Int64.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[13].Value, int64(testConfig.Int64.Number)) }) t.Run("should work and override uint8 value", func(t *testing.T) { @@ -756,11 +729,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU8.Uint8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[14].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[14].Path, overrideConfig.OverridableConfigTomlValues[14].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[14].Value, int64(testConfig.Uint8.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[14].Value, int64(testConfig.Uint8.Number)) }) t.Run("should error uint8 value", func(t *testing.T) { @@ -772,9 +743,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU8.Uint8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[15].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[15].Path, overrideConfig.OverridableConfigTomlValues[15].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '256' of type to type ", err.Error()) }) @@ -788,9 +757,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU8.Uint8.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[16].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[16].Path, overrideConfig.OverridableConfigTomlValues[16].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-256' of type to type ", err.Error()) }) @@ -804,11 +771,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU16.Uint16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[17].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[17].Path, overrideConfig.OverridableConfigTomlValues[17].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[17].Value, int64(testConfig.Uint16.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[17].Value, int64(testConfig.Uint16.Number)) }) t.Run("should error uint16 value", func(t *testing.T) { @@ -820,9 +785,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU16.Uint16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[18].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[18].Path, overrideConfig.OverridableConfigTomlValues[18].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '65536' of type to type ", err.Error()) }) @@ -836,9 +799,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU16.Uint16.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[19].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[19].Path, overrideConfig.OverridableConfigTomlValues[19].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-65536' of type to type ", err.Error()) }) @@ -852,11 +813,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU32.Uint32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[20].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[20].Path, overrideConfig.OverridableConfigTomlValues[20].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[20].Value, int64(testConfig.Uint32.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[20].Value, int64(testConfig.Uint32.Number)) }) t.Run("should error uint32 value", func(t *testing.T) { @@ -868,9 +827,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU32.Uint32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[21].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[21].Path, overrideConfig.OverridableConfigTomlValues[21].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '4294967296' of type to type ", err.Error()) }) @@ -884,9 +841,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU32.Uint32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[22].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[22].Path, overrideConfig.OverridableConfigTomlValues[22].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-4294967296' of type to type ", err.Error()) }) @@ -900,11 +855,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU64.Uint64.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[23].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[23].Path, overrideConfig.OverridableConfigTomlValues[23].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[23].Value, int64(testConfig.Uint64.Value)) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[23].Value, int64(testConfig.Uint64.Number)) }) t.Run("should error uint64 negative value", func(t *testing.T) { @@ -916,9 +869,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigU64.Uint64.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[24].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[24].Path, overrideConfig.OverridableConfigTomlValues[24].Value) require.Equal(t, "unable to cast value '-9223372036854775808' of type to type ", err.Error()) }) @@ -931,11 +882,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigF32.Float32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[25].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[25].Path, overrideConfig.OverridableConfigTomlValues[25].Value) require.NoError(t, err) - require.Equal(t, float32(3.4), testConfig.Float32.Value) + require.Equal(t, float32(3.4), testConfig.Float32.Number) }) t.Run("should error float32 value", func(t *testing.T) { @@ -947,9 +896,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigF32.Float32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[26].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[26].Path, overrideConfig.OverridableConfigTomlValues[26].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '3.4e+39' of type to type ", err.Error()) }) @@ -963,11 +910,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigF32.Float32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[27].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[27].Path, overrideConfig.OverridableConfigTomlValues[27].Value) require.NoError(t, err) - require.Equal(t, float32(-3.4), testConfig.Float32.Value) + require.Equal(t, float32(-3.4), testConfig.Float32.Number) }) t.Run("should error float32 negative value", func(t *testing.T) { @@ -979,9 +924,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigF32.Float32.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[28].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[28].Path, overrideConfig.OverridableConfigTomlValues[28].Value) require.NotNil(t, err) require.Equal(t, "unable to cast value '-3.4e+40' of type to type ", err.Error()) }) @@ -995,11 +938,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigF64.Float64.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[29].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[29].Path, overrideConfig.OverridableConfigTomlValues[29].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[29].Value, testConfig.Float64.Value) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[29].Value, testConfig.Float64.Number) }) t.Run("should work and override float64 negative value", func(t *testing.T) { @@ -1011,11 +952,9 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigF64.Float64.Value" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[30].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[30].Path, overrideConfig.OverridableConfigTomlValues[30].Value) require.NoError(t, err) - require.Equal(t, overrideConfig.OverridableConfigTomlValues[30].Value, testConfig.Float64.Value) + require.Equal(t, overrideConfig.OverridableConfigTomlValues[30].Value, testConfig.Float64.Number) }) t.Run("should work and override struct", func(t *testing.T) { @@ -1027,13 +966,11 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigStruct.ConfigStruct.Description" - expectedNewValue := toml.Description{ Number: 11, } - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[31].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[31].Path, overrideConfig.OverridableConfigTomlValues[31].Value) require.NoError(t, err) require.Equal(t, expectedNewValue, testConfig.TestConfigStruct.ConfigStruct.Description) }) @@ -1047,9 +984,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigStruct.ConfigStruct.Description" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[32].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[32].Path, overrideConfig.OverridableConfigTomlValues[32].Value) require.Equal(t, "field not found or cannot be set", err.Error()) }) @@ -1062,9 +997,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigStruct.ConfigStruct.Description" - - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[33].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[33].Path, overrideConfig.OverridableConfigTomlValues[33].Value) require.Equal(t, "unable to cast value '11' of type to type ", err.Error()) }) @@ -1077,8 +1010,6 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigNestedStruct.ConfigNestedStruct" - expectedNewValue := toml.ConfigNestedStruct{ Text: "Overwritten text", Message: toml.Message{ @@ -1089,7 +1020,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { }, } - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[34].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[34].Path, overrideConfig.OverridableConfigTomlValues[34].Value) require.NoError(t, err) require.Equal(t, expectedNewValue, testConfig.TestConfigNestedStruct.ConfigNestedStruct) }) @@ -1103,14 +1034,12 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") require.NoError(t, err) - path := "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription" - expectedNewValue := []toml.MessageDescription{ {Text: "Overwritten Text1"}, {Text: "Overwritten Text2"}, } - err = AdaptStructureValueBasedOnPath(testConfig, path, overrideConfig.OverridableConfigTomlValues[35].Value) + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[35].Path, overrideConfig.OverridableConfigTomlValues[35].Value) require.NoError(t, err) require.Equal(t, expectedNewValue, testConfig.TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription) }) @@ -1193,6 +1122,161 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, expectedNewValue, testConfig.TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription) }) + t.Run("should work on map, override and insert from config", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[36].Path, overrideConfig.OverridableConfigTomlValues[36].Value) + require.NoError(t, err) + require.Equal(t, 2, len(testConfig.TestMap.Map)) + require.Equal(t, 10, testConfig.TestMap.Map["Key1"].Number) + require.Equal(t, 11, testConfig.TestMap.Map["Key2"].Number) + }) + + t.Run("should work on map and insert from config", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[37].Path, overrideConfig.OverridableConfigTomlValues[37].Value) + require.NoError(t, err) + require.Equal(t, 3, len(testConfig.TestMap.Map)) + require.Equal(t, 999, testConfig.TestMap.Map["Key1"].Number) + require.Equal(t, 2, testConfig.TestMap.Map["Key2"].Number) + require.Equal(t, 3, testConfig.TestMap.Map["Key3"].Number) + }) + + t.Run("should work on map, override and insert values in map", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedNewValue := make(map[string]toml.MapValues) + expectedNewValue["Key1"] = toml.MapValues{Number: 100} + expectedNewValue["Key2"] = toml.MapValues{Number: 200} + + path := "TestMap.Map" + + err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) + require.NoError(t, err) + require.Equal(t, 2, len(testConfig.TestMap.Map)) + require.Equal(t, 100, testConfig.TestMap.Map["Key1"].Number) + require.Equal(t, 200, testConfig.TestMap.Map["Key2"].Number) + }) + + t.Run("should error on map when override different map", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedNewValue := make(map[string]toml.MessageDescription) + expectedNewValue["Key1"] = toml.MessageDescription{Text: "A"} + expectedNewValue["Key2"] = toml.MessageDescription{Text: "B"} + + path := "TestMap.Map" + + err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) + require.Equal(t, "field not found or cannot be set", err.Error()) + }) + + t.Run("should error on map when override anything else other than map", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedNewValue := 1 + + path := "TestMap.Map" + + err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) + require.Equal(t, "unsupported type when trying to add value in type ", err.Error()) + }) + + t.Run("should work and override string array", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedArray := []string{"x", "y", "z"} + + err = AdaptStructureValueBasedOnPath(testConfig, "TestArray.Strings", expectedArray) + require.NoError(t, err) + require.Equal(t, expectedArray, testConfig.TestArray.Strings) + }) + + t.Run("should work and override int array", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedArray := []int{10, 20, 30} + + err = AdaptStructureValueBasedOnPath(testConfig, "TestArray.Ints", expectedArray) + require.NoError(t, err) + require.Equal(t, expectedArray, testConfig.TestArray.Ints) + }) + + t.Run("should work and override string array from toml", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[38].Path, overrideConfig.OverridableConfigTomlValues[38].Value) + require.NoError(t, err) + expectedArray := []string{"x", "y", "z"} + require.Equal(t, expectedArray, testConfig.TestArray.Strings) + }) + + t.Run("should work and override int array from toml", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[39].Path, overrideConfig.OverridableConfigTomlValues[39].Value) + require.NoError(t, err) + expectedArray := []int{10, 20, 30} + require.Equal(t, expectedArray, testConfig.TestArray.Ints) + }) + + t.Run("should work and override struct of arrays from toml", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + expectedStringsArray := []string{"x", "y", "z"} + expectedIntsArray := []int{10, 20, 30} + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[40].Path, overrideConfig.OverridableConfigTomlValues[40].Value) + require.NoError(t, err) + require.Equal(t, expectedStringsArray, testConfig.TestArray.Strings) + require.Equal(t, expectedIntsArray, testConfig.TestArray.Ints) + }) + } func loadTestConfig(filepath string) (*toml.Config, error) { diff --git a/config/epochConfig.go b/config/epochConfig.go index c197eb2e614..9ff32a12b72 100644 --- a/config/epochConfig.go +++ b/config/epochConfig.go @@ -114,9 +114,15 @@ type EnableEpochs struct { StakingV4Step3EnableEpoch uint32 CleanupAuctionOnLowWaitingListEnableEpoch uint32 AlwaysMergeContextsInEEIEnableEpoch uint32 + UseGasBoundedShouldFailExecutionEnableEpoch uint32 DynamicESDTEnableEpoch uint32 EGLDInMultiTransferEnableEpoch uint32 CryptoOpcodesV2EnableEpoch uint32 + UnJailCleanupEnableEpoch uint32 + FixRelayedBaseCostEnableEpoch uint32 + MultiESDTNFTTransferAndExecuteByUserEnableEpoch uint32 + FixRelayedMoveBalanceToNonPayableSCEnableEpoch uint32 + RelayedTransactionsV3EnableEpoch uint32 EquivalentMessagesEnableEpoch uint32 FixedOrderInConsensusEnableEpoch uint32 BLSMultiSignerEnableEpoch []MultiSignerConfig diff --git a/config/overridableConfig/configOverriding_test.go b/config/overridableConfig/configOverriding_test.go index 5e23a2bacda..9f187a8a501 100644 --- a/config/overridableConfig/configOverriding_test.go +++ b/config/overridableConfig/configOverriding_test.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-chain-go/config" p2pConfig "github.com/multiversx/mx-chain-go/p2p/config" + "github.com/stretchr/testify/require" ) @@ -104,16 +105,15 @@ func TestOverrideConfigValues(t *testing.T) { }) t.Run("should work for enableRounds.toml", func(t *testing.T) { - // TODO: fix this test - t.Skip("skipped, as this test requires the fix from this PR: https://github.com/multiversx/mx-chain-go/pull/5851") - t.Parallel() configs := &config.Configs{RoundConfig: &config.RoundConfig{}} + value := make(map[string]config.ActivationRoundByName) + value["DisableAsyncCallV1"] = config.ActivationRoundByName{Round: "37"} - err := OverrideConfigValues([]config.OverridableConfig{{Path: "RoundActivations.DisableAsyncCallV1.Round", Value: "37", File: "enableRounds.toml"}}, configs) + err := OverrideConfigValues([]config.OverridableConfig{{Path: "RoundActivations", Value: value, File: "enableRounds.toml"}}, configs) require.NoError(t, err) - require.Equal(t, uint32(37), configs.RoundConfig.RoundActivations["DisableAsyncCallV1"]) + require.Equal(t, "37", configs.RoundConfig.RoundActivations["DisableAsyncCallV1"].Round) }) t.Run("should work for ratings.toml", func(t *testing.T) { diff --git a/config/tomlConfig_test.go b/config/tomlConfig_test.go index 89f6fedbb8d..11ae291511c 100644 --- a/config/tomlConfig_test.go +++ b/config/tomlConfig_test.go @@ -896,20 +896,35 @@ func TestEnableEpochConfig(t *testing.T) { # CleanupAuctionOnLowWaitingListEnableEpoch represents the epoch when the cleanup auction on low waiting list is enabled CleanupAuctionOnLowWaitingListEnableEpoch = 95 + # UseGasBoundedShouldFailExecutionEnableEpoch represents the epoch when use bounded gas function should fail execution in case of error + UseGasBoundedShouldFailExecutionEnableEpoch = 96 + # DynamicESDTEnableEpoch represents the epoch when dynamic NFT feature is enabled - DynamicESDTEnableEpoch = 96 + DynamicESDTEnableEpoch = 97 # EGLDInMultiTransferEnableEpoch represents the epoch when EGLD in MultiTransfer is enabled - EGLDInMultiTransferEnableEpoch = 97 + EGLDInMultiTransferEnableEpoch = 98 # CryptoOpcodesV2EnableEpoch represents the epoch when BLSMultiSig, Secp256r1 and other opcodes are enabled - CryptoOpcodesV2EnableEpoch = 98 + CryptoOpcodesV2EnableEpoch = 99 + + # FixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost will be enabled + FixRelayedBaseCostEnableEpoch = 100 + + # MultiESDTNFTTransferAndExecuteByUserEnableEpoch represents the epoch when enshrined sovereign cross chain opcodes are enabled + MultiESDTNFTTransferAndExecuteByUserEnableEpoch = 101 + + # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled + FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 102 + + # RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3EnableEpoch = 103 # EquivalentMessagesEnableEpoch represents the epoch when the equivalent messages are enabled - EquivalentMessagesEnableEpoch = 99 + EquivalentMessagesEnableEpoch = 104 # FixedOrderInConsensusEnableEpoch represents the epoch when the fixed order in consensus is enabled - FixedOrderInConsensusEnableEpoch = 100 + FixedOrderInConsensusEnableEpoch = 105 # MaxNodesChangeEnableEpoch holds configuration for changing the maximum number of nodes and the enabling epoch MaxNodesChangeEnableEpoch = [ @@ -1024,11 +1039,16 @@ func TestEnableEpochConfig(t *testing.T) { CurrentRandomnessOnSortingEnableEpoch: 93, AlwaysMergeContextsInEEIEnableEpoch: 94, CleanupAuctionOnLowWaitingListEnableEpoch: 95, - DynamicESDTEnableEpoch: 96, - EGLDInMultiTransferEnableEpoch: 97, - CryptoOpcodesV2EnableEpoch: 98, - EquivalentMessagesEnableEpoch: 99, - FixedOrderInConsensusEnableEpoch: 100, + UseGasBoundedShouldFailExecutionEnableEpoch: 96, + DynamicESDTEnableEpoch: 97, + EGLDInMultiTransferEnableEpoch: 98, + CryptoOpcodesV2EnableEpoch: 99, + FixRelayedBaseCostEnableEpoch: 100, + MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 101, + FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 102, + RelayedTransactionsV3EnableEpoch: 103, + EquivalentMessagesEnableEpoch: 104, + FixedOrderInConsensusEnableEpoch: 105, MaxNodesChangeEnableEpoch: []MaxNodesChangeConfig{ { EpochEnable: 44, diff --git a/consensus/broadcast/delayedBroadcast.go b/consensus/broadcast/delayedBroadcast.go index a1c94cf33d7..f9c5769fe8e 100644 --- a/consensus/broadcast/delayedBroadcast.go +++ b/consensus/broadcast/delayedBroadcast.go @@ -717,7 +717,7 @@ func (dbb *delayedBlockBroadcaster) interceptedHeader(_ string, headerHash []byt ) alarmsToCancel := make([]string, 0) - dbb.mutDataForBroadcast.RLock() + dbb.mutDataForBroadcast.Lock() for i, broadcastData := range dbb.valHeaderBroadcastData { samePrevRandSeed := bytes.Equal(broadcastData.Header.GetPrevRandSeed(), headerHandler.GetPrevRandSeed()) sameRound := broadcastData.Header.GetRound() == headerHandler.GetRound() @@ -736,7 +736,7 @@ func (dbb *delayedBlockBroadcaster) interceptedHeader(_ string, headerHash []byt } } - dbb.mutDataForBroadcast.RUnlock() + dbb.mutDataForBroadcast.Unlock() for _, alarmID := range alarmsToCancel { dbb.alarm.Cancel(alarmID) diff --git a/consensus/spos/bls/v2/export_test.go b/consensus/spos/bls/v2/export_test.go index 84ab13e2016..38ecf9c9d59 100644 --- a/consensus/spos/bls/v2/export_test.go +++ b/consensus/spos/bls/v2/export_test.go @@ -263,7 +263,7 @@ func (sr *subroundEndRound) DoEndRoundJobByNode() bool { // CreateAndBroadcastProof calls the unexported createAndBroadcastHeaderFinalInfo function func (sr *subroundEndRound) CreateAndBroadcastProof(signature []byte, bitmap []byte) { - sr.createAndBroadcastProof(signature, bitmap) + _ = sr.createAndBroadcastProof(signature, bitmap) } // ReceivedProof calls the unexported receivedProof function diff --git a/consensus/spos/bls/v2/subroundBlock.go b/consensus/spos/bls/v2/subroundBlock.go index f6566153f72..6838343f57f 100644 --- a/consensus/spos/bls/v2/subroundBlock.go +++ b/consensus/spos/bls/v2/subroundBlock.go @@ -355,6 +355,10 @@ func (sr *subroundBlock) createHeader() (data.HeaderHandler, error) { } func (sr *subroundBlock) saveProofForPreviousHeaderIfNeeded(header data.HeaderHandler, prevHeader data.HeaderHandler) { + if !common.ShouldBlockHavePrevProof(header, sr.EnableEpochsHandler(), common.EquivalentMessagesFlag) { + return + } + hasProof := sr.EquivalentProofsPool().HasProof(sr.ShardCoordinator().SelfId(), header.GetPrevHash()) if hasProof { log.Debug("saveProofForPreviousHeaderIfNeeded: no need to set proof since it is already saved") @@ -364,7 +368,7 @@ func (sr *subroundBlock) saveProofForPreviousHeaderIfNeeded(header data.HeaderHa proof := header.GetPreviousProof() err := common.VerifyProofAgainstHeader(proof, prevHeader) if err != nil { - log.Debug("saveProofForPreviousHeaderIfNeeded: invalid proof", "error", err) + log.Debug("saveProofForPreviousHeaderIfNeeded: invalid proof", "error", err.Error()) return } diff --git a/consensus/spos/bls/v2/subroundEndRound.go b/consensus/spos/bls/v2/subroundEndRound.go index a6d763d28c4..b68cf550614 100644 --- a/consensus/spos/bls/v2/subroundEndRound.go +++ b/consensus/spos/bls/v2/subroundEndRound.go @@ -11,7 +11,6 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/display" @@ -247,10 +246,36 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool { if !sr.waitForSignalSync() { return false } + sr.sendProof() } - proof, ok := sr.sendProof() - if !ok { + return sr.finalizeConfirmedBlock() +} + +func (sr *subroundEndRound) waitForProof() bool { + shardID := sr.ShardCoordinator().SelfId() + headerHash := sr.GetData() + if sr.EquivalentProofsPool().HasProof(shardID, headerHash) { + return true + } + + ctx, cancel := context.WithTimeout(context.Background(), sr.RoundHandler().TimeDuration()) + defer cancel() + + for { + select { + case <-time.After(time.Millisecond): + if sr.EquivalentProofsPool().HasProof(shardID, headerHash) { + return true + } + case <-ctx.Done(): + return false + } + } +} + +func (sr *subroundEndRound) finalizeConfirmedBlock() bool { + if !sr.waitForProof() { return false } @@ -259,14 +284,6 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool { return false } - // if proof not nil, it was created and broadcasted so it has to be added to the pool - if proof != nil { - ok := sr.EquivalentProofsPool().AddProof(proof) - if !ok { - log.Trace("doEndRoundJobByNode.AddProof", "added", ok) - } - } - sr.SetStatus(sr.Current(), spos.SsFinished) sr.worker.DisplayStatistics() @@ -281,29 +298,29 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool { return true } -func (sr *subroundEndRound) sendProof() (data.HeaderProofHandler, bool) { +func (sr *subroundEndRound) sendProof() { if !sr.shouldSendProof() { - return nil, true + return } bitmap := sr.GenerateBitmap(bls.SrSignature) err := sr.checkSignaturesValidity(bitmap) if err != nil { log.Debug("sendProof.checkSignaturesValidity", "error", err.Error()) - return nil, false + return } // Aggregate signatures, handle invalid signers and send final info if needed bitmap, sig, err := sr.aggregateSigsAndHandleInvalidSigners(bitmap) if err != nil { log.Debug("sendProof.aggregateSigsAndHandleInvalidSigners", "error", err.Error()) - return nil, false + return } ok := sr.ScheduledProcessor().IsProcessedOKWithTimeout() // placeholder for subroundEndRound.doEndRoundJobByLeader script if !ok { - return nil, false + return } roundHandler := sr.RoundHandler() @@ -311,12 +328,14 @@ func (sr *subroundEndRound) sendProof() (data.HeaderProofHandler, bool) { log.Debug("sendProof: time is out -> cancel broadcasting final info and header", "round time stamp", roundHandler.TimeStamp(), "current time", time.Now()) - return nil, false + return } // broadcast header proof - proof, err := sr.createAndBroadcastProof(sig, bitmap) - return proof, err == nil + err = sr.createAndBroadcastProof(sig, bitmap) + if err != nil { + log.Warn("sendProof.createAndBroadcastProof", "error", err.Error()) + } } func (sr *subroundEndRound) shouldSendProof() bool { @@ -530,7 +549,7 @@ func (sr *subroundEndRound) computeAggSigOnValidNodes() ([]byte, []byte, error) return bitmap, sig, nil } -func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []byte) (*block.HeaderProof, error) { +func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []byte) error { headerProof := &block.HeaderProof{ PubKeysBitmap: bitmap, AggregatedSignature: signature, @@ -544,14 +563,14 @@ func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []b err := sr.BroadcastMessenger().BroadcastEquivalentProof(headerProof, []byte(sr.SelfPubKey())) if err != nil { - return nil, err + return err } log.Debug("step 3: block header proof has been sent", "PubKeysBitmap", bitmap, "AggregateSignature", signature) - return headerProof, nil + return nil } func (sr *subroundEndRound) createAndBroadcastInvalidSigners(invalidSigners []byte) { diff --git a/consensus/spos/bls/v2/subroundEndRound_test.go b/consensus/spos/bls/v2/subroundEndRound_test.go index 394df2b8d20..966d7fcbea3 100644 --- a/consensus/spos/bls/v2/subroundEndRound_test.go +++ b/consensus/spos/bls/v2/subroundEndRound_test.go @@ -572,6 +572,11 @@ func TestSubroundEndRound_DoEndRoundJobAllOK(t *testing.T) { t.Parallel() container := consensusMocks.InitConsensusCore() + container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return true + }, + }) sr := initSubroundEndRoundWithContainer(container, &statusHandler.AppStatusHandlerStub{}) sr.SetSelfPubKey("A") @@ -1176,6 +1181,16 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { t.Parallel() container := consensusMocks.InitConsensusCore() + numCalls := 0 + container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + if numCalls <= 1 { + numCalls++ + return false + } + return true + }, + }) sr := initSubroundEndRoundWithContainer(container, &statusHandler.AppStatusHandlerStub{}) verifySigShareNumCalls := 0 @@ -1227,14 +1242,17 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { t.Run("should work with equivalent messages flag active", func(t *testing.T) { t.Parallel() - providedPrevSig := []byte("prev sig") - providedPrevBitmap := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1} container := consensusMocks.InitConsensusCore() container.SetBlockchain(&testscommon.ChainHandlerStub{ GetGenesisHeaderCalled: func() data.HeaderHandler { return &block.HeaderV2{} }, }) + container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return true + }, + }) enableEpochsHandler := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { return flag == common.EquivalentMessagesFlag @@ -1242,16 +1260,6 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { } container.SetEnableEpochsHandler(enableEpochsHandler) - wasSetCurrentHeaderProofCalled := false - container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ - AddProofCalled: func(headerProof data.HeaderProofHandler) bool { - wasSetCurrentHeaderProofCalled = true - require.NotEqual(t, providedPrevSig, headerProof.GetAggregatedSignature()) - require.NotEqual(t, providedPrevBitmap, headerProof.GetPubKeysBitmap()) - return true - }, - }) - ch := make(chan bool, 1) consensusState := initializers.InitConsensusState() sr, _ := spos.NewSubround( @@ -1295,7 +1303,6 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { r := srEndRound.DoEndRoundJobByNode() require.True(t, r) - require.True(t, wasSetCurrentHeaderProofCalled) }) } diff --git a/dataRetriever/constants.go b/dataRetriever/constants.go index ef8ceaca082..198e2ea4ea0 100644 --- a/dataRetriever/constants.go +++ b/dataRetriever/constants.go @@ -1,8 +1,5 @@ package dataRetriever -// TxPoolNumSendersToPreemptivelyEvict instructs tx pool eviction algorithm to remove this many senders when eviction takes place -const TxPoolNumSendersToPreemptivelyEvict = uint32(100) - // UnsignedTxPoolName defines the name of the unsigned transactions pool const UnsignedTxPoolName = "uTxPool" diff --git a/dataRetriever/dataPool/headersCache/headersPool.go b/dataRetriever/dataPool/headersCache/headersPool.go index 8b2e044b432..dc8759441c9 100644 --- a/dataRetriever/dataPool/headersCache/headersPool.go +++ b/dataRetriever/dataPool/headersCache/headersPool.go @@ -65,7 +65,7 @@ func (pool *headersPool) AddHeader(headerHash []byte, header data.HeaderHandler) added := pool.cache.addHeader(headerHash, header) if added { - log.Debug("TOREMOVE - added header to pool", "cache ptr", fmt.Sprintf("%p", pool.cache), "header shard", header.GetShardID(), "header nonce", header.GetNonce()) + log.Debug("added header to pool", "header shard", header.GetShardID(), "header nonce", header.GetNonce(), "header hash", headerHash) pool.callAddedDataHandlers(header, headerHash) } } diff --git a/dataRetriever/dataPool/proofsCache/proofsPool_test.go b/dataRetriever/dataPool/proofsCache/proofsPool_test.go index cbdf6fefed8..14b25f63541 100644 --- a/dataRetriever/dataPool/proofsCache/proofsPool_test.go +++ b/dataRetriever/dataPool/proofsCache/proofsPool_test.go @@ -82,6 +82,7 @@ func TestProofsPool_ShouldWork(t *testing.T) { proof, err = pp.GetProof(shardID, []byte("hash3")) require.Nil(t, err) + require.Equal(t, proof3, proof) proof, err = pp.GetProof(shardID, []byte("hash4")) require.Nil(t, err) diff --git a/dataRetriever/errors.go b/dataRetriever/errors.go index 21465bf26c7..1c0524aa0b8 100644 --- a/dataRetriever/errors.go +++ b/dataRetriever/errors.go @@ -116,9 +116,6 @@ var ErrCacheConfigInvalidSize = errors.New("cache parameter [size] is not valid, // ErrCacheConfigInvalidShards signals that the cache parameter "shards" is invalid var ErrCacheConfigInvalidShards = errors.New("cache parameter [shards] is not valid, it must be a positive number") -// ErrCacheConfigInvalidEconomics signals that an economics parameter required by the cache is invalid -var ErrCacheConfigInvalidEconomics = errors.New("cache-economics parameter is not valid") - // ErrCacheConfigInvalidSharding signals that a sharding parameter required by the cache is invalid var ErrCacheConfigInvalidSharding = errors.New("cache-sharding parameter is not valid") diff --git a/dataRetriever/factory/dataPoolFactory.go b/dataRetriever/factory/dataPoolFactory.go index 56c2ab152e5..e869b249fdc 100644 --- a/dataRetriever/factory/dataPoolFactory.go +++ b/dataRetriever/factory/dataPoolFactory.go @@ -54,6 +54,9 @@ func NewDataPoolFromConfig(args ArgsDataPool) (dataRetriever.PoolsHolder, error) if check.IfNil(args.ShardCoordinator) { return nil, dataRetriever.ErrNilShardCoordinator } + if check.IfNil(args.Marshalizer) { + return nil, dataRetriever.ErrNilMarshalizer + } if check.IfNil(args.PathManager) { return nil, dataRetriever.ErrNilPathManager } @@ -62,9 +65,10 @@ func NewDataPoolFromConfig(args ArgsDataPool) (dataRetriever.PoolsHolder, error) txPool, err := txpool.NewShardedTxPool(txpool.ArgShardedTxPool{ Config: factory.GetCacherFromConfig(mainConfig.TxDataPool), + TxGasHandler: args.EconomicsData, + Marshalizer: args.Marshalizer, NumberOfShards: args.ShardCoordinator.NumberOfShards(), SelfShardID: args.ShardCoordinator.SelfId(), - TxGasHandler: args.EconomicsData, }) if err != nil { return nil, fmt.Errorf("%w while creating the cache for the transactions", err) diff --git a/dataRetriever/factory/dataPoolFactory_test.go b/dataRetriever/factory/dataPoolFactory_test.go index c9ae8b60c43..b4896244ad9 100644 --- a/dataRetriever/factory/dataPoolFactory_test.go +++ b/dataRetriever/factory/dataPoolFactory_test.go @@ -41,6 +41,12 @@ func TestNewDataPoolFromConfig_MissingDependencyShouldErr(t *testing.T) { require.Nil(t, holder) require.Equal(t, dataRetriever.ErrNilShardCoordinator, err) + args = getGoodArgs() + args.Marshalizer = nil + holder, err = NewDataPoolFromConfig(args) + require.Nil(t, holder) + require.Equal(t, dataRetriever.ErrNilMarshalizer, err) + args = getGoodArgs() args.PathManager = nil holder, err = NewDataPoolFromConfig(args) @@ -76,7 +82,6 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.HeadersPoolConfig.MaxHeadersPerShard = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.True(t, errors.Is(err, headersCache.ErrInvalidHeadersCacheParameter)) require.True(t, strings.Contains(err.Error(), "the cache for the headers")) @@ -84,7 +89,6 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.TxBlockBodyDataPool.Capacity = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.NotNil(t, err) require.True(t, strings.Contains(err.Error(), "must provide a positive size while creating the cache for the miniblocks")) @@ -92,7 +96,6 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.PeerBlockBodyDataPool.Capacity = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.NotNil(t, err) require.True(t, strings.Contains(err.Error(), "must provide a positive size while creating the cache for the peer mini block body")) @@ -100,19 +103,9 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.TrieSyncStorage.Capacity = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.True(t, errors.Is(err, storage.ErrCacheSizeInvalid)) require.True(t, strings.Contains(err.Error(), "the cache for the trie nodes")) - args = getGoodArgs() - args.Config.TrieSyncStorage.EnableDB = true - args.Config.TrieSyncStorage.DB.Type = "invalid DB type" - holder, err = NewDataPoolFromConfig(args) - require.Nil(t, holder) - fmt.Println(err) - require.True(t, errors.Is(err, storage.ErrNotSupportedDBType)) - require.True(t, strings.Contains(err.Error(), "the db for the trie nodes")) - args = getGoodArgs() args.Config.TrieNodesChunksDataPool.Type = "invalid cache type" holder, err = NewDataPoolFromConfig(args) diff --git a/dataRetriever/requestHandlers/requestHandler.go b/dataRetriever/requestHandlers/requestHandler.go index 7166715dd3c..91e4992aee3 100644 --- a/dataRetriever/requestHandlers/requestHandler.go +++ b/dataRetriever/requestHandlers/requestHandler.go @@ -14,7 +14,7 @@ import ( "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/process/factory" - "github.com/multiversx/mx-chain-logger-go" + logger "github.com/multiversx/mx-chain-logger-go" ) var _ epochStart.RequestHandler = (*resolverRequestHandler)(nil) @@ -571,10 +571,12 @@ func (rrh *resolverRequestHandler) RequestValidatorInfo(hash []byte) { return } + epoch := rrh.getEpoch() + log.Debug("requesting validator info messages from network", "topic", common.ValidatorInfoTopic, "hash", hash, - "epoch", rrh.epoch, + "epoch", epoch, ) requester, err := rrh.requestersFinder.MetaChainRequester(common.ValidatorInfoTopic) @@ -583,20 +585,20 @@ func (rrh *resolverRequestHandler) RequestValidatorInfo(hash []byte) { "error", err.Error(), "topic", common.ValidatorInfoTopic, "hash", hash, - "epoch", rrh.epoch, + "epoch", epoch, ) return } rrh.whiteList.Add([][]byte{hash}) - err = requester.RequestDataFromHash(hash, rrh.epoch) + err = requester.RequestDataFromHash(hash, epoch) if err != nil { log.Debug("RequestValidatorInfo.RequestDataFromHash", "error", err.Error(), "topic", common.ValidatorInfoTopic, "hash", hash, - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -611,10 +613,12 @@ func (rrh *resolverRequestHandler) RequestValidatorsInfo(hashes [][]byte) { return } + epoch := rrh.getEpoch() + log.Debug("requesting validator info messages from network", "topic", common.ValidatorInfoTopic, "num hashes", len(unrequestedHashes), - "epoch", rrh.epoch, + "epoch", epoch, ) requester, err := rrh.requestersFinder.MetaChainRequester(common.ValidatorInfoTopic) @@ -623,7 +627,7 @@ func (rrh *resolverRequestHandler) RequestValidatorsInfo(hashes [][]byte) { "error", err.Error(), "topic", common.ValidatorInfoTopic, "num hashes", len(unrequestedHashes), - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -636,13 +640,13 @@ func (rrh *resolverRequestHandler) RequestValidatorsInfo(hashes [][]byte) { rrh.whiteList.Add(unrequestedHashes) - err = validatorInfoRequester.RequestDataFromHashArray(unrequestedHashes, rrh.epoch) + err = validatorInfoRequester.RequestDataFromHashArray(unrequestedHashes, epoch) if err != nil { log.Debug("RequestValidatorInfo.RequestDataFromHash", "error", err.Error(), "topic", common.ValidatorInfoTopic, "num hashes", len(unrequestedHashes), - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -827,11 +831,13 @@ func (rrh *resolverRequestHandler) GetNumPeersToQuery(key string) (int, int, err // RequestPeerAuthenticationsByHashes asks for peer authentication messages from specific peers hashes func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashes(destShardID uint32, hashes [][]byte) { + epoch := rrh.getEpoch() + log.Debug("requesting peer authentication messages from network", "topic", common.PeerAuthenticationTopic, "shard", destShardID, "num hashes", len(hashes), - "epoch", rrh.epoch, + "epoch", epoch, ) requester, err := rrh.requestersFinder.MetaChainRequester(common.PeerAuthenticationTopic) @@ -840,7 +846,7 @@ func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashes(destShardI "error", err.Error(), "topic", common.PeerAuthenticationTopic, "shard", destShardID, - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -851,13 +857,13 @@ func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashes(destShardI return } - err = peerAuthRequester.RequestDataFromHashArray(hashes, rrh.epoch) + err = peerAuthRequester.RequestDataFromHashArray(hashes, epoch) if err != nil { log.Debug("RequestPeerAuthenticationsByHashes.RequestDataFromHashArray", "error", err.Error(), "topic", common.PeerAuthenticationTopic, "shard", destShardID, - "epoch", rrh.epoch, + "epoch", epoch, ) } } diff --git a/dataRetriever/shardedData/shardedData.go b/dataRetriever/shardedData/shardedData.go index 2800bb3352b..785998164b9 100644 --- a/dataRetriever/shardedData/shardedData.go +++ b/dataRetriever/shardedData/shardedData.go @@ -4,6 +4,7 @@ import ( "fmt" "sync" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/counting" "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -52,7 +53,7 @@ func NewShardedData(name string, config storageunit.CacheConfig) (*shardedData, NumChunks: config.Shards, MaxNumItems: config.Capacity, MaxNumBytes: uint32(config.SizeInBytes), - NumItemsToPreemptivelyEvict: storage.TxPoolNumTxsToPreemptivelyEvict, + NumItemsToPreemptivelyEvict: storage.ShardedDataNumItemsToPreemptivelyEvict, } err := configPrototype.Verify() @@ -161,6 +162,9 @@ func (sd *shardedData) RemoveSetOfDataFromPool(keys [][]byte, cacheID string) { return } + stopWatch := core.NewStopWatch() + stopWatch.Start("removal") + numRemoved := 0 for _, key := range keys { if store.cache.RemoveWithResult(key) { @@ -168,7 +172,15 @@ func (sd *shardedData) RemoveSetOfDataFromPool(keys [][]byte, cacheID string) { } } - log.Trace("shardedData.removeTxBulk()", "name", sd.name, "cacheID", cacheID, "numToRemove", len(keys), "numRemoved", numRemoved) + stopWatch.Stop("removal") + + log.Debug("shardedData.removeTxBulk", + "name", sd.name, + "cacheID", cacheID, + "numToRemove", len(keys), + "numRemoved", numRemoved, + "duration", stopWatch.GetMeasurement("removal"), + ) } // ImmunizeSetOfDataAgainstEviction marks the items as non-evictable diff --git a/dataRetriever/txpool/argShardedTxPool.go b/dataRetriever/txpool/argShardedTxPool.go index 8b12dbddf7a..dca1efa56bd 100644 --- a/dataRetriever/txpool/argShardedTxPool.go +++ b/dataRetriever/txpool/argShardedTxPool.go @@ -1,19 +1,19 @@ package txpool import ( - "encoding/json" "fmt" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/storage/storageunit" - "github.com/multiversx/mx-chain-go/storage/txcache" ) // ArgShardedTxPool is the argument for ShardedTxPool's constructor type ArgShardedTxPool struct { Config storageunit.CacheConfig - TxGasHandler txcache.TxGasHandler + TxGasHandler txGasHandler + Marshalizer marshal.Marshalizer NumberOfShards uint32 SelfShardID uint32 } @@ -40,8 +40,8 @@ func (args *ArgShardedTxPool) verify() error { if check.IfNil(args.TxGasHandler) { return fmt.Errorf("%w: TxGasHandler is not valid", dataRetriever.ErrNilTxGasHandler) } - if args.TxGasHandler.MinGasPrice() == 0 { - return fmt.Errorf("%w: MinGasPrice is not valid", dataRetriever.ErrCacheConfigInvalidEconomics) + if check.IfNil(args.Marshalizer) { + return fmt.Errorf("%w: Marshalizer is not valid", dataRetriever.ErrNilMarshalizer) } if args.NumberOfShards == 0 { return fmt.Errorf("%w: NumberOfShards is not valid", dataRetriever.ErrCacheConfigInvalidSharding) @@ -49,13 +49,3 @@ func (args *ArgShardedTxPool) verify() error { return nil } - -// String returns a readable representation of the object -func (args *ArgShardedTxPool) String() string { - bytes, err := json.Marshal(args) - if err != nil { - log.Error("ArgShardedTxPool.String()", "err", err) - } - - return string(bytes) -} diff --git a/dataRetriever/txpool/interface.go b/dataRetriever/txpool/interface.go index 6579659d692..ee55a246a48 100644 --- a/dataRetriever/txpool/interface.go +++ b/dataRetriever/txpool/interface.go @@ -1,6 +1,9 @@ package txpool import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/txcache" ) @@ -17,3 +20,8 @@ type txCache interface { Diagnose(deep bool) GetTransactionsPoolForSender(sender string) []*txcache.WrappedTransaction } + +type txGasHandler interface { + ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int + IsInterfaceNil() bool +} diff --git a/dataRetriever/txpool/memorytests/memory_test.go b/dataRetriever/txpool/memorytests/memory_test.go index d2d48fbbcd5..e4e7f5f68b8 100644 --- a/dataRetriever/txpool/memorytests/memory_test.go +++ b/dataRetriever/txpool/memorytests/memory_test.go @@ -12,6 +12,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/txpool" "github.com/multiversx/mx-chain-go/storage/storageunit" @@ -35,26 +36,26 @@ func TestShardedTxPool_MemoryFootprint(t *testing.T) { journals = append(journals, runScenario(t, newScenario(200, 1, core.MegabyteSize, "0"), memoryAssertion{200, 200}, memoryAssertion{0, 1})) journals = append(journals, runScenario(t, newScenario(10, 1000, 20480, "0"), memoryAssertion{190, 205}, memoryAssertion{1, 4})) - journals = append(journals, runScenario(t, newScenario(10000, 1, 1024, "0"), memoryAssertion{10, 16}, memoryAssertion{4, 10})) - journals = append(journals, runScenario(t, newScenario(1, 60000, 256, "0"), memoryAssertion{30, 36}, memoryAssertion{10, 16})) - journals = append(journals, runScenario(t, newScenario(10, 10000, 100, "0"), memoryAssertion{36, 46}, memoryAssertion{16, 24})) - journals = append(journals, runScenario(t, newScenario(100000, 1, 1024, "0"), memoryAssertion{120, 136}, memoryAssertion{56, 60})) + journals = append(journals, runScenario(t, newScenario(10000, 1, 1024, "0"), memoryAssertion{10, 16}, memoryAssertion{0, 10})) + journals = append(journals, runScenario(t, newScenario(1, 60000, 256, "0"), memoryAssertion{30, 40}, memoryAssertion{10, 24})) + journals = append(journals, runScenario(t, newScenario(10, 10000, 100, "0"), memoryAssertion{36, 52}, memoryAssertion{16, 36})) + journals = append(journals, runScenario(t, newScenario(100000, 1, 1024, "0"), memoryAssertion{120, 138}, memoryAssertion{32, 60})) // With larger memory footprint - journals = append(journals, runScenario(t, newScenario(100000, 3, 650, "0"), memoryAssertion{290, 320}, memoryAssertion{95, 120})) - journals = append(journals, runScenario(t, newScenario(150000, 2, 650, "0"), memoryAssertion{290, 320}, memoryAssertion{120, 140})) - journals = append(journals, runScenario(t, newScenario(300000, 1, 650, "0"), memoryAssertion{290, 320}, memoryAssertion{170, 190})) - journals = append(journals, runScenario(t, newScenario(30, 10000, 650, "0"), memoryAssertion{290, 320}, memoryAssertion{60, 75})) - journals = append(journals, runScenario(t, newScenario(300, 1000, 650, "0"), memoryAssertion{290, 320}, memoryAssertion{60, 80})) + journals = append(journals, runScenario(t, newScenario(100000, 3, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{80, 148})) + journals = append(journals, runScenario(t, newScenario(150000, 2, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{90, 160})) + journals = append(journals, runScenario(t, newScenario(300000, 1, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{100, 190})) + journals = append(journals, runScenario(t, newScenario(30, 10000, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{60, 132})) + journals = append(journals, runScenario(t, newScenario(300, 1000, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{60, 148})) // Scenarios where destination == me journals = append(journals, runScenario(t, newScenario(100, 1, core.MegabyteSize, "1_0"), memoryAssertion{90, 100}, memoryAssertion{0, 1})) journals = append(journals, runScenario(t, newScenario(10000, 1, 10240, "1_0"), memoryAssertion{96, 128}, memoryAssertion{0, 4})) - journals = append(journals, runScenario(t, newScenario(10, 10000, 1000, "1_0"), memoryAssertion{96, 136}, memoryAssertion{16, 25})) - journals = append(journals, runScenario(t, newScenario(150000, 1, 128, "1_0"), memoryAssertion{50, 75}, memoryAssertion{30, 40})) - journals = append(journals, runScenario(t, newScenario(1, 150000, 128, "1_0"), memoryAssertion{50, 75}, memoryAssertion{30, 40})) + journals = append(journals, runScenario(t, newScenario(10, 10000, 1000, "1_0"), memoryAssertion{96, 148}, memoryAssertion{16, 32})) + journals = append(journals, runScenario(t, newScenario(150000, 1, 128, "1_0"), memoryAssertion{50, 84}, memoryAssertion{30, 48})) + journals = append(journals, runScenario(t, newScenario(1, 150000, 128, "1_0"), memoryAssertion{50, 84}, memoryAssertion{30, 48})) for _, journal := range journals { journal.displayFootprintsSummary() @@ -110,12 +111,9 @@ func newPool() dataRetriever.ShardedDataCacherNotifier { } args := txpool.ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 2, SelfShardID: 0, } @@ -187,9 +185,10 @@ func createTxWithPayload(senderTag int, nonce int, payloadLength int) *dummyTx { return &dummyTx{ Transaction: transaction.Transaction{ - SndAddr: sender, - Nonce: uint64(nonce), - Data: make([]byte, payloadLength), + SndAddr: sender, + Nonce: uint64(nonce), + Data: make([]byte, payloadLength), + GasLimit: uint64(50000 + 1500*payloadLength), }, hash: hash, } diff --git a/dataRetriever/txpool/mempoolHost.go b/dataRetriever/txpool/mempoolHost.go new file mode 100644 index 00000000000..916dd436cd7 --- /dev/null +++ b/dataRetriever/txpool/mempoolHost.go @@ -0,0 +1,112 @@ +package txpool + +import ( + "bytes" + "math/big" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/process" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-vm-common-go/parsers" +) + +type argsMempoolHost struct { + txGasHandler txGasHandler + marshalizer marshal.Marshalizer +} + +type mempoolHost struct { + txGasHandler txGasHandler + callArgumentsParser process.CallArgumentsParser + esdtTransferParser vmcommon.ESDTTransferParser +} + +func newMempoolHost(args argsMempoolHost) (*mempoolHost, error) { + if check.IfNil(args.txGasHandler) { + return nil, dataRetriever.ErrNilTxGasHandler + } + if check.IfNil(args.marshalizer) { + return nil, dataRetriever.ErrNilMarshalizer + } + + argsParser := parsers.NewCallArgsParser() + + esdtTransferParser, err := parsers.NewESDTTransferParser(args.marshalizer) + if err != nil { + return nil, err + } + + return &mempoolHost{ + txGasHandler: args.txGasHandler, + callArgumentsParser: argsParser, + esdtTransferParser: esdtTransferParser, + }, nil +} + +// ComputeTxFee computes the fee for a transaction. +func (host *mempoolHost) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { + return host.txGasHandler.ComputeTxFee(tx) +} + +// GetTransferredValue returns the value transferred by a transaction. +func (host *mempoolHost) GetTransferredValue(tx data.TransactionHandler) *big.Int { + value := tx.GetValue() + hasValue := value != nil && value.Sign() != 0 + if hasValue { + // Early exit (optimization): a transaction can either bear a regular value or be a "MultiESDTNFTTransfer". + return value + } + + data := tx.GetData() + hasData := len(data) > 0 + if !hasData { + // Early exit (optimization): no "MultiESDTNFTTransfer" to parse. + return tx.GetValue() + } + + maybeMultiTransfer := bytes.HasPrefix(data, []byte(core.BuiltInFunctionMultiESDTNFTTransfer)) + if !maybeMultiTransfer { + // Early exit (optimization). + return big.NewInt(0) + } + + function, args, err := host.callArgumentsParser.ParseData(string(data)) + if err != nil { + return big.NewInt(0) + } + + if function != core.BuiltInFunctionMultiESDTNFTTransfer { + // Early exit (optimization). + return big.NewInt(0) + } + + esdtTransfers, err := host.esdtTransferParser.ParseESDTTransfers(tx.GetSndAddr(), tx.GetRcvAddr(), function, args) + if err != nil { + return big.NewInt(0) + } + + accumulatedNativeValue := big.NewInt(0) + + for _, transfer := range esdtTransfers.ESDTTransfers { + if transfer.ESDTTokenNonce != 0 { + continue + } + if string(transfer.ESDTTokenName) != vmcommon.EGLDIdentifier { + // We only care about native transfers. + continue + } + + _ = accumulatedNativeValue.Add(accumulatedNativeValue, transfer.ESDTValue) + } + + return accumulatedNativeValue +} + +// IsInterfaceNil returns true if there is no value under the interface +func (host *mempoolHost) IsInterfaceNil() bool { + return host == nil +} diff --git a/dataRetriever/txpool/mempoolHost_test.go b/dataRetriever/txpool/mempoolHost_test.go new file mode 100644 index 00000000000..a013a88fa19 --- /dev/null +++ b/dataRetriever/txpool/mempoolHost_test.go @@ -0,0 +1,182 @@ +package txpool + +import ( + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/stretchr/testify/require" +) + +func TestNewMempoolHost(t *testing.T) { + t.Parallel() + + host, err := newMempoolHost(argsMempoolHost{ + txGasHandler: nil, + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.Nil(t, host) + require.ErrorIs(t, err, dataRetriever.ErrNilTxGasHandler) + + host, err = newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: nil, + }) + require.Nil(t, host) + require.ErrorIs(t, err, dataRetriever.ErrNilMarshalizer) + + host, err = newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.NoError(t, err) + require.NotNil(t, host) +} + +func TestMempoolHost_GetTransferredValue(t *testing.T) { + t.Parallel() + + host, err := newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.NoError(t, err) + require.NotNil(t, host) + + t.Run("with value", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + Value: big.NewInt(1000000000000000000), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) + + t.Run("with value and data", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + Value: big.NewInt(1000000000000000000), + Data: []byte("data"), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) + + t.Run("native transfer within MultiESDTNFTTransfer", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + SndAddr: testscommon.TestPubKeyAlice, + RcvAddr: testscommon.TestPubKeyAlice, + Data: []byte("MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@03@4e46542d313233343536@0a@01@544553542d393837363534@01@01@45474c442d303030303030@@0de0b6b3a7640000"), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) + + t.Run("native transfer within MultiESDTNFTTransfer; transfer & execute", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + SndAddr: testscommon.TestPubKeyAlice, + RcvAddr: testscommon.TestPubKeyAlice, + Data: []byte("MultiESDTNFTTransfer@00000000000000000500b9353fe8407f87310c87e12fa1ac807f0485da39d152@03@4e46542d313233343536@01@01@4e46542d313233343536@2a@01@45474c442d303030303030@@0de0b6b3a7640000@64756d6d79@07"), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) +} + +func TestBenchmarkMempoolHost_GetTransferredValue(t *testing.T) { + host, err := newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.NoError(t, err) + require.NotNil(t, host) + + sw := core.NewStopWatch() + + valueMultiplier := int64(1_000_000_000_000) + + t.Run("numTransactions = 5_000", func(t *testing.T) { + numTransactions := 5_000 + transactions := createMultiESDTNFTTransfersWithNativeTransfer(numTransactions, valueMultiplier) + + sw.Start(t.Name()) + + for i := 0; i < numTransactions; i++ { + tx := transactions[i] + value := host.GetTransferredValue(tx) + require.Equal(t, big.NewInt(int64(i)*valueMultiplier), value) + } + + sw.Stop(t.Name()) + }) + + t.Run("numTransactions = 10_000", func(t *testing.T) { + numTransactions := 10_000 + transactions := createMultiESDTNFTTransfersWithNativeTransfer(numTransactions, valueMultiplier) + + sw.Start(t.Name()) + + for i := 0; i < numTransactions; i++ { + tx := transactions[i] + value := host.GetTransferredValue(tx) + require.Equal(t, big.NewInt(int64(i)*valueMultiplier), value) + } + + sw.Stop(t.Name()) + }) + + t.Run("numTransactions = 20_000", func(t *testing.T) { + numTransactions := 20_000 + transactions := createMultiESDTNFTTransfersWithNativeTransfer(numTransactions, valueMultiplier) + + sw.Start(t.Name()) + + for i := 0; i < numTransactions; i++ { + tx := transactions[i] + value := host.GetTransferredValue(tx) + require.Equal(t, big.NewInt(int64(i)*valueMultiplier), value) + } + + sw.Stop(t.Name()) + }) + + for name, measurement := range sw.GetMeasurementsMap() { + fmt.Printf("%fs (%s)\n", measurement, name) + } + + // (1) + // Vendor ID: GenuineIntel + // Model name: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz + // CPU family: 6 + // Model: 140 + // Thread(s) per core: 2 + // Core(s) per socket: 4 + // + // NOTE: 20% is also due to the require() / assert() calls. + // 0.012993s (TestBenchmarkMempoolHost_GetTransferredValue/numTransactions_=_5_000) + // 0.024580s (TestBenchmarkMempoolHost_GetTransferredValue/numTransactions_=_10_000) + // 0.048808s (TestBenchmarkMempoolHost_GetTransferredValue/numTransactions_=_20_000) +} + +func createMultiESDTNFTTransfersWithNativeTransfer(numTransactions int, valueMultiplier int64) []*transaction.Transaction { + transactions := make([]*transaction.Transaction, 0, numTransactions) + + for i := 0; i < numTransactions; i++ { + nativeValue := big.NewInt(int64(i) * valueMultiplier) + data := fmt.Sprintf( + "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@03@4e46542d313233343536@0a@01@544553542d393837363534@01@01@45474c442d303030303030@@%s", + hex.EncodeToString(nativeValue.Bytes()), + ) + + tx := &transaction.Transaction{ + SndAddr: testscommon.TestPubKeyAlice, + RcvAddr: testscommon.TestPubKeyAlice, + Data: []byte(data), + } + + transactions = append(transactions, tx) + } + + return transactions +} diff --git a/dataRetriever/txpool/shardedTxPool.go b/dataRetriever/txpool/shardedTxPool.go index af09753bd52..0f40817893d 100644 --- a/dataRetriever/txpool/shardedTxPool.go +++ b/dataRetriever/txpool/shardedTxPool.go @@ -27,7 +27,7 @@ type shardedTxPool struct { configPrototypeDestinationMe txcache.ConfigDestinationMe configPrototypeSourceMe txcache.ConfigSourceMe selfShardID uint32 - txGasHandler txcache.TxGasHandler + host txcache.MempoolHost } type txPoolShard struct { @@ -38,24 +38,32 @@ type txPoolShard struct { // NewShardedTxPool creates a new sharded tx pool // Implements "dataRetriever.TxPool" func NewShardedTxPool(args ArgShardedTxPool) (*shardedTxPool, error) { - log.Debug("NewShardedTxPool", "args", args.String()) + log.Debug("NewShardedTxPool", "args.SelfShardID", args.SelfShardID) err := args.verify() if err != nil { return nil, err } + mempoolHost, err := newMempoolHost(argsMempoolHost{ + txGasHandler: args.TxGasHandler, + marshalizer: args.Marshalizer, + }) + if err != nil { + return nil, err + } + halfOfSizeInBytes := args.Config.SizeInBytes / 2 halfOfCapacity := args.Config.Capacity / 2 configPrototypeSourceMe := txcache.ConfigSourceMe{ - NumChunks: args.Config.Shards, - EvictionEnabled: true, - NumBytesThreshold: uint32(halfOfSizeInBytes), - CountThreshold: halfOfCapacity, - NumBytesPerSenderThreshold: args.Config.SizeInBytesPerSender, - CountPerSenderThreshold: args.Config.SizePerSender, - NumSendersToPreemptivelyEvict: dataRetriever.TxPoolNumSendersToPreemptivelyEvict, + NumChunks: args.Config.Shards, + EvictionEnabled: true, + NumBytesThreshold: uint32(halfOfSizeInBytes), + CountThreshold: halfOfCapacity, + NumBytesPerSenderThreshold: args.Config.SizeInBytesPerSender, + CountPerSenderThreshold: args.Config.SizePerSender, + NumItemsToPreemptivelyEvict: storage.TxPoolSourceMeNumItemsToPreemptivelyEvict, } // We do not reserve cross tx cache capacity for [metachain] -> [me] (no transactions), [me] -> me (already reserved above). @@ -66,7 +74,7 @@ func NewShardedTxPool(args ArgShardedTxPool) (*shardedTxPool, error) { NumChunks: args.Config.Shards, MaxNumBytes: uint32(halfOfSizeInBytes) / numCrossTxCaches, MaxNumItems: halfOfCapacity / numCrossTxCaches, - NumItemsToPreemptivelyEvict: storage.TxPoolNumTxsToPreemptivelyEvict, + NumItemsToPreemptivelyEvict: storage.TxPoolDestinationMeNumItemsToPreemptivelyEvict, } shardedTxPoolObject := &shardedTxPool{ @@ -77,7 +85,7 @@ func NewShardedTxPool(args ArgShardedTxPool) (*shardedTxPool, error) { configPrototypeDestinationMe: configPrototypeDestinationMe, configPrototypeSourceMe: configPrototypeSourceMe, selfShardID: args.SelfShardID, - txGasHandler: args.TxGasHandler, + host: mempoolHost, } return shardedTxPoolObject, nil @@ -134,7 +142,7 @@ func (txPool *shardedTxPool) createTxCache(cacheID string) txCache { if isForSenderMe { config := txPool.configPrototypeSourceMe config.Name = cacheID - cache, err := txcache.NewTxCache(config, txPool.txGasHandler) + cache, err := txcache.NewTxCache(config, txPool.host) if err != nil { log.Error("shardedTxPool.createTxCache()", "err", err) return txcache.NewDisabledCache() @@ -188,6 +196,7 @@ func (txPool *shardedTxPool) AddData(key []byte, value interface{}, sizeInBytes func (txPool *shardedTxPool) addTx(tx *txcache.WrappedTransaction, cacheID string) { shard := txPool.getOrCreateShard(cacheID) cache := shard.Cache + _, added := cache.AddTx(tx) if added { txPool.onAdded(tx.TxHash, tx) @@ -228,13 +237,8 @@ func (txPool *shardedTxPool) searchFirstTx(txHash []byte) (tx data.TransactionHa // RemoveData removes the transaction from the pool func (txPool *shardedTxPool) RemoveData(key []byte, cacheID string) { - txPool.removeTx(key, cacheID) -} - -// removeTx removes the transaction from the pool -func (txPool *shardedTxPool) removeTx(txHash []byte, cacheID string) bool { shard := txPool.getOrCreateShard(cacheID) - return shard.Cache.RemoveTxByHash(txHash) + _ = shard.Cache.RemoveTxByHash(key) } // RemoveSetOfDataFromPool removes a bunch of transactions from the pool @@ -244,14 +248,27 @@ func (txPool *shardedTxPool) RemoveSetOfDataFromPool(keys [][]byte, cacheID stri // removeTxBulk removes a bunch of transactions from the pool func (txPool *shardedTxPool) removeTxBulk(txHashes [][]byte, cacheID string) { + shard := txPool.getOrCreateShard(cacheID) + + stopWatch := core.NewStopWatch() + stopWatch.Start("removal") + numRemoved := 0 for _, key := range txHashes { - if txPool.removeTx(key, cacheID) { + if shard.Cache.RemoveTxByHash(key) { numRemoved++ } } - log.Trace("shardedTxPool.removeTxBulk()", "name", cacheID, "numToRemove", len(txHashes), "numRemoved", numRemoved) + stopWatch.Stop("removal") + + // Transactions with lower / equal nonce are also removed, but the counter does not reflect that. + log.Debug("shardedTxPool.removeTxBulk", + "cacheID", cacheID, + "numToRemove", len(txHashes), + "numRemoved", numRemoved, + "duration", stopWatch.GetMeasurement("removal"), + ) } // RemoveDataFromAllShards removes the transaction from the pool (it searches in all shards) diff --git a/dataRetriever/txpool/shardedTxPool_test.go b/dataRetriever/txpool/shardedTxPool_test.go index 6fdaa4676ad..1b3ab585dc3 100644 --- a/dataRetriever/txpool/shardedTxPool_test.go +++ b/dataRetriever/txpool/shardedTxPool_test.go @@ -10,6 +10,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" @@ -33,11 +34,8 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { SizeInBytesPerSender: 40960, Shards: 16, }, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 1000000000, - GasProcessingDivisor: 100, - }, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 1, } @@ -84,15 +82,11 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { require.Errorf(t, err, dataRetriever.ErrNilTxGasHandler.Error()) args = goodArgs - args.TxGasHandler = &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 0, - GasProcessingDivisor: 1, - } + args.Marshalizer = nil pool, err = NewShardedTxPool(args) require.Nil(t, pool) require.NotNil(t, err) - require.Errorf(t, err, dataRetriever.ErrCacheConfigInvalidEconomics.Error()) + require.Errorf(t, err, dataRetriever.ErrNilMarshalizer.Error()) args = goodArgs args.NumberOfShards = 0 @@ -105,12 +99,9 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { func Test_NewShardedTxPool_ComputesCacheConfig(t *testing.T) { config := storageunit.CacheConfig{SizeInBytes: 419430400, SizeInBytesPerSender: 614400, Capacity: 600000, SizePerSender: 1000, Shards: 1} args := ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 1000000000, - GasProcessingDivisor: 1, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 2, } @@ -121,7 +112,6 @@ func Test_NewShardedTxPool_ComputesCacheConfig(t *testing.T) { require.Equal(t, 209715200, int(pool.configPrototypeSourceMe.NumBytesThreshold)) require.Equal(t, 614400, int(pool.configPrototypeSourceMe.NumBytesPerSenderThreshold)) require.Equal(t, 1000, int(pool.configPrototypeSourceMe.CountPerSenderThreshold)) - require.Equal(t, 100, int(pool.configPrototypeSourceMe.NumSendersToPreemptivelyEvict)) require.Equal(t, 300000, int(pool.configPrototypeSourceMe.CountThreshold)) require.Equal(t, 300000, int(pool.configPrototypeDestinationMe.MaxNumItems)) @@ -392,12 +382,9 @@ func Test_routeToCacheUnions(t *testing.T) { Shards: 1, } args := ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 4, SelfShardID: 42, } @@ -414,8 +401,9 @@ func Test_routeToCacheUnions(t *testing.T) { func createTx(sender string, nonce uint64) data.TransactionHandler { return &transaction.Transaction{ - SndAddr: []byte(sender), - Nonce: nonce, + SndAddr: []byte(sender), + Nonce: nonce, + GasLimit: 50000, } } @@ -435,12 +423,9 @@ func newTxPoolToTest() (dataRetriever.ShardedDataCacherNotifier, error) { Shards: 1, } args := ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 4, SelfShardID: 0, } diff --git a/docker/keygenerator/Dockerfile b/docker/keygenerator/Dockerfile index c66a732e629..574f9ea15e5 100644 --- a/docker/keygenerator/Dockerfile +++ b/docker/keygenerator/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.7 as builder +FROM golang:1.20.7 AS builder RUN apt-get update && apt-get install -y WORKDIR /go/mx-chain-go @@ -12,5 +12,4 @@ RUN go build FROM ubuntu:22.04 COPY --from=builder /go/mx-chain-go/cmd/keygenerator /go/mx-chain-go/cmd/keygenerator -WORKDIR /go/mx-chain-go/cmd/keygenerator/ -ENTRYPOINT ["./keygenerator"] +ENTRYPOINT ["/go/mx-chain-go/cmd/keygenerator/keygenerator"] diff --git a/docker/node/Dockerfile b/docker/node/Dockerfile index 2513f789dc8..205733030c1 100644 --- a/docker/node/Dockerfile +++ b/docker/node/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.7 as builder +FROM golang:1.20.7 AS builder RUN apt-get update && apt-get upgrade -y WORKDIR /go/mx-chain-go @@ -7,15 +7,24 @@ RUN go mod tidy # Multiversx node WORKDIR /go/mx-chain-go/cmd/node RUN go build -v -ldflags="-X main.appVersion=$(git describe --tags --long --dirty)" -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-v | sort -n | tail -n -1| awk -F '/' '{print$3}'| sed 's/ /@/g')/wasmer/libwasmer_linux_amd64.so /lib/libwasmer_linux_amd64.so -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-go | sort -n | tail -n -1| awk -F '/' '{print$3}'| sed 's/ /@/g')/wasmer2/libvmexeccapi.so /lib/libvmexeccapi.so + +RUN mkdir -p /lib_amd64 /lib_arm64 + +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-v | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer/libwasmer_linux_amd64.so /lib_amd64/ +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-go | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer2/libvmexeccapi.so /lib_amd64/ + +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-v | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer/libwasmer_linux_arm64_shim.so /lib_arm64/ +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-go | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer2/libvmexeccapi_arm.so /lib_arm64/ # ===== SECOND STAGE ====== FROM ubuntu:22.04 +ARG TARGETARCH RUN apt-get update && apt-get upgrade -y COPY --from=builder "/go/mx-chain-go/cmd/node/node" "/go/mx-chain-go/cmd/node/" -COPY --from=builder "/lib/libwasmer_linux_amd64.so" "/lib/libwasmer_linux_amd64.so" -COPY --from=builder "/lib/libvmexeccapi.so" "/lib/libvmexeccapi.so" + +# Copy architecture-specific files +COPY --from=builder "/lib_${TARGETARCH}/*" "/lib/" + WORKDIR /go/mx-chain-go/cmd/node/ EXPOSE 8080 ENTRYPOINT ["/go/mx-chain-go/cmd/node/node"] diff --git a/docker/seednode/Dockerfile b/docker/seednode/Dockerfile index 74cc250c047..0a88e94e2c0 100644 --- a/docker/seednode/Dockerfile +++ b/docker/seednode/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.7 as builder +FROM golang:1.20.7 AS builder RUN apt-get update && apt-get install -y WORKDIR /go/mx-chain-go diff --git a/docker/termui/Dockerfile b/docker/termui/Dockerfile index bcc670e3ce3..22b053b64d4 100644 --- a/docker/termui/Dockerfile +++ b/docker/termui/Dockerfile @@ -1,14 +1,21 @@ -FROM golang:1.20.7 as builder +FROM golang:1.20.7 AS builder RUN apt-get update && apt-get install -y WORKDIR /go/mx-chain-go COPY . . WORKDIR /go/mx-chain-go/cmd/termui RUN go build -v -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-v | sort -n | tail -n -1| awk -F '/' '{print$3}'| sed 's/ /@/g')/wasmer/libwasmer_linux_amd64.so /lib/libwasmer_linux_amd64.so +RUN mkdir -p /lib_amd64 /lib_arm64 +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-v | sort -n | tail -n -1| awk -F '/' '{print$3}'| sed 's/ /@/g')/wasmer/libwasmer_linux_amd64.so /lib_amd64/ +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /go/mx-chain-go/go.mod | grep mx-chain-vm-v | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer/libwasmer_linux_arm64_shim.so /lib_arm64/ + # ===== SECOND STAGE ====== FROM ubuntu:22.04 +ARG TARGETARCH COPY --from=builder /go/mx-chain-go/cmd/termui /go/mx-chain-go/cmd/termui -COPY --from=builder "/lib/libwasmer_linux_amd64.so" "/lib/libwasmer_linux_amd64.so" + +# Copy architecture-specific files +COPY --from=builder "/lib_${TARGETARCH}/*" "/lib/" + WORKDIR /go/mx-chain-go/cmd/termui/ ENTRYPOINT ["./termui"] diff --git a/epochStart/bootstrap/epochStartMetaBlockProcessor.go b/epochStart/bootstrap/epochStartMetaBlockProcessor.go index 8ee40232287..93b993e81cc 100644 --- a/epochStart/bootstrap/epochStartMetaBlockProcessor.go +++ b/epochStart/bootstrap/epochStartMetaBlockProcessor.go @@ -264,7 +264,7 @@ func (e *epochStartMetaBlockProcessor) waitForConfMetaBlock(ctx context.Context, return epochStart.ErrNilMetaBlock } - err := e.requestConfirmationMetaBlock(metaBlock.GetNonce()) + err := e.requestConfirmationMetaBlock(metaBlock.GetNonce() + 1) if err != nil { return err } @@ -278,7 +278,7 @@ func (e *epochStartMetaBlockProcessor) waitForConfMetaBlock(ctx context.Context, case <-ctx.Done(): return epochStart.ErrTimeoutWaitingForMetaBlock case <-chanRequests: - err = e.requestConfirmationMetaBlock(metaBlock.GetNonce()) + err = e.requestConfirmationMetaBlock(metaBlock.GetNonce() + 1) if err != nil { return err } diff --git a/epochStart/bootstrap/process_test.go b/epochStart/bootstrap/process_test.go index e38737a7a3e..52c04a2f4c8 100644 --- a/epochStart/bootstrap/process_test.go +++ b/epochStart/bootstrap/process_test.go @@ -234,7 +234,7 @@ func createMockEpochStartBootstrapArgs( RoundHandler: &mock.RoundHandlerStub{}, LatestStorageDataProvider: &mock.LatestStorageDataProviderStub{}, StorageUnitOpener: &storageMocks.UnitOpenerStub{}, - ArgumentsParser: &mock.ArgumentParserMock{}, + ArgumentsParser: &testscommon.ArgumentParserMock{}, StatusHandler: &statusHandlerMock.AppStatusHandlerStub{}, HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, DataSyncerCreator: &scheduledDataSyncer.ScheduledSyncerFactoryStub{ @@ -833,16 +833,18 @@ func TestEpochStartBootstrap_BootstrapStartInEpochNotEnabled(t *testing.T) { coreComp, cryptoComp := createComponentsForEpochStart() args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) - err := errors.New("localErr") + localErr := errors.New("localErr") args.LatestStorageDataProvider = &mock.LatestStorageDataProviderStub{ GetCalled: func() (storage.LatestDataFromStorage, error) { - return storage.LatestDataFromStorage{}, err + return storage.LatestDataFromStorage{}, localErr }, } - epochStartProvider, _ := NewEpochStartBootstrap(args) + + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.NoError(t, err) params, err := epochStartProvider.Bootstrap() - assert.Nil(t, err) + assert.NoError(t, err) assert.NotNil(t, params) } @@ -883,7 +885,8 @@ func TestPrepareForEpochZero(t *testing.T) { coreComp, cryptoComp := createComponentsForEpochStart() args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) - epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.Nil(t, err) params, err := epochStartProvider.prepareEpochZero() assert.Nil(t, err) @@ -919,7 +922,8 @@ func TestPrepareForEpochZero_NodeInGenesisShouldNotAlterShardID(t *testing.T) { }, } - epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.NoError(t, err) params, err := epochStartProvider.prepareEpochZero() assert.NoError(t, err) @@ -954,7 +958,8 @@ func TestPrepareForEpochZero_NodeNotInGenesisShouldAlterShardID(t *testing.T) { }, } - epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.NoError(t, err) params, err := epochStartProvider.prepareEpochZero() assert.NoError(t, err) diff --git a/facade/initial/initialNodeFacade.go b/facade/initial/initialNodeFacade.go index 7411a2078e9..d6043dbcd62 100644 --- a/facade/initial/initialNodeFacade.go +++ b/facade/initial/initialNodeFacade.go @@ -421,6 +421,11 @@ func (inf *initialNodeFacade) IsDataTrieMigrated(_ string, _ api.AccountQueryOpt return false, errNodeStarting } +// GetSCRsByTxHash return a nil slice and error +func (inf *initialNodeFacade) GetSCRsByTxHash(_ string, _ string) ([]*transaction.ApiSmartContractResult, error) { + return nil, errNodeStarting +} + // GetManagedKeysCount returns 0 func (inf *initialNodeFacade) GetManagedKeysCount() int { return 0 diff --git a/facade/interface.go b/facade/interface.go index 35f185874ed..309f6c98d6f 100644 --- a/facade/interface.go +++ b/facade/interface.go @@ -127,6 +127,7 @@ type ApiResolver interface { GetDirectStakedList(ctx context.Context) ([]*api.DirectStakedValue, error) GetDelegatorsList(ctx context.Context) ([]*api.Delegator, error) GetTransaction(hash string, withResults bool) (*transaction.ApiTransactionResult, error) + GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) GetTransactionsPool(fields string) (*common.TransactionsPoolAPIResponse, error) GetTransactionsPoolForSender(sender, fields string) (*common.TransactionsPoolForSenderApiResponse, error) GetLastPoolNonceForSender(sender string) (uint64, error) diff --git a/facade/mock/apiResolverStub.go b/facade/mock/apiResolverStub.go index 33bae8518aa..8e38ab6707d 100644 --- a/facade/mock/apiResolverStub.go +++ b/facade/mock/apiResolverStub.go @@ -50,6 +50,16 @@ type ApiResolverStub struct { GetEligibleManagedKeysCalled func() ([]string, error) GetWaitingManagedKeysCalled func() ([]string, error) GetWaitingEpochsLeftForPublicKeyCalled func(publicKey string) (uint32, error) + GetSCRsByTxHashCalled func(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) +} + +// GetSCRsByTxHash - +func (ars *ApiResolverStub) GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) { + if ars.GetSCRsByTxHashCalled != nil { + return ars.GetSCRsByTxHashCalled(txHash, scrHash) + } + + return nil, nil } // GetTransaction - diff --git a/facade/nodeFacade.go b/facade/nodeFacade.go index 8bc696b6adc..c3a7f290edf 100644 --- a/facade/nodeFacade.go +++ b/facade/nodeFacade.go @@ -304,6 +304,11 @@ func (nf *nodeFacade) GetTransaction(hash string, withResults bool) (*transactio return nf.apiResolver.GetTransaction(hash, withResults) } +// GetSCRsByTxHash will return a list of smart contract results based on a provided tx hash and smart contract result hash +func (nf *nodeFacade) GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) { + return nf.apiResolver.GetSCRsByTxHash(txHash, scrHash) +} + // GetTransactionsPool will return a structure containing the transactions pool that is to be returned on API calls func (nf *nodeFacade) GetTransactionsPool(fields string) (*common.TransactionsPoolAPIResponse, error) { return nf.apiResolver.GetTransactionsPool(fields) diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index dfefa56ff94..399ee4b1533 100644 --- a/factory/api/apiResolverFactory.go +++ b/factory/api/apiResolverFactory.go @@ -244,6 +244,8 @@ func CreateApiResolver(args *ApiResolverArgs) (facade.ApiResolver, error) { TxTypeHandler: txTypeHandler, LogsFacade: logsFacade, DataFieldParser: dataFieldParser, + TxMarshaller: args.CoreComponents.TxMarshalizer(), + EnableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), } apiTransactionProcessor, err := transactionAPI.NewAPITransactionProcessor(argsAPITransactionProc) if err != nil { diff --git a/factory/bootstrap/bootstrapComponents.go b/factory/bootstrap/bootstrapComponents.go index af6dc1aafa3..2154289a4c7 100644 --- a/factory/bootstrap/bootstrapComponents.go +++ b/factory/bootstrap/bootstrapComponents.go @@ -3,9 +3,11 @@ package bootstrap import ( "fmt" "path/filepath" + "time" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" + interceptorFactory "github.com/multiversx/mx-chain-go/process/interceptors/factory" logger "github.com/multiversx/mx-chain-logger-go" nodeFactory "github.com/multiversx/mx-chain-go/cmd/node/factory" @@ -200,6 +202,12 @@ func (bcf *bootstrapComponentsFactory) Create() (*bootstrapComponents, error) { return nil, err } + // create a new instance of interceptedDataVerifier which will be used for bootstrap only + interceptedDataVerifierFactory := interceptorFactory.NewInterceptedDataVerifierFactory(interceptorFactory.InterceptedDataVerifierFactoryArgs{ + CacheSpan: time.Duration(bcf.config.InterceptedDataVerifier.CacheSpanInSec) * time.Second, + CacheExpiry: time.Duration(bcf.config.InterceptedDataVerifier.CacheExpiryInSec) * time.Second, + }) + epochStartBootstrapArgs := bootstrap.ArgsEpochStartBootstrap{ CoreComponentsHolder: bcf.coreComponents, CryptoComponentsHolder: bcf.cryptoComponents, @@ -227,6 +235,7 @@ func (bcf *bootstrapComponentsFactory) Create() (*bootstrapComponents, error) { StateStatsHandler: bcf.statusCoreComponents.StateStatsHandler(), NodesCoordinatorRegistryFactory: nodesCoordinatorRegistryFactory, EnableEpochsHandler: bcf.coreComponents.EnableEpochsHandler(), + InterceptedDataVerifierFactory: interceptedDataVerifierFactory, } var epochStartBootstrapper factory.EpochStartBootstrapper diff --git a/factory/disabled/txCoordinator.go b/factory/disabled/txCoordinator.go index 9d8002fb034..a5d0dfe330a 100644 --- a/factory/disabled/txCoordinator.go +++ b/factory/disabled/txCoordinator.go @@ -25,8 +25,8 @@ func (txCoordinator *TxCoordinator) CreateReceiptsHash() ([]byte, error) { } // ComputeTransactionType does nothing as it is disabled -func (txCoordinator *TxCoordinator) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return 0, 0 +func (txCoordinator *TxCoordinator) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return 0, 0, false } // RequestMiniBlocksAndTransactions does nothing as it is disabled diff --git a/factory/disabled/txProcessor.go b/factory/disabled/txProcessor.go index 950add6c732..16df5db2d89 100644 --- a/factory/disabled/txProcessor.go +++ b/factory/disabled/txProcessor.go @@ -2,6 +2,7 @@ package disabled import ( "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -19,6 +20,11 @@ func (txProc *TxProcessor) VerifyTransaction(_ *transaction.Transaction) error { return nil } +// VerifyGuardian does nothing as it is disabled +func (txProc *TxProcessor) VerifyGuardian(_ *transaction.Transaction, _ state.UserAccountHandler) error { + return nil +} + // IsInterfaceNil returns true if there is no value under the interface func (txProc *TxProcessor) IsInterfaceNil() bool { return txProc == nil diff --git a/factory/processing/blockProcessorCreator.go b/factory/processing/blockProcessorCreator.go index 4c952f4bb25..0721efc6a23 100644 --- a/factory/processing/blockProcessorCreator.go +++ b/factory/processing/blockProcessorCreator.go @@ -174,6 +174,11 @@ func (pcf *processComponentsFactory) newShardBlockProcessor( return nil, err } + err = builtInFuncFactory.SetBlockchainHook(vmFactory.BlockChainHookImpl()) + if err != nil { + return nil, err + } + argsFactory := shard.ArgsNewIntermediateProcessorsContainerFactory{ ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), Marshalizer: pcf.coreData.InternalMarshalizer(), diff --git a/factory/processing/blockProcessorCreator_test.go b/factory/processing/blockProcessorCreator_test.go index 8b01c44c8f8..f56cc9a0d24 100644 --- a/factory/processing/blockProcessorCreator_test.go +++ b/factory/processing/blockProcessorCreator_test.go @@ -163,7 +163,8 @@ func Test_newBlockProcessorCreatorForMeta(t *testing.T) { componentsMock.SetShardCoordinator(t, args.BootstrapComponents, shardC) - pcf, _ := processComp.NewProcessComponentsFactory(args) + pcf, err := processComp.NewProcessComponentsFactory(args) + require.NoError(t, err) require.NotNil(t, pcf) _, err = pcf.Create() diff --git a/factory/processing/processComponents.go b/factory/processing/processComponents.go index 9fd1d330d19..bfd985b4074 100644 --- a/factory/processing/processComponents.go +++ b/factory/processing/processComponents.go @@ -296,6 +296,7 @@ func (pcf *processComponentsFactory) Create() (*processComponents, error) { FallbackHeaderValidator: fallbackHeaderValidator, EnableEpochsHandler: pcf.coreData.EnableEpochsHandler(), HeadersPool: pcf.data.Datapool().Headers(), + StorageService: pcf.data.StorageService(), } headerSigVerifier, err := headerCheck.NewHeaderSigVerifier(argsHeaderSig) if err != nil { diff --git a/factory/processing/txSimulatorProcessComponents_test.go b/factory/processing/txSimulatorProcessComponents_test.go index aad848600d8..e6fd1f90f6a 100644 --- a/factory/processing/txSimulatorProcessComponents_test.go +++ b/factory/processing/txSimulatorProcessComponents_test.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-go/process/mock" "github.com/multiversx/mx-chain-go/testscommon/components" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestManagedProcessComponents_createAPITransactionEvaluator(t *testing.T) { @@ -24,30 +25,35 @@ func TestManagedProcessComponents_createAPITransactionEvaluator(t *testing.T) { t.Run("invalid VMOutputCacher config should error", func(t *testing.T) { processArgs := components.GetProcessComponentsFactoryArgs(shardCoordinatorForShardID2) processArgs.Config.VMOutputCacher.Type = "invalid" - pcf, _ := processing.NewProcessComponentsFactory(processArgs) + pcf, err := processing.NewProcessComponentsFactory(processArgs) + require.Nil(t, err) apiTransactionEvaluator, vmContainerFactory, err := pcf.CreateAPITransactionEvaluator() - assert.NotNil(t, err) + require.NotNil(t, err) assert.True(t, check.IfNil(apiTransactionEvaluator)) assert.True(t, check.IfNil(vmContainerFactory)) assert.Contains(t, err.Error(), "not supported cache type") }) t.Run("should work for shard", func(t *testing.T) { processArgs := components.GetProcessComponentsFactoryArgs(shardCoordinatorForShardID2) - pcf, _ := processing.NewProcessComponentsFactory(processArgs) + pcf, err := processing.NewProcessComponentsFactory(processArgs) + require.Nil(t, err) apiTransactionEvaluator, vmContainerFactory, err := pcf.CreateAPITransactionEvaluator() - assert.Nil(t, err) + require.Nil(t, err) assert.False(t, check.IfNil(apiTransactionEvaluator)) assert.False(t, check.IfNil(vmContainerFactory)) + require.NoError(t, vmContainerFactory.Close()) }) t.Run("should work for metachain", func(t *testing.T) { processArgs := components.GetProcessComponentsFactoryArgs(shardCoordinatorForMetachain) - pcf, _ := processing.NewProcessComponentsFactory(processArgs) + pcf, err := processing.NewProcessComponentsFactory(processArgs) + require.Nil(t, err) apiTransactionEvaluator, vmContainerFactory, err := pcf.CreateAPITransactionEvaluator() - assert.Nil(t, err) + require.Nil(t, err) assert.False(t, check.IfNil(apiTransactionEvaluator)) assert.False(t, check.IfNil(vmContainerFactory)) + require.NoError(t, vmContainerFactory.Close()) }) } diff --git a/genesis/mock/coreComponentsMock.go b/genesis/mock/coreComponentsMock.go index fb0907ef8a0..e44dd801243 100644 --- a/genesis/mock/coreComponentsMock.go +++ b/genesis/mock/coreComponentsMock.go @@ -22,6 +22,12 @@ type CoreComponentsMock struct { StatHandler core.AppStatusHandler EnableEpochsHandlerField common.EnableEpochsHandler TxVersionCheck process.TxVersionCheckerHandler + EconomicsDataField process.EconomicsDataHandler +} + +// EconomicsData - +func (ccm *CoreComponentsMock) EconomicsData() process.EconomicsDataHandler { + return ccm.EconomicsDataField } // InternalMarshalizer - diff --git a/genesis/process/argGenesisBlockCreator.go b/genesis/process/argGenesisBlockCreator.go index 19b5fc9adcc..685e356f31b 100644 --- a/genesis/process/argGenesisBlockCreator.go +++ b/genesis/process/argGenesisBlockCreator.go @@ -29,6 +29,7 @@ type coreComponentsHandler interface { TxVersionChecker() process.TxVersionCheckerHandler ChainID() string EnableEpochsHandler() common.EnableEpochsHandler + EconomicsData() process.EconomicsDataHandler IsInterfaceNil() bool } diff --git a/genesis/process/disabled/feeHandler.go b/genesis/process/disabled/feeHandler.go index 1fc34bbc2b5..0b3051cf845 100644 --- a/genesis/process/disabled/feeHandler.go +++ b/genesis/process/disabled/feeHandler.go @@ -12,6 +12,11 @@ import ( type FeeHandler struct { } +// ComputeGasUnitsFromRefundValue return 0 +func (fh *FeeHandler) ComputeGasUnitsFromRefundValue(_ data.TransactionWithFeeHandler, _ *big.Int, _ uint32) uint64 { + return 0 +} + // GasPriceModifier returns 1.0 func (fh *FeeHandler) GasPriceModifier() float64 { return 1.0 diff --git a/genesis/process/genesisBlockCreator_test.go b/genesis/process/genesisBlockCreator_test.go index b7b788f0d37..a681a0e271c 100644 --- a/genesis/process/genesisBlockCreator_test.go +++ b/genesis/process/genesisBlockCreator_test.go @@ -76,6 +76,7 @@ func createMockArgument( TxVersionCheck: &testscommon.TxVersionCheckerStub{}, MinTxVersion: 1, EnableEpochsHandlerField: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, + EconomicsDataField: &economicsmocks.EconomicsHandlerMock{}, }, Data: &mock.DataComponentsMock{ Storage: &storageCommon.ChainStorerStub{ @@ -307,7 +308,8 @@ func TestNewGenesisBlockCreator(t *testing.T) { arg := createMockArgument(t, "testdata/genesisTest1.json", &mock.InitialNodesHandlerStub{}, big.NewInt(22000)) arg.Core = &mock.CoreComponentsMock{ - AddrPubKeyConv: nil, + AddrPubKeyConv: nil, + EconomicsDataField: &economicsmocks.EconomicsHandlerMock{}, } gbc, err := NewGenesisBlockCreator(arg) diff --git a/go.mod b/go.mod index 4048536d947..a0736593c96 100644 --- a/go.mod +++ b/go.mod @@ -14,18 +14,18 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/klauspost/cpuid/v2 v2.2.5 github.com/mitchellh/mapstructure v1.5.0 - github.com/multiversx/mx-chain-communication-go v1.0.15-0.20240508074652-e128a1c05c8e - github.com/multiversx/mx-chain-core-go v1.2.21-0.20250122100317-798de8d35458 - github.com/multiversx/mx-chain-crypto-go v1.2.12-0.20240508074452-cc21c1b505df - github.com/multiversx/mx-chain-es-indexer-go v1.7.2-0.20240619122842-05143459c554 - github.com/multiversx/mx-chain-logger-go v1.0.15-0.20240508072523-3f00a726af57 - github.com/multiversx/mx-chain-scenario-go v1.4.4-0.20240509103754-9e8129721f00 - github.com/multiversx/mx-chain-storage-go v1.0.16-0.20240508073549-dcb8e6e0370f - github.com/multiversx/mx-chain-vm-common-go v1.5.13-0.20240619122724-2bd2e64cebdc - github.com/multiversx/mx-chain-vm-go v1.5.30-0.20240509104139-8b0eaa8a85d1 - github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68-0.20240509103859-89de3c5da36b - github.com/multiversx/mx-chain-vm-v1_3-go v1.3.69-0.20240509104009-598a37ff36b9 - github.com/multiversx/mx-chain-vm-v1_4-go v1.4.98-0.20240509104102-2a6a709b4041 + github.com/multiversx/mx-chain-communication-go v1.1.1 + github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128131118-6d145bb2a7b4 + github.com/multiversx/mx-chain-crypto-go v1.2.12 + github.com/multiversx/mx-chain-es-indexer-go v1.7.14 + github.com/multiversx/mx-chain-logger-go v1.0.15 + github.com/multiversx/mx-chain-scenario-go v1.4.4 + github.com/multiversx/mx-chain-storage-go v1.0.19 + github.com/multiversx/mx-chain-vm-common-go v1.5.16 + github.com/multiversx/mx-chain-vm-go v1.5.37 + github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68 + github.com/multiversx/mx-chain-vm-v1_3-go v1.3.69 + github.com/multiversx/mx-chain-vm-v1_4-go v1.4.98 github.com/pelletier/go-toml v1.9.3 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 diff --git a/go.sum b/go.sum index d7014c13691..5382a93a886 100644 --- a/go.sum +++ b/go.sum @@ -385,30 +385,30 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUYwbO0993uPI= github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= -github.com/multiversx/mx-chain-communication-go v1.0.15-0.20240508074652-e128a1c05c8e h1:Tsmwhu+UleE+l3buPuqXSKTqfu5FbPmzQ4MjMoUvCWA= -github.com/multiversx/mx-chain-communication-go v1.0.15-0.20240508074652-e128a1c05c8e/go.mod h1:2yXl18wUbuV3cRZr7VHxM1xo73kTaC1WUcu2kx8R034= -github.com/multiversx/mx-chain-core-go v1.2.21-0.20250122100317-798de8d35458 h1:lMzqNUv+UbtcvwL98uf52z0hfXlA/ASrWAKAh8lyt0E= -github.com/multiversx/mx-chain-core-go v1.2.21-0.20250122100317-798de8d35458/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= -github.com/multiversx/mx-chain-crypto-go v1.2.12-0.20240508074452-cc21c1b505df h1:clihfi78bMEOWk/qw6WA4uQbCM2e2NGliqswLAvw19k= -github.com/multiversx/mx-chain-crypto-go v1.2.12-0.20240508074452-cc21c1b505df/go.mod h1:gtJYB4rR21KBSqJlazn+2z6f9gFSqQP3KvAgL7Qgxw4= -github.com/multiversx/mx-chain-es-indexer-go v1.7.2-0.20240619122842-05143459c554 h1:Fv8BfzJSzdovmoh9Jh/by++0uGsOVBlMP3XiN5Svkn4= -github.com/multiversx/mx-chain-es-indexer-go v1.7.2-0.20240619122842-05143459c554/go.mod h1:yMq9q5VdN7jBaErRGQ0T8dkZwbBtfQYmqGbD/Ese1us= -github.com/multiversx/mx-chain-logger-go v1.0.15-0.20240508072523-3f00a726af57 h1:g9t410dqjcb7UUptbVd/H6Ua12sEzWU4v7VplyNvRZ0= -github.com/multiversx/mx-chain-logger-go v1.0.15-0.20240508072523-3f00a726af57/go.mod h1:cY6CIXpndW5g5PTPn4WzPwka/UBEf+mgw+PSY5pHGAU= -github.com/multiversx/mx-chain-scenario-go v1.4.4-0.20240509103754-9e8129721f00 h1:hFEcbGBtXu8UyB9BMhmAIH2R8BtV/NOq/rsxespLCN8= -github.com/multiversx/mx-chain-scenario-go v1.4.4-0.20240509103754-9e8129721f00/go.mod h1:pnIIfWopbDMQ1EW5Ddc6KDMqv8Qtx+hxbH9rorHpCyo= -github.com/multiversx/mx-chain-storage-go v1.0.16-0.20240508073549-dcb8e6e0370f h1:yd/G8iPBGOEAwbaS8zndJpO6bQk7Tk72ZhmlqRasThI= -github.com/multiversx/mx-chain-storage-go v1.0.16-0.20240508073549-dcb8e6e0370f/go.mod h1:E6nfj9EQzGxWDGM3Dn6eZWRC3qFy1G8IqOsYsBOcgWw= -github.com/multiversx/mx-chain-vm-common-go v1.5.13-0.20240619122724-2bd2e64cebdc h1:KpLloX0pIclo3axCQVOm3wZE+U9cfeHgPWGvDuUohTk= -github.com/multiversx/mx-chain-vm-common-go v1.5.13-0.20240619122724-2bd2e64cebdc/go.mod h1:RgGmPei0suQcFTHfO4cS5dxJSiokp2SM5lmNgp1icMo= -github.com/multiversx/mx-chain-vm-go v1.5.30-0.20240509104139-8b0eaa8a85d1 h1:5/h1i7Xd/JH9CiO3ZqvzAjdze+mAbar5sWkh2UqfLgI= -github.com/multiversx/mx-chain-vm-go v1.5.30-0.20240509104139-8b0eaa8a85d1/go.mod h1:N3Oa8QeeHlSip4nbESQpVSLgi/WxtgIwvqfXIZm6gDs= -github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68-0.20240509103859-89de3c5da36b h1:puYO0lUyPGA5kZqsiDjGa+daDGQwj9xFs0S5urhZjU8= -github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68-0.20240509103859-89de3c5da36b/go.mod h1:SY95hGdAIc8YCGb4uNSy1ux8V8qQbF1ReZJDwQ6AqEo= -github.com/multiversx/mx-chain-vm-v1_3-go v1.3.69-0.20240509104009-598a37ff36b9 h1:rrkgAS58jRXc6LThPHY5fm3AnFoUa0VUiYkH5czdlYg= -github.com/multiversx/mx-chain-vm-v1_3-go v1.3.69-0.20240509104009-598a37ff36b9/go.mod h1:TiOTsz2kxHadU0It7okOwcynyNPePXzjyl7lnpGLlUQ= -github.com/multiversx/mx-chain-vm-v1_4-go v1.4.98-0.20240509104102-2a6a709b4041 h1:k0xkmCrJiQzsWk4ZM3oNQ31lheiDvd1qQnNwnyuZzXU= -github.com/multiversx/mx-chain-vm-v1_4-go v1.4.98-0.20240509104102-2a6a709b4041/go.mod h1:XeZNaDMV0hbDlm3JtW0Hj3mCWKaB/XecQlCzEjiK5L8= +github.com/multiversx/mx-chain-communication-go v1.1.1 h1:y4DoQeQOJTaSUsRzczQFazf8JYQmInddypApqA3AkwM= +github.com/multiversx/mx-chain-communication-go v1.1.1/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128131118-6d145bb2a7b4 h1:GInmVgiTE3vRGL8V8XK7+RQJ1renroYNEuejxkxZcBA= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128131118-6d145bb2a7b4/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= +github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= +github.com/multiversx/mx-chain-es-indexer-go v1.7.14 h1:V4fuubEUYskWCLQIkbuoB0WHoKyldLQRq/fllIzW1CU= +github.com/multiversx/mx-chain-es-indexer-go v1.7.14/go.mod h1:5Sr49FjWWzZ3/WcC3jzln8TlMSNToCIT9Lqy6P7i7bs= +github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+wRqOwi3n+m2QIHXc= +github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= +github.com/multiversx/mx-chain-scenario-go v1.4.4 h1:DVE2V+FPeyD/yWoC+KEfPK3jsFzHeruelESfpTlf460= +github.com/multiversx/mx-chain-scenario-go v1.4.4/go.mod h1:kI+TWR3oIEgUkbwkHCPo2CQ3VjIge+ezGTibiSGwMxo= +github.com/multiversx/mx-chain-storage-go v1.0.19 h1:2R35MoSXcuNJOFmV5xEhcXqiEGZw6AYGy9R8J9KH66Q= +github.com/multiversx/mx-chain-storage-go v1.0.19/go.mod h1:Pb/BuVmiFqO66DSZO16KFkSUeom94x3e3Q9IloBvkYI= +github.com/multiversx/mx-chain-vm-common-go v1.5.16 h1:g1SqYjxl7K66Y1O/q6tvDJ37fzpzlxCSfRzSm/woQQY= +github.com/multiversx/mx-chain-vm-common-go v1.5.16/go.mod h1:1rSkXreUZNXyPTTdhj47M+Fy62yjxbu3aAsXEtKN3UY= +github.com/multiversx/mx-chain-vm-go v1.5.37 h1:Iy3KCvM+DOq1f9UPA7uYK/rI3ZbBOXc2CVNO2/vm5zw= +github.com/multiversx/mx-chain-vm-go v1.5.37/go.mod h1:nzLrWeXvfxCIiwj5uNBZq3d7stkXyeY+Fktfr4tTaiY= +github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68 h1:L3GoAVFtLLzr9ya0rVv1YdTUzS3MyM7kQNBSAjCNO2g= +github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68/go.mod h1:ixxwib+1pXwSDHG5Wa34v0SRScF+BwFzH4wFWY31saI= +github.com/multiversx/mx-chain-vm-v1_3-go v1.3.69 h1:G/PLsyfQV4bMLs2amGRvaLKZoW1DC7M+7ecVaLuaCNc= +github.com/multiversx/mx-chain-vm-v1_3-go v1.3.69/go.mod h1:msY3zaS+K+R10ypqQs/jke6xdNAJzS38PGIaeJj2zhg= +github.com/multiversx/mx-chain-vm-v1_4-go v1.4.98 h1:/fYx4ClVPU48pTKh2qk4QVlve0xjjDpvzOakjFUtXJ8= +github.com/multiversx/mx-chain-vm-v1_4-go v1.4.98/go.mod h1:4vqG8bSmufZx263DMrmr8OLbO6q6//VPC4W9PxZLB5Q= github.com/multiversx/mx-components-big-int v1.0.0 h1:Wkr8lSzK2nDqixOrrBa47VNuqdhV1m/aJhaP1EMaiS8= github.com/multiversx/mx-components-big-int v1.0.0/go.mod h1:maIEMgHlNE2u78JaDD0oLzri+ShgU4okHfzP3LWGdQM= github.com/multiversx/protobuf v1.3.2 h1:RaNkxvGTGbA0lMcnHAN24qE1G1i+Xs5yHA6MDvQ4mSM= diff --git a/integrationTests/chainSimulator/mempool/mempool_test.go b/integrationTests/chainSimulator/mempool/mempool_test.go new file mode 100644 index 00000000000..704be8e40fb --- /dev/null +++ b/integrationTests/chainSimulator/mempool/mempool_test.go @@ -0,0 +1,331 @@ +package mempool + +import ( + "math/big" + "testing" + "time" + + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" + "github.com/multiversx/mx-chain-go/storage" + "github.com/stretchr/testify/require" +) + +func TestMempoolWithChainSimulator_Selection(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + numSenders := 10000 + numTransactionsPerSender := 3 + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + participants := createParticipants(t, simulator, numSenders) + noncesTracker := newNoncesTracker() + + transactions := make([]*transaction.Transaction, 0, numSenders*numTransactionsPerSender) + + for i := 0; i < numSenders; i++ { + sender := participants.sendersByShard[shard][i] + receiver := participants.receiverByShard[shard] + + for j := 0; j < numTransactionsPerSender; j++ { + tx := &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(sender), + Value: oneQuarterOfEGLD, + SndAddr: sender.Bytes, + RcvAddr: receiver.Bytes, + Data: []byte{}, + GasLimit: 50_000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + } + + transactions = append(transactions, tx) + } + } + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendMany) + require.Equal(t, 30_000, getNumTransactionsInPool(simulator, shard)) + + selectedTransactions, gas := selectTransactions(t, simulator, shard) + require.Equal(t, 30_000, len(selectedTransactions)) + require.Equal(t, 50_000*30_000, int(gas)) + + err := simulator.GenerateBlocks(1) + require.Nil(t, err) + require.Equal(t, 27_756, getNumTransactionsInCurrentBlock(simulator, shard)) + + selectedTransactions, gas = selectTransactions(t, simulator, shard) + require.Equal(t, 30_000-27_756, len(selectedTransactions)) + require.Equal(t, 50_000*(30_000-27_756), int(gas)) +} + +func TestMempoolWithChainSimulator_Selection_WhenUsersHaveZeroBalance_WithRelayedV3(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + err := simulator.GenerateBlocksUntilEpochIsReached(2) + require.NoError(t, err) + + relayer, err := simulator.GenerateAndMintWalletAddress(uint32(shard), oneEGLD) + require.NoError(t, err) + + receiver, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + alice, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + bob, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + err = simulator.GenerateBlocks(1) + require.Nil(t, err) + + noncesTracker := newNoncesTracker() + transactions := make([]*transaction.Transaction, 0) + + // Transfer (executable, invalid) from Alice (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(alice), + Value: oneQuarterOfEGLD, + SndAddr: alice.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_002, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + // Contract call from Bob (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(bob), + Value: big.NewInt(0), + SndAddr: bob.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte("hello"), + GasLimit: 100_000 + 5*1500, + GasPrice: 1_000_000_001, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendSome) + require.Equal(t, 2, getNumTransactionsInPool(simulator, shard)) + + selectedTransactions, _ := selectTransactions(t, simulator, shard) + require.Equal(t, 2, len(selectedTransactions)) + require.Equal(t, alice.Bytes, selectedTransactions[0].Tx.GetSndAddr()) + require.Equal(t, bob.Bytes, selectedTransactions[1].Tx.GetSndAddr()) + + err = simulator.GenerateBlocks(1) + require.Nil(t, err) + require.Equal(t, 2, getNumTransactionsInCurrentBlock(simulator, shard)) + + require.Equal(t, "invalid", getTransaction(t, simulator, shard, selectedTransactions[0].TxHash).Status.String()) + require.Equal(t, "success", getTransaction(t, simulator, shard, selectedTransactions[1].TxHash).Status.String()) +} + +func TestMempoolWithChainSimulator_Selection_WhenInsufficientBalanceForFee_WithRelayedV3(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + numSenders := 3 + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + err := simulator.GenerateBlocksUntilEpochIsReached(2) + require.NoError(t, err) + + participants := createParticipants(t, simulator, numSenders) + noncesTracker := newNoncesTracker() + + alice := participants.sendersByShard[shard][0] + bob := participants.sendersByShard[shard][1] + carol := participants.sendersByShard[shard][2] + relayer := participants.relayerByShard[shard] + receiver := participants.receiverByShard[shard] + + transactions := make([]*transaction.Transaction, 0) + + // Consume most of relayer's balance. Keep an amount that is enough for the fee of two simple transfer transactions. + currentRelayerBalance := int64(1000000000000000000) + feeForTransfer := int64(50_000 * 1_000_000_004) + feeForRelayingTransactionsOfAliceAndBob := int64(100_000*1_000_000_003 + 100_000*1_000_000_002) + + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(relayer), + Value: big.NewInt(currentRelayerBalance - feeForTransfer - feeForRelayingTransactionsOfAliceAndBob), + SndAddr: relayer.Bytes, + RcvAddr: receiver.Bytes, + Data: []byte{}, + GasLimit: 50_000, + GasPrice: 1_000_000_004, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + }) + + // Transfer from Alice (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(alice), + Value: oneQuarterOfEGLD, + SndAddr: alice.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_003, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + // Transfer from Bob (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(bob), + Value: oneQuarterOfEGLD, + SndAddr: bob.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_002, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + // Transfer from Carol (relayed) - this one should not be selected due to insufficient balance (of the relayer) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(carol), + Value: oneQuarterOfEGLD, + SndAddr: carol.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_001, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendSome) + require.Equal(t, 4, getNumTransactionsInPool(simulator, shard)) + + selectedTransactions, _ := selectTransactions(t, simulator, shard) + require.Equal(t, 3, len(selectedTransactions)) + require.Equal(t, relayer.Bytes, selectedTransactions[0].Tx.GetSndAddr()) + require.Equal(t, alice.Bytes, selectedTransactions[1].Tx.GetSndAddr()) + require.Equal(t, bob.Bytes, selectedTransactions[2].Tx.GetSndAddr()) +} + +func TestMempoolWithChainSimulator_Eviction(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + numSenders := 10000 + numTransactionsPerSender := 30 + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + participants := createParticipants(t, simulator, numSenders) + noncesTracker := newNoncesTracker() + + transactions := make([]*transaction.Transaction, 0, numSenders*numTransactionsPerSender) + + for i := 0; i < numSenders; i++ { + sender := participants.sendersByShard[shard][i] + receiver := participants.receiverByShard[shard] + + for j := 0; j < numTransactionsPerSender; j++ { + tx := &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(sender), + Value: oneQuarterOfEGLD, + SndAddr: sender.Bytes, + RcvAddr: receiver.Bytes, + Data: []byte{}, + GasLimit: 50_000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + } + + transactions = append(transactions, tx) + } + } + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendMany) + require.Equal(t, 300_000, getNumTransactionsInPool(simulator, shard)) + + // Send one more transaction (fill up the mempool) + sendTransaction(t, simulator, &transaction.Transaction{ + Nonce: 42, + Value: oneEGLD, + SndAddr: participants.sendersByShard[shard][7].Bytes, + RcvAddr: participants.receiverByShard[shard].Bytes, + Data: []byte{}, + GasLimit: 50000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + }) + + time.Sleep(durationWaitAfterSendSome) + require.Equal(t, 300_001, getNumTransactionsInPool(simulator, shard)) + + // Send one more transaction to trigger eviction + sendTransaction(t, simulator, &transaction.Transaction{ + Nonce: 42, + Value: oneEGLD, + SndAddr: participants.sendersByShard[shard][7].Bytes, + RcvAddr: participants.receiverByShard[shard].Bytes, + Data: []byte{}, + GasLimit: 50000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + }) + + time.Sleep(2 * time.Second) + + expectedNumTransactionsInPool := 300_000 + 1 + 1 - int(storage.TxPoolSourceMeNumItemsToPreemptivelyEvict) + require.Equal(t, expectedNumTransactionsInPool, getNumTransactionsInPool(simulator, shard)) +} diff --git a/integrationTests/chainSimulator/mempool/testutils_test.go b/integrationTests/chainSimulator/mempool/testutils_test.go new file mode 100644 index 00000000000..3d4a0afd5f7 --- /dev/null +++ b/integrationTests/chainSimulator/mempool/testutils_test.go @@ -0,0 +1,193 @@ +package mempool + +import ( + "encoding/hex" + "math/big" + "strconv" + "testing" + "time" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/config" + testsChainSimulator "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" + "github.com/multiversx/mx-chain-go/node/chainSimulator" + "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" + "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/preprocess" + "github.com/multiversx/mx-chain-go/storage/txcache" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/require" +) + +var ( + oneEGLD = big.NewInt(1000000000000000000) + oneQuarterOfEGLD = big.NewInt(250000000000000000) + durationWaitAfterSendMany = 1500 * time.Millisecond + durationWaitAfterSendSome = 50 * time.Millisecond +) + +func startChainSimulator(t *testing.T, alterConfigsFunction func(cfg *config.Configs)) testsChainSimulator.ChainSimulator { + simulator, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: "../../../cmd/node/config/", + NumOfShards: 1, + GenesisTimestamp: time.Now().Unix(), + RoundDurationInMillis: uint64(4000), + RoundsPerEpoch: core.OptionalUint64{ + HasValue: true, + Value: 10, + }, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 1, + MetaChainMinNodes: 1, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: alterConfigsFunction, + }) + require.NoError(t, err) + require.NotNil(t, simulator) + + err = simulator.GenerateBlocksUntilEpochIsReached(1) + require.NoError(t, err) + + return simulator +} + +type participantsHolder struct { + sendersByShard map[int][]dtos.WalletAddress + relayerByShard map[int]dtos.WalletAddress + receiverByShard map[int]dtos.WalletAddress +} + +func newParticipantsHolder() *participantsHolder { + return &participantsHolder{ + sendersByShard: make(map[int][]dtos.WalletAddress), + relayerByShard: make(map[int]dtos.WalletAddress), + receiverByShard: make(map[int]dtos.WalletAddress), + } +} + +func createParticipants(t *testing.T, simulator testsChainSimulator.ChainSimulator, numSendersPerShard int) *participantsHolder { + numShards := int(simulator.GetNodeHandler(0).GetShardCoordinator().NumberOfShards()) + participants := newParticipantsHolder() + + for shard := 0; shard < numShards; shard++ { + senders := make([]dtos.WalletAddress, 0, numSendersPerShard) + + for i := 0; i < numSendersPerShard; i++ { + sender, err := simulator.GenerateAndMintWalletAddress(uint32(shard), oneEGLD) + require.NoError(t, err) + + senders = append(senders, sender) + } + + relayer, err := simulator.GenerateAndMintWalletAddress(uint32(shard), oneEGLD) + require.NoError(t, err) + + receiver, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + participants.sendersByShard[shard] = senders + participants.relayerByShard[shard] = relayer + participants.receiverByShard[shard] = receiver + } + + err := simulator.GenerateBlocks(1) + require.Nil(t, err) + + return participants +} + +type noncesTracker struct { + nonceByAddress map[string]uint64 +} + +func newNoncesTracker() *noncesTracker { + return &noncesTracker{ + nonceByAddress: make(map[string]uint64), + } +} + +func (tracker *noncesTracker) getThenIncrementNonce(address dtos.WalletAddress) uint64 { + nonce, ok := tracker.nonceByAddress[address.Bech32] + if !ok { + tracker.nonceByAddress[address.Bech32] = 0 + } + + tracker.nonceByAddress[address.Bech32]++ + return nonce +} + +func sendTransactions(t *testing.T, simulator testsChainSimulator.ChainSimulator, transactions []*transaction.Transaction) { + transactionsBySenderShard := make(map[int][]*transaction.Transaction) + shardCoordinator := simulator.GetNodeHandler(0).GetShardCoordinator() + + for _, tx := range transactions { + shard := int(shardCoordinator.ComputeId(tx.SndAddr)) + transactionsBySenderShard[shard] = append(transactionsBySenderShard[shard], tx) + } + + for shard, transactionsFromShard := range transactionsBySenderShard { + node := simulator.GetNodeHandler(uint32(shard)) + + for _, tx := range transactionsFromShard { + err := node.GetFacadeHandler().ValidateTransaction(tx) + require.NoError(t, err) + } + + numSent, err := node.GetFacadeHandler().SendBulkTransactions(transactionsFromShard) + + require.NoError(t, err) + require.Equal(t, len(transactionsFromShard), int(numSent)) + } +} + +func sendTransaction(t *testing.T, simulator testsChainSimulator.ChainSimulator, tx *transaction.Transaction) { + sendTransactions(t, simulator, []*transaction.Transaction{tx}) +} + +func selectTransactions(t *testing.T, simulator testsChainSimulator.ChainSimulator, shard int) ([]*txcache.WrappedTransaction, uint64) { + shardAsString := strconv.Itoa(shard) + node := simulator.GetNodeHandler(uint32(shard)) + accountsAdapter := node.GetStateComponents().AccountsAdapter() + poolsHolder := node.GetDataComponents().Datapool().Transactions() + + selectionSession, err := preprocess.NewSelectionSession(preprocess.ArgsSelectionSession{ + AccountsAdapter: accountsAdapter, + TransactionsProcessor: &testscommon.TxProcessorStub{}, + }) + require.NoError(t, err) + + mempool := poolsHolder.ShardDataStore(shardAsString).(*txcache.TxCache) + + selectedTransactions, gas := mempool.SelectTransactions( + selectionSession, + process.TxCacheSelectionGasRequested, + process.TxCacheSelectionMaxNumTxs, + process.TxCacheSelectionLoopMaximumDuration, + ) + + return selectedTransactions, gas +} + +func getNumTransactionsInPool(simulator testsChainSimulator.ChainSimulator, shard int) int { + node := simulator.GetNodeHandler(uint32(shard)) + poolsHolder := node.GetDataComponents().Datapool().Transactions() + return int(poolsHolder.GetCounts().GetTotal()) +} + +func getNumTransactionsInCurrentBlock(simulator testsChainSimulator.ChainSimulator, shard int) int { + node := simulator.GetNodeHandler(uint32(shard)) + currentBlock := node.GetDataComponents().Blockchain().GetCurrentBlockHeader() + return int(currentBlock.GetTxCount()) +} + +func getTransaction(t *testing.T, simulator testsChainSimulator.ChainSimulator, shard int, hash []byte) *transaction.ApiTransactionResult { + hashAsHex := hex.EncodeToString(hash) + transaction, err := simulator.GetNodeHandler(uint32(shard)).GetFacadeHandler().GetTransaction(hashAsHex, true) + require.NoError(t, err) + return transaction +} diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go new file mode 100644 index 00000000000..b7426c63f5f --- /dev/null +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -0,0 +1,1176 @@ +package relayedTx + +import ( + "crypto/rand" + "encoding/hex" + "encoding/json" + "math/big" + "strconv" + "strings" + "testing" + "time" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/config" + testsChainSimulator "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" + "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" + "github.com/multiversx/mx-chain-go/node/chainSimulator" + "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" + "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" + "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" + chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/sharding" + "github.com/multiversx/mx-chain-go/vm" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/require" +) + +const ( + defaultPathToInitialConfig = "../../../cmd/node/config/" + minGasPrice = 1_000_000_000 + minGasLimit = 50_000 + gasPerDataByte = 1_500 + deductionFactor = 100 + txVersion = 2 + mockTxSignature = "ssig" + mockRelayerTxSignature = "rsig" + maxNumOfBlocksToGenerateWhenExecutingTx = 10 + roundsPerEpoch = 30 + guardAccountCost = 250_000 + extraGasLimitForGuarded = minGasLimit +) + +var ( + oneEGLD = big.NewInt(1000000000000000000) +) + +func TestRelayedV3WithChainSimulator(t *testing.T) { + t.Run("sender == relayer move balance should consume fee", testRelayedV3RelayedBySenderMoveBalance()) + t.Run("receiver == relayer move balance should consume fee", testRelayedV3RelayedByReceiverMoveBalance()) + t.Run("successful intra shard move balance", testRelayedV3MoveBalance(0, 0, false, false)) + t.Run("successful intra shard guarded move balance", testRelayedV3MoveBalance(0, 0, false, true)) + t.Run("successful intra shard move balance with extra gas", testRelayedV3MoveBalance(0, 0, true, false)) + t.Run("successful cross shard move balance", testRelayedV3MoveBalance(0, 1, false, false)) + t.Run("successful cross shard guarded move balance", testRelayedV3MoveBalance(0, 1, false, true)) + t.Run("successful cross shard move balance with extra gas", testRelayedV3MoveBalance(0, 1, true, false)) + t.Run("intra shard move balance, lower nonce", testRelayedV3MoveBalanceLowerNonce(0, 0)) + t.Run("cross shard move balance, lower nonce", testRelayedV3MoveBalanceLowerNonce(0, 1)) + t.Run("intra shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 0)) + t.Run("cross shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 1)) + + t.Run("successful intra shard sc call with refunds, existing sender", testRelayedV3ScCall(0, 0, true, false)) + t.Run("successful intra shard sc call with refunds, existing sender, relayed by sender", testRelayedV3ScCall(0, 0, true, true)) + t.Run("successful intra shard sc call with refunds, new sender", testRelayedV3ScCall(0, 0, false, false)) + t.Run("successful cross shard sc call with refunds, existing sender", testRelayedV3ScCall(0, 1, true, false)) + t.Run("successful cross shard sc call with refunds, existing sender, relayed by sender", testRelayedV3ScCall(0, 1, true, true)) + t.Run("successful cross shard sc call with refunds, new sender", testRelayedV3ScCall(0, 1, false, false)) + t.Run("intra shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 0)) + t.Run("cross shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 1)) + t.Run("intra shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 0)) + t.Run("cross shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 1)) + + t.Run("create new delegation contract", testRelayedV3MetaInteraction()) +} + +func testRelayedV3MoveBalance( + relayerShard uint32, + destinationShard uint32, + extraGas bool, + guardedTx bool, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(destinationShard, big.NewInt(0)) + require.NoError(t, err) + + guardian, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + senderNonce := uint64(0) + if guardedTx { + // Set guardian for sender + setGuardianTxData := "SetGuardian@" + hex.EncodeToString(guardian.Bytes) + "@" + hex.EncodeToString([]byte("uuid")) + setGuardianGasLimit := minGasLimit + 1500*len(setGuardianTxData) + guardAccountCost + setGuardianTx := generateTransaction(sender.Bytes, senderNonce, sender.Bytes, big.NewInt(0), setGuardianTxData, uint64(setGuardianGasLimit)) + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(setGuardianTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + senderNonce++ + + // fast-forward until the guardian becomes active + err = cs.GenerateBlocks(roundsPerEpoch * 20) + require.NoError(t, err) + + // guard account + guardAccountTxData := "GuardAccount" + guardAccountGasLimit := minGasLimit + 1500*len(guardAccountTxData) + guardAccountCost + guardAccountTx := generateTransaction(sender.Bytes, senderNonce, sender.Bytes, big.NewInt(0), guardAccountTxData, uint64(guardAccountGasLimit)) + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(guardAccountTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + senderNonce++ + } + + senderBalanceBefore := getBalance(t, cs, sender) + + gasLimit := minGasLimit * 2 + extraGasLimit := 0 + if extraGas { + extraGasLimit = minGasLimit + } + if guardedTx { + gasLimit += extraGasLimitForGuarded + } + relayedTx := generateRelayedV3Transaction(sender.Bytes, senderNonce, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit+extraGasLimit)) + + if guardedTx { + relayedTx.GuardianAddr = guardian.Bytes + relayedTx.GuardianSignature = []byte(mockTxSignature) + relayedTx.Options = 2 + } + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + isIntraShard := relayerShard == destinationShard + if isIntraShard { + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + } + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, guardedTx) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + senderBalanceDiff := big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter) + require.Equal(t, oneEGLD.String(), senderBalanceDiff.String()) + + // check receiver balance + receiverBalanceAfter := getBalance(t, cs, receiver) + require.Equal(t, oneEGLD.String(), receiverBalanceAfter.String()) + + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + + if extraGas && isIntraShard { + require.NotNil(t, result.Receipt) + } + } +} + +func testRelayedV3MoveBalanceLowerNonce( + relayerShard uint32, + receiverShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(receiverShard, big.NewInt(0)) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + gasLimit := minGasLimit * 2 + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit)) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // send same tx again, lower nonce + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.Contains(t, err.Error(), process.ErrWrongTransaction.Error()) + require.Nil(t, result) + } +} + +func testRelayedV3MoveInvalidGasLimit( + relayerShard uint32, + receiverShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(receiverShard, big.NewInt(0)) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + gasLimit := minGasLimit + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.Contains(t, err.Error(), process.ErrInsufficientGasLimitInTx.Error()) + require.Nil(t, result) + } +} + +func testRelayedV3ScCall( + relayerShard uint32, + ownerShard uint32, + existingSenderWithBalance bool, + relayedBySender bool, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + relayerInitialBalance := initialBalance + + sender, senderInitialBalance := prepareSender(t, cs, existingSenderWithBalance, relayerShard, initialBalance) + if relayedBySender { + relayer = sender + relayerInitialBalance = senderInitialBalance + } + + owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + resultDeploy, scAddressBytes := deployAdder(t, cs, owner, 0) + refundDeploy := getRefundValue(resultDeploy.SmartContractResults) + + // send relayed tx + txDataAdd := "add@" + hex.EncodeToString(big.NewInt(1).Bytes()) + gasLimit := uint64(3000000) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, scAddressBytes, relayer.Bytes, big.NewInt(0), txDataAdd, gasLimit) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // if cross shard, generate few more blocks for eventual refunds to be executed + if relayerShard != ownerShard { + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + } + + checkSum(t, cs.GetNodeHandler(ownerShard), scAddressBytes, owner.Bytes, 1) + + refundValue := getRefundValue(result.SmartContractResults) + require.NotZero(t, refundValue.Uint64()) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(relayerInitialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance, only if the tx was not relayed by sender + if !relayedBySender { + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, senderInitialBalance.String(), senderBalanceAfter.String()) + } + + // check owner balance + _, feeDeploy, _ := computeTxGasAndFeeBasedOnRefund(resultDeploy, refundDeploy, false, false) + ownerBalanceAfter := getBalance(t, cs, owner) + ownerFee := big.NewInt(0).Sub(initialBalance, ownerBalanceAfter) + require.Equal(t, feeDeploy.String(), ownerFee.String()) + + // check scrs + require.Equal(t, 1, len(result.SmartContractResults)) + for _, scr := range result.SmartContractResults { + checkSCRSucceeded(t, cs, scr) + } + } +} + +func testRelayedV3RelayedBySenderMoveBalance() func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + + sender, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + senderNonce := uint64(0) + senderBalanceBefore := getBalance(t, cs, sender) + + gasLimit := minGasLimit * 2 + relayedTx := generateRelayedV3Transaction(sender.Bytes, senderNonce, sender.Bytes, sender.Bytes, big.NewInt(0), "", uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check sender balance + expectedFee := core.SafeMul(uint64(gasLimit), uint64(minGasPrice)) + senderBalanceAfter := getBalance(t, cs, sender) + senderBalanceDiff := big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter) + require.Equal(t, expectedFee.String(), senderBalanceDiff.String()) + + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + } +} + +func testRelayedV3RelayedByReceiverMoveBalance() func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + + sender, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + senderNonce := uint64(0) + receiverBalanceBefore := getBalance(t, cs, receiver) + + gasLimit := minGasLimit * 2 + relayedTx := generateRelayedV3Transaction(sender.Bytes, senderNonce, receiver.Bytes, receiver.Bytes, big.NewInt(0), "", uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, senderBalanceAfter.String(), initialBalance.String()) + + // check receiver balance + expectedFee := core.SafeMul(uint64(gasLimit), uint64(minGasPrice)) + receiverBalanceAfter := getBalance(t, cs, receiver) + receiverBalanceDiff := big.NewInt(0).Sub(receiverBalanceBefore, receiverBalanceAfter) + require.Equal(t, receiverBalanceDiff.String(), expectedFee.String()) + + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + } +} + +func prepareSender( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + existingSenderWithBalance bool, + shard uint32, + initialBalance *big.Int, +) (dtos.WalletAddress, *big.Int) { + if existingSenderWithBalance { + sender, err := cs.GenerateAndMintWalletAddress(shard, initialBalance) + require.NoError(t, err) + + return sender, initialBalance + } + + shardC := cs.GetNodeHandler(shard).GetShardCoordinator() + pkConv := cs.GetNodeHandler(shard).GetCoreComponents().AddressPubKeyConverter() + newAddress := generateAddressInShard(shardC, pkConv.Len()) + return dtos.WalletAddress{ + Bech32: pkConv.SilentEncode(newAddress, logger.GetOrCreate("tmp")), + Bytes: newAddress, + }, big.NewInt(0) +} + +func generateAddressInShard(shardCoordinator sharding.Coordinator, len int) []byte { + for { + buff := generateAddress(len) + shardID := shardCoordinator.ComputeId(buff) + if shardID == shardCoordinator.SelfId() { + return buff + } + } +} + +func generateAddress(len int) []byte { + buff := make([]byte, len) + _, _ = rand.Read(buff) + + return buff +} + +func testRelayedV3ScCallInvalidGasLimit( + relayerShard uint32, + ownerShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + _, scAddressBytes := deployAdder(t, cs, owner, 0) + + // send relayed tx with less gas limit + txDataAdd := "add@" + hex.EncodeToString(big.NewInt(1).Bytes()) + gasLimit := gasPerDataByte*len(txDataAdd) + minGasLimit + minGasLimit + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, scAddressBytes, relayer.Bytes, big.NewInt(0), txDataAdd, uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + require.NotNil(t, result.Logs) + require.Equal(t, 2, len(result.Logs.Events)) + for _, event := range result.Logs.Events { + if event.Identifier == core.SignalErrorOperation { + continue + } + + require.Equal(t, 1, len(event.AdditionalData)) + require.Contains(t, string(event.AdditionalData[0]), "[not enough gas]") + } + + refundValue := getRefundValue(result.SmartContractResults) + require.Zero(t, refundValue.Uint64()) + + // check fee fields, should consume full gas + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, result.InitiallyPaidFee, result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + require.Equal(t, relayedTx.GasLimit, result.GasUsed) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) + } +} + +func testRelayedV3ScCallInvalidMethod( + relayerShard uint32, + ownerShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + _, scAddressBytes := deployAdder(t, cs, owner, 0) + + // send relayed tx with invalid value + txDataAdd := "invalid" + gasLimit := uint64(3000000) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, scAddressBytes, relayer.Bytes, big.NewInt(0), txDataAdd, uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + require.NotNil(t, result.Logs) + require.Equal(t, 2, len(result.Logs.Events)) + for _, event := range result.Logs.Events { + if event.Identifier == core.SignalErrorOperation { + continue + } + + require.Equal(t, 1, len(event.AdditionalData)) + require.Contains(t, string(event.AdditionalData[0]), "[invalid function (not found)]") + } + + refundValue := getRefundValue(result.SmartContractResults) + require.Zero(t, refundValue.Uint64()) // no refund, tx failed + + // check fee fields, should consume full gas + initiallyPaidFee, fee, _ := computeTxGasAndFeeBasedOnRefund(result, refundValue, false, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) + } +} + +func testRelayedV3MetaInteraction() func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + relayerShard := uint32(0) + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10000)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + // send createNewDelegationContract transaction + txData := "createNewDelegationContract@00@00" + gasLimit := uint64(60000000) + value := big.NewInt(0).Mul(oneEGLD, big.NewInt(1250)) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, vm.DelegationManagerSCAddress, relayer.Bytes, value, txData, gasLimit) + + relayerBefore := getBalance(t, cs, relayer) + senderBefore := getBalance(t, cs, sender) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + + relayerAfter := getBalance(t, cs, relayer) + senderAfter := getBalance(t, cs, sender) + + // check consumed fees + refund := getRefundValue(result.SmartContractResults) + consumedFee := big.NewInt(0).Sub(relayerBefore, relayerAfter) + + gasForFullPrice := uint64(len(txData)*gasPerDataByte + minGasLimit + minGasLimit) + gasForDeductedPrice := gasLimit - gasForFullPrice + deductedGasPrice := uint64(minGasPrice / deductionFactor) + initialFee := gasForFullPrice*minGasPrice + gasForDeductedPrice*deductedGasPrice + initialFeeInt := big.NewInt(0).SetUint64(initialFee) + expectedConsumedFee := big.NewInt(0).Sub(initialFeeInt, refund) + + gasUsed := gasForFullPrice + gasForDeductedPrice - refund.Uint64()/deductedGasPrice + require.Equal(t, expectedConsumedFee.String(), consumedFee.String()) + require.Equal(t, value.String(), big.NewInt(0).Sub(senderBefore, senderAfter).String(), "sender should have consumed the value only") + require.Equal(t, initialFeeInt.String(), result.InitiallyPaidFee) + require.Equal(t, expectedConsumedFee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + } +} + +func TestFixRelayedMoveBalanceWithChainSimulator(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + expectedFeeScCallBefore := "827294920000000" + expectedFeeScCallAfter := "885704920000000" + t.Run("sc call", testFixRelayedMoveBalanceWithChainSimulatorScCall(expectedFeeScCallBefore, expectedFeeScCallAfter)) + + expectedFeeMoveBalanceBefore := "809500000000000" // 506 * 1500 + 50000 + 500 + expectedFeeMoveBalanceAfter := "859000000000000" // 506 * 1500 + 50000 + 50000 + t.Run("move balance", testFixRelayedMoveBalanceWithChainSimulatorMoveBalance(expectedFeeMoveBalanceBefore, expectedFeeMoveBalanceAfter)) +} + +func testFixRelayedMoveBalanceWithChainSimulatorScCall( + expectedFeeBeforeFix string, + expectedFeeAfterFix string, +) func(t *testing.T) { + return func(t *testing.T) { + + providedActivationEpoch := uint32(7) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + // deploy adder contract + owner, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + ownerNonce := uint64(0) + _, scAddressBytes := deployAdder(t, cs, owner, ownerNonce) + + // fast-forward until epoch 4 + err = cs.GenerateBlocksUntilEpochIsReached(int32(4)) + require.NoError(t, err) + + // send relayed tx + txDataAdd := "add@" + hex.EncodeToString(big.NewInt(1).Bytes()) + innerTx := generateTransaction(owner.Bytes, 1, scAddressBytes, big.NewInt(0), txDataAdd, 3000000) + marshalledTx, err := json.Marshal(innerTx) + require.NoError(t, err) + txData := []byte("relayedTx@" + hex.EncodeToString(marshalledTx)) + gasLimit := 50000 + uint64(len(txData))*1500 + innerTx.GasLimit + + relayedTx := generateTransaction(relayer.Bytes, 0, owner.Bytes, big.NewInt(0), string(txData), gasLimit) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // send relayed tx, fix still not active + innerTx = generateTransaction(owner.Bytes, 2, scAddressBytes, big.NewInt(0), txDataAdd, 3000000) + marshalledTx, err = json.Marshal(innerTx) + require.NoError(t, err) + txData = []byte("relayedTx@" + hex.EncodeToString(marshalledTx)) + gasLimit = 50000 + uint64(len(txData))*1500 + innerTx.GasLimit + + relayedTx = generateTransaction(relayer.Bytes, 1, owner.Bytes, big.NewInt(0), string(txData), gasLimit) + + relayerBalanceBefore := getBalance(t, cs, relayer) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + relayerBalanceAfter := getBalance(t, cs, relayer) + + feeConsumed := big.NewInt(0).Sub(relayerBalanceBefore, relayerBalanceAfter) + + require.Equal(t, expectedFeeBeforeFix, feeConsumed.String()) + + // fast-forward until the fix is active + err = cs.GenerateBlocksUntilEpochIsReached(int32(providedActivationEpoch)) + require.NoError(t, err) + + // send relayed tx after fix + innerTx = generateTransaction(owner.Bytes, 3, scAddressBytes, big.NewInt(0), txDataAdd, 3000000) + marshalledTx, err = json.Marshal(innerTx) + require.NoError(t, err) + txData = []byte("relayedTx@" + hex.EncodeToString(marshalledTx)) + gasLimit = 50000 + uint64(len(txData))*1500 + innerTx.GasLimit + + relayedTx = generateTransaction(relayer.Bytes, 2, owner.Bytes, big.NewInt(0), string(txData), gasLimit) + + relayerBalanceBefore = getBalance(t, cs, relayer) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + relayerBalanceAfter = getBalance(t, cs, relayer) + + feeConsumed = big.NewInt(0).Sub(relayerBalanceBefore, relayerBalanceAfter) + + require.Equal(t, expectedFeeAfterFix, feeConsumed.String()) + } +} + +func testFixRelayedMoveBalanceWithChainSimulatorMoveBalance( + expectedFeeBeforeFix string, + expectedFeeAfterFix string, +) func(t *testing.T) { + return func(t *testing.T) { + + providedActivationEpoch := uint32(5) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(0, big.NewInt(0)) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + // send relayed tx + innerTx := generateTransaction(sender.Bytes, 0, receiver.Bytes, oneEGLD, "", 50000) + marshalledTx, err := json.Marshal(innerTx) + require.NoError(t, err) + txData := []byte("relayedTx@" + hex.EncodeToString(marshalledTx)) + gasLimit := 50000 + uint64(len(txData))*1500 + innerTx.GasLimit + + relayedTx := generateTransaction(relayer.Bytes, 0, sender.Bytes, big.NewInt(0), string(txData), gasLimit) + + relayerBalanceBefore := getBalance(t, cs, relayer) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + relayerBalanceAfter := getBalance(t, cs, relayer) + + feeConsumed := big.NewInt(0).Sub(relayerBalanceBefore, relayerBalanceAfter) + + require.Equal(t, expectedFeeBeforeFix, feeConsumed.String()) + + // fast-forward until the fix is active + err = cs.GenerateBlocksUntilEpochIsReached(int32(providedActivationEpoch)) + require.NoError(t, err) + + // send relayed tx + innerTx = generateTransaction(sender.Bytes, 1, receiver.Bytes, oneEGLD, "", 50000) + marshalledTx, err = json.Marshal(innerTx) + require.NoError(t, err) + txData = []byte("relayedTx@" + hex.EncodeToString(marshalledTx)) + gasLimit = 50000 + uint64(len(txData))*1500 + innerTx.GasLimit + + relayedTx = generateTransaction(relayer.Bytes, 1, sender.Bytes, big.NewInt(0), string(txData), gasLimit) + + relayerBalanceBefore = getBalance(t, cs, relayer) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + relayerBalanceAfter = getBalance(t, cs, relayer) + + feeConsumed = big.NewInt(0).Sub(relayerBalanceBefore, relayerBalanceAfter) + + require.Equal(t, expectedFeeAfterFix, feeConsumed.String()) + } +} + +func TestRelayedTransactionFeeField(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + cs := startChainSimulator(t, func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.RelayedTransactionsEnableEpoch = 1 + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = 1 + }) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(0, big.NewInt(0)) + require.NoError(t, err) + + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + t.Run("relayed v1", func(t *testing.T) { + innerTx := generateTransaction(sender.Bytes, 0, receiver.Bytes, oneEGLD, "", minGasLimit) + buff, err := json.Marshal(innerTx) + require.NoError(t, err) + + txData := []byte("relayedTx@" + hex.EncodeToString(buff)) + gasLimit := minGasLimit + len(txData)*gasPerDataByte + int(innerTx.GasLimit) + relayedTx := generateTransaction(relayer.Bytes, 0, sender.Bytes, big.NewInt(0), string(txData), uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + expectedFee := core.SafeMul(uint64(gasLimit), minGasPrice) + require.Equal(t, expectedFee.String(), result.Fee) + require.Equal(t, expectedFee.String(), result.InitiallyPaidFee) + require.Equal(t, uint64(gasLimit), result.GasUsed) + }) +} + +func TestRegularMoveBalanceWithRefundReceipt(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + cs := startChainSimulator(t, func(cfg *config.Configs) {}) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + + sender, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + senderNonce := uint64(0) + + extraGas := uint64(minGasLimit) + gasLimit := minGasLimit + extraGas + tx := generateTransaction(sender.Bytes, senderNonce, sender.Bytes, oneEGLD, "", gasLimit) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + require.NotNil(t, result.Receipt) + expectedGasRefunded := core.SafeMul(extraGas, minGasPrice/deductionFactor) + require.Equal(t, expectedGasRefunded.String(), result.Receipt.Value.String()) +} + +func startChainSimulator( + t *testing.T, + alterConfigsFunction func(cfg *config.Configs), +) testsChainSimulator.ChainSimulator { + roundDurationInMillis := uint64(6000) + roundsPerEpochOpt := core.OptionalUint64{ + HasValue: true, + Value: roundsPerEpoch, + } + + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: 3, + GenesisTimestamp: time.Now().Unix(), + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpochOpt, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 3, + NumNodesWaitingListShard: 3, + AlterConfigsFunction: alterConfigsFunction, + }) + require.NoError(t, err) + require.NotNil(t, cs) + + err = cs.GenerateBlocksUntilEpochIsReached(1) + require.NoError(t, err) + + return cs +} + +func generateRelayedV3Transaction(sender []byte, nonce uint64, receiver []byte, relayer []byte, value *big.Int, data string, gasLimit uint64) *transaction.Transaction { + tx := generateTransaction(sender, nonce, receiver, value, data, gasLimit) + tx.RelayerSignature = []byte(mockRelayerTxSignature) + tx.RelayerAddr = relayer + return tx +} + +func generateTransaction(sender []byte, nonce uint64, receiver []byte, value *big.Int, data string, gasLimit uint64) *transaction.Transaction { + return &transaction.Transaction{ + Nonce: nonce, + Value: value, + SndAddr: sender, + RcvAddr: receiver, + Data: []byte(data), + GasLimit: gasLimit, + GasPrice: minGasPrice, + ChainID: []byte(configs.ChainID), + Version: txVersion, + Signature: []byte(mockTxSignature), + } +} + +func getBalance( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + address dtos.WalletAddress, +) *big.Int { + account, err := cs.GetAccount(address) + require.NoError(t, err) + + balance, ok := big.NewInt(0).SetString(account.Balance, 10) + require.True(t, ok) + + return balance +} + +func deployAdder( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + owner dtos.WalletAddress, + ownerNonce uint64, +) (*transaction.ApiTransactionResult, []byte) { + pkConv := cs.GetNodeHandler(0).GetCoreComponents().AddressPubKeyConverter() + + err := cs.GenerateBlocks(1) + require.Nil(t, err) + + scCode := wasm.GetSCCode("testData/adder.wasm") + params := []string{scCode, wasm.VMTypeHex, wasm.DummyCodeMetadataHex, "00"} + txDataDeploy := strings.Join(params, "@") + deployTx := generateTransaction(owner.Bytes, ownerNonce, make([]byte, 32), big.NewInt(0), txDataDeploy, 100000000) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(deployTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + scAddress := result.Logs.Events[0].Address + scAddressBytes, _ := pkConv.Decode(scAddress) + + return result, scAddressBytes +} + +func checkSum( + t *testing.T, + nodeHandler chainSimulatorProcess.NodeHandler, + scAddress []byte, + callerAddress []byte, + expectedSum int, +) { + scQuery := &process.SCQuery{ + ScAddress: scAddress, + FuncName: "getSum", + CallerAddr: callerAddress, + CallValue: big.NewInt(0), + } + result, _, err := nodeHandler.GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "ok", result.ReturnCode) + + sum, err := strconv.Atoi(hex.EncodeToString(result.ReturnData[0])) + require.NoError(t, err) + + require.Equal(t, expectedSum, sum) +} + +func getRefundValue(scrs []*transaction.ApiSmartContractResult) *big.Int { + for _, scr := range scrs { + if scr.IsRefund { + return scr.Value + } + } + + return big.NewInt(0) +} + +func computeTxGasAndFeeBasedOnRefund( + result *transaction.ApiTransactionResult, + refund *big.Int, + isMoveBalance bool, + guardedTx bool, +) (*big.Int, *big.Int, uint64) { + deductedGasPrice := uint64(minGasPrice / deductionFactor) + + initialTx := result.Tx + gasForFullPrice := uint64(minGasLimit + gasPerDataByte*len(initialTx.GetData())) + if guardedTx { + gasForFullPrice += extraGasLimitForGuarded + } + + if common.IsRelayedTxV3(initialTx) { + gasForFullPrice += uint64(minGasLimit) // relayer fee + } + gasForDeductedPrice := initialTx.GetGasLimit() - gasForFullPrice + + initialFee := gasForFullPrice*minGasPrice + gasForDeductedPrice*deductedGasPrice + finalFee := initialFee - refund.Uint64() + + gasRefunded := refund.Uint64() / deductedGasPrice + gasConsumed := gasForFullPrice + gasForDeductedPrice - gasRefunded + + if isMoveBalance { + return big.NewInt(0).SetUint64(initialFee), big.NewInt(0).SetUint64(gasForFullPrice * minGasPrice), gasForFullPrice + } + + return big.NewInt(0).SetUint64(initialFee), big.NewInt(0).SetUint64(finalFee), gasConsumed +} + +func checkSCRSucceeded( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + scr *transaction.ApiSmartContractResult, +) { + pkConv := cs.GetNodeHandler(0).GetCoreComponents().AddressPubKeyConverter() + shardC := cs.GetNodeHandler(0).GetShardCoordinator() + addr, err := pkConv.Decode(scr.RcvAddr) + require.NoError(t, err) + + senderShard := shardC.ComputeId(addr) + tx, err := cs.GetNodeHandler(senderShard).GetFacadeHandler().GetTransaction(scr.Hash, true) + require.NoError(t, err) + require.Equal(t, transaction.TxStatusSuccess, tx.Status) + + if tx.ReturnMessage == core.GasRefundForRelayerMessage { + return + } + + require.GreaterOrEqual(t, len(tx.Logs.Events), 1) + for _, event := range tx.Logs.Events { + if event.Identifier == core.WriteLogIdentifier { + continue + } + + require.Equal(t, core.CompletedTxEventIdentifier, event.Identifier) + } +} diff --git a/integrationTests/chainSimulator/relayedTx/testData/adder.wasm b/integrationTests/chainSimulator/relayedTx/testData/adder.wasm new file mode 100644 index 00000000000..b6bc9b4e13b Binary files /dev/null and b/integrationTests/chainSimulator/relayedTx/testData/adder.wasm differ diff --git a/integrationTests/chainSimulator/relayedTx/testData/egld-esdt-swap.wasm b/integrationTests/chainSimulator/relayedTx/testData/egld-esdt-swap.wasm new file mode 100644 index 00000000000..7244307f1cc Binary files /dev/null and b/integrationTests/chainSimulator/relayedTx/testData/egld-esdt-swap.wasm differ diff --git a/integrationTests/chainSimulator/staking/jail/jail_test.go b/integrationTests/chainSimulator/staking/jail/jail_test.go index f3e920a4dbf..219468389b4 100644 --- a/integrationTests/chainSimulator/staking/jail/jail_test.go +++ b/integrationTests/chainSimulator/staking/jail/jail_test.go @@ -100,6 +100,9 @@ func testChainSimulatorJailAndUnJail(t *testing.T, targetEpoch int32, nodeStatus walletAddress, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(walletAddress.Bytes, 0, vm.ValidatorSCAddress, chainSimulatorIntegrationTests.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation) stakeTx, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(txStake, staking.MaxNumOfBlockToGenerateWhenExecutingTx) @@ -204,6 +207,9 @@ func TestChainSimulator_FromQueueToAuctionList(t *testing.T) { walletAddress, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(walletAddress.Bytes, 0, vm.ValidatorSCAddress, chainSimulatorIntegrationTests.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation) stakeTx, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(txStake, staking.MaxNumOfBlockToGenerateWhenExecutingTx) diff --git a/integrationTests/chainSimulator/staking/stake/simpleStake_test.go b/integrationTests/chainSimulator/staking/stake/simpleStake_test.go index a1176b7795f..bfc9f3c11b6 100644 --- a/integrationTests/chainSimulator/staking/stake/simpleStake_test.go +++ b/integrationTests/chainSimulator/staking/stake/simpleStake_test.go @@ -94,6 +94,9 @@ func testChainSimulatorSimpleStake(t *testing.T, targetEpoch int32, nodesStatus wallet3, err := cs.GenerateAndMintWalletAddress(0, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + _, blsKeys, err := chainSimulator.GenerateBlsPrivateKeys(3) require.Nil(t, err) @@ -201,6 +204,9 @@ func TestChainSimulator_StakingV4Step2APICalls(t *testing.T) { validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + // Stake a new validator that should end up in auction in step 1 txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, chainSimulatorIntegrationTests.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation) diff --git a/integrationTests/chainSimulator/staking/stake/stakeAndUnStake_test.go b/integrationTests/chainSimulator/staking/stake/stakeAndUnStake_test.go index 1804350ded9..acb0c7537ed 100644 --- a/integrationTests/chainSimulator/staking/stake/stakeAndUnStake_test.go +++ b/integrationTests/chainSimulator/staking/stake/stakeAndUnStake_test.go @@ -103,6 +103,9 @@ func TestChainSimulator_AddValidatorKey(t *testing.T) { }) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + // Step 3 --- generate and send a stake transaction with the BLS key of the validator key that was added at step 1 stakeValue, _ := big.NewInt(0).SetString("2500000000000000000000", 10) tx := &transaction.Transaction{ @@ -237,6 +240,9 @@ func TestChainSimulator_AddANewValidatorAfterStakingV4(t *testing.T) { }) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + // Step 3 --- generate and send a stake transaction with the BLS keys of the validators key that were added at step 1 validatorData := "" for _, blsKey := range blsKeys { @@ -353,6 +359,9 @@ func testStakeUnStakeUnBond(t *testing.T, targetEpoch int32) { walletAddress, err := cs.GenerateAndMintWalletAddress(walletAddressShardID, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(walletAddress.Bytes, 0, vm.ValidatorSCAddress, chainSimulatorIntegrationTests.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation) stakeTx, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(txStake, staking.MaxNumOfBlockToGenerateWhenExecutingTx) @@ -583,6 +592,9 @@ func testChainSimulatorDirectStakedNodesStakingFunds(t *testing.T, cs chainSimul validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Set(chainSimulatorIntegrationTests.MinimumStakeValue) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -811,6 +823,9 @@ func testChainSimulatorDirectStakedUnstakeFundsWithDeactivation(t *testing.T, cs validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Set(chainSimulatorIntegrationTests.MinimumStakeValue) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -1092,6 +1107,9 @@ func testChainSimulatorDirectStakedUnstakeFundsWithDeactivationAndReactivation(t validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Set(chainSimulatorIntegrationTests.MinimumStakeValue) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -1322,6 +1340,9 @@ func testChainSimulatorDirectStakedWithdrawUnstakedFundsBeforeUnbonding(t *testi validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(2600)) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -1556,6 +1577,9 @@ func testChainSimulatorDirectStakedWithdrawUnstakedFundsInFirstEpoch(t *testing. validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(2600)) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -1827,6 +1851,9 @@ func testChainSimulatorDirectStakedWithdrawUnstakedFundsInBatches(t *testing.T, validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(2600)) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -2183,6 +2210,9 @@ func testChainSimulatorDirectStakedWithdrawUnstakedFundsInEpoch(t *testing.T, cs validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + stakeValue := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(2600)) txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) txStake := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, stakeValue, txDataField, staking.GasLimitForStakeOperation) @@ -2524,6 +2554,9 @@ func createStakeTransaction(t *testing.T, cs chainSimulatorIntegrationTests.Chai validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature) return chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, chainSimulatorIntegrationTests.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation) } diff --git a/integrationTests/chainSimulator/staking/stakingProvider/delegation_test.go b/integrationTests/chainSimulator/staking/stakingProvider/delegation_test.go index 392bce9ff02..1d1b35f19bd 100644 --- a/integrationTests/chainSimulator/staking/stakingProvider/delegation_test.go +++ b/integrationTests/chainSimulator/staking/stakingProvider/delegation_test.go @@ -301,6 +301,9 @@ func testChainSimulatorMakeNewContractFromValidatorData(t *testing.T, cs chainSi delegator2, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + log.Info("working with the following addresses", "newValidatorOwner", validatorOwner.Bech32, "delegator1", delegator1.Bech32, "delegator2", delegator2.Bech32) @@ -634,6 +637,9 @@ func testChainSimulatorMakeNewContractFromValidatorDataWith2StakingContracts(t * validatorOwnerB, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + log.Info("working with the following addresses", "validatorOwnerA", validatorOwnerA.Bech32, "validatorOwnerB", validatorOwnerB.Bech32) @@ -875,6 +881,9 @@ func testChainSimulatorMakeNewContractFromValidatorDataWith1StakingContractUnsta delegator, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + log.Info("working with the following addresses", "owner", owner.Bech32, "", delegator.Bech32) @@ -1203,6 +1212,9 @@ func testChainSimulatorCreateNewDelegationContract(t *testing.T, cs chainSimulat delegator2, err := cs.GenerateAndMintWalletAddress(core.AllShardId, initialFunds) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + maxDelegationCap := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(51000)) // 51000 EGLD cap txCreateDelegationContract := chainSimulatorIntegrationTests.GenerateTransaction(validatorOwner.Bytes, 0, vm.DelegationManagerSCAddress, staking.InitialDelegationValue, fmt.Sprintf("createNewDelegationContract@%s@%s", hex.EncodeToString(maxDelegationCap.Bytes()), hexServiceFee), @@ -1580,6 +1592,9 @@ func testChainSimulatorMaxDelegationCap(t *testing.T, cs chainSimulatorIntegrati delegatorC, err := cs.GenerateAndMintWalletAddress(core.AllShardId, initialFunds) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + // Step 3: Create a new delegation contract maxDelegationCap := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(3000)) // 3000 EGLD cap @@ -1965,6 +1980,9 @@ func testChainSimulatorMergingDelegation(t *testing.T, cs chainSimulatorIntegrat validatorB, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + log.Info("Step 1. User A: - stake 1 node to have 100 egld more than minimum stake value") stakeValue := big.NewInt(0).Set(chainSimulatorIntegrationTests.MinimumStakeValue) addedStakedValue := big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(100)) diff --git a/integrationTests/chainSimulator/staking/stakingProvider/stakingProviderWithNodesinQueue_test.go b/integrationTests/chainSimulator/staking/stakingProvider/stakingProviderWithNodesinQueue_test.go index 375953d7588..dd89ecf2c28 100644 --- a/integrationTests/chainSimulator/staking/stakingProvider/stakingProviderWithNodesinQueue_test.go +++ b/integrationTests/chainSimulator/staking/stakingProvider/stakingProviderWithNodesinQueue_test.go @@ -75,6 +75,8 @@ func testStakingProviderWithNodesReStakeUnStaked(t *testing.T, stakingV4Activati mintValue := big.NewInt(0).Mul(big.NewInt(5000), chainSimulatorIntegrationTests.OneEGLD) validatorOwner, err := cs.GenerateAndMintWalletAddress(0, mintValue) require.Nil(t, err) + + err = cs.GenerateBlocks(1) require.Nil(t, err) err = cs.GenerateBlocksUntilEpochIsReached(1) diff --git a/integrationTests/chainSimulator/testing.go b/integrationTests/chainSimulator/testing.go index 605bf76ac7f..212021a8fbd 100644 --- a/integrationTests/chainSimulator/testing.go +++ b/integrationTests/chainSimulator/testing.go @@ -196,6 +196,9 @@ func CheckGenerateTransactions(t *testing.T, chainSimulator ChainSimulator) { wallet4, err := chainSimulator.GenerateAndMintWalletAddress(2, InitialAmount) require.Nil(t, err) + err = chainSimulator.GenerateBlocks(1) + require.Nil(t, err) + gasLimit := uint64(50000) tx0 := GenerateTransaction(wallet0.Bytes, 0, wallet2.Bytes, transferValue, "", gasLimit) tx1 := GenerateTransaction(wallet1.Bytes, 0, wallet2.Bytes, transferValue, "", gasLimit) diff --git a/integrationTests/chainSimulator/vm/egldMultiTransfer_test.go b/integrationTests/chainSimulator/vm/egldMultiTransfer_test.go new file mode 100644 index 00000000000..5e9d641f90a --- /dev/null +++ b/integrationTests/chainSimulator/vm/egldMultiTransfer_test.go @@ -0,0 +1,763 @@ +package vm + +import ( + "encoding/hex" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee" + "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + "github.com/multiversx/mx-chain-go/node/chainSimulator" + "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" + "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" + "github.com/multiversx/mx-chain-go/vm" + "github.com/stretchr/testify/require" +) + +func TestChainSimulator_EGLD_MultiTransfer(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpoch := uint32(4) + + baseIssuingCost := "1000" + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.EGLDInMultiTransferEnableEpoch = activationEpoch + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], metaESDTTokenID, roles) + nonce++ + + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) + + // issue NFT + nftTicker := []byte("NFTTICKER") + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + sftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[0], sftTokenID, roles) + nonce++ + + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } + + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } + + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + fmt.Println(txResult) + fmt.Println(string(txResult.Logs.Events[0].Topics[0])) + fmt.Println(string(txResult.Logs.Events[0].Topics[1])) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + } + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + account0, err := cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalanceStr0 := account0.Balance + + account1, err := cs.GetAccount(addrs[1]) + require.Nil(t, err) + + beforeBalanceStr1 := account1.Balance + + egldValue := oneEGLD.Mul(oneEGLD, big.NewInt(3)) + tx = multiESDTNFTTransferWithEGLDTx(nonce, addrs[0].Bytes, addrs[1].Bytes, tokenIDs, egldValue) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + egldLog := string(txResult.Logs.Events[0].Topics[0]) + require.Equal(t, "EGLD-000000", egldLog) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + // check accounts balance + account0, err = cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalance0, _ := big.NewInt(0).SetString(beforeBalanceStr0, 10) + + expectedBalance0 := big.NewInt(0).Sub(beforeBalance0, egldValue) + txsFee, _ := big.NewInt(0).SetString(txResult.Fee, 10) + expectedBalanceWithFee0 := big.NewInt(0).Sub(expectedBalance0, txsFee) + + require.Equal(t, expectedBalanceWithFee0.String(), account0.Balance) + + account1, err = cs.GetAccount(addrs[1]) + require.Nil(t, err) + + beforeBalance1, _ := big.NewInt(0).SetString(beforeBalanceStr1, 10) + expectedBalance1 := big.NewInt(0).Add(beforeBalance1, egldValue) + + require.Equal(t, expectedBalance1.String(), account1.Balance) +} + +func TestChainSimulator_EGLD_MultiTransfer_Insufficient_Funds(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpoch := uint32(4) + + baseIssuingCost := "1000" + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.EGLDInMultiTransferEnableEpoch = activationEpoch + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + // issue NFT + nftTicker := []byte("NFTTICKER") + nonce := uint64(0) + tx := issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + account0, err := cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalanceStr0 := account0.Balance + + account1, err := cs.GetAccount(addrs[1]) + require.Nil(t, err) + + beforeBalanceStr1 := account1.Balance + + egldValue, _ := big.NewInt(0).SetString(beforeBalanceStr0, 10) + egldValue = egldValue.Add(egldValue, big.NewInt(13)) + tx = multiESDTNFTTransferWithEGLDTx(nonce, addrs[0].Bytes, addrs[1].Bytes, [][]byte{nftTokenID}, egldValue) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.NotEqual(t, "success", txResult.Status.String()) + + eventLog := string(txResult.Logs.Events[0].Topics[1]) + require.Equal(t, "insufficient funds for token EGLD-000000", eventLog) + + // check accounts balance + account0, err = cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalance0, _ := big.NewInt(0).SetString(beforeBalanceStr0, 10) + + txsFee, _ := big.NewInt(0).SetString(txResult.Fee, 10) + expectedBalanceWithFee0 := big.NewInt(0).Sub(beforeBalance0, txsFee) + + require.Equal(t, expectedBalanceWithFee0.String(), account0.Balance) + + account1, err = cs.GetAccount(addrs[1]) + require.Nil(t, err) + + require.Equal(t, beforeBalanceStr1, account1.Balance) +} + +func TestChainSimulator_EGLD_MultiTransfer_Invalid_Value(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpoch := uint32(4) + + baseIssuingCost := "1000" + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.EGLDInMultiTransferEnableEpoch = activationEpoch + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + // issue NFT + nftTicker := []byte("NFTTICKER") + nonce := uint64(0) + tx := issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + account0, err := cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalanceStr0 := account0.Balance + + account1, err := cs.GetAccount(addrs[1]) + require.Nil(t, err) + + beforeBalanceStr1 := account1.Balance + + egldValue := oneEGLD.Mul(oneEGLD, big.NewInt(3)) + tx = multiESDTNFTTransferWithEGLDTx(nonce, addrs[0].Bytes, addrs[1].Bytes, [][]byte{nftTokenID}, egldValue) + tx.Value = egldValue // invalid value field + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.NotEqual(t, "success", txResult.Status.String()) + + eventLog := string(txResult.Logs.Events[0].Topics[1]) + require.Equal(t, "built in function called with tx value is not allowed", eventLog) + + // check accounts balance + account0, err = cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalance0, _ := big.NewInt(0).SetString(beforeBalanceStr0, 10) + + txsFee, _ := big.NewInt(0).SetString(txResult.Fee, 10) + expectedBalanceWithFee0 := big.NewInt(0).Sub(beforeBalance0, txsFee) + + require.Equal(t, expectedBalanceWithFee0.String(), account0.Balance) + + account1, err = cs.GetAccount(addrs[1]) + require.Nil(t, err) + + require.Equal(t, beforeBalanceStr1, account1.Balance) +} + +func TestChainSimulator_Multiple_EGLD_Transfers(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpoch := uint32(4) + + baseIssuingCost := "1000" + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.EGLDInMultiTransferEnableEpoch = activationEpoch + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + // issue NFT + nftTicker := []byte("NFTTICKER") + nonce := uint64(0) + tx := issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + account0, err := cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalanceStr0 := account0.Balance + + account1, err := cs.GetAccount(addrs[1]) + require.Nil(t, err) + + beforeBalanceStr1 := account1.Balance + + // multi nft transfer with multiple EGLD-000000 tokens + numTransfers := 3 + encodedReceiver := hex.EncodeToString(addrs[1].Bytes) + egldValue := oneEGLD.Mul(oneEGLD, big.NewInt(3)) + + txDataField := []byte(strings.Join( + []string{ + core.BuiltInFunctionMultiESDTNFTTransfer, + encodedReceiver, + hex.EncodeToString(big.NewInt(int64(numTransfers)).Bytes()), + hex.EncodeToString([]byte("EGLD-000000")), + "00", + hex.EncodeToString(egldValue.Bytes()), + hex.EncodeToString(nftTokenID), + hex.EncodeToString(big.NewInt(1).Bytes()), + hex.EncodeToString(big.NewInt(int64(1)).Bytes()), + hex.EncodeToString([]byte("EGLD-000000")), + "00", + hex.EncodeToString(egldValue.Bytes()), + }, "@"), + ) + + tx = &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: addrs[0].Bytes, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Data: txDataField, + Value: big.NewInt(0), + Version: 1, + Signature: []byte("dummySig"), + ChainID: []byte(configs.ChainID), + } + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + // check accounts balance + account0, err = cs.GetAccount(addrs[0]) + require.Nil(t, err) + + beforeBalance0, _ := big.NewInt(0).SetString(beforeBalanceStr0, 10) + + expectedBalance0 := big.NewInt(0).Sub(beforeBalance0, egldValue) + expectedBalance0 = big.NewInt(0).Sub(expectedBalance0, egldValue) + txsFee, _ := big.NewInt(0).SetString(txResult.Fee, 10) + expectedBalanceWithFee0 := big.NewInt(0).Sub(expectedBalance0, txsFee) + + require.Equal(t, expectedBalanceWithFee0.String(), account0.Balance) + + account1, err = cs.GetAccount(addrs[1]) + require.Nil(t, err) + + beforeBalance1, _ := big.NewInt(0).SetString(beforeBalanceStr1, 10) + expectedBalance1 := big.NewInt(0).Add(beforeBalance1, egldValue) + expectedBalance1 = big.NewInt(0).Add(expectedBalance1, egldValue) + + require.Equal(t, expectedBalance1.String(), account1.Balance) +} + +func multiESDTNFTTransferWithEGLDTx(nonce uint64, sndAdr, rcvAddr []byte, tokens [][]byte, egldValue *big.Int) *transaction.Transaction { + transferData := make([]*utils.TransferESDTData, 0) + + for _, tokenID := range tokens { + transferData = append(transferData, &utils.TransferESDTData{ + Token: tokenID, + Nonce: 1, + Value: big.NewInt(1), + }) + } + + numTransfers := len(tokens) + encodedReceiver := hex.EncodeToString(rcvAddr) + hexEncodedNumTransfers := hex.EncodeToString(big.NewInt(int64(numTransfers)).Bytes()) + hexEncodedEGLD := hex.EncodeToString([]byte("EGLD-000000")) + hexEncodedEGLDNonce := "00" + + txDataField := []byte(strings.Join( + []string{ + core.BuiltInFunctionMultiESDTNFTTransfer, + encodedReceiver, + hexEncodedNumTransfers, + hexEncodedEGLD, + hexEncodedEGLDNonce, + hex.EncodeToString(egldValue.Bytes()), + }, "@"), + ) + + for _, td := range transferData { + hexEncodedToken := hex.EncodeToString(td.Token) + esdtValueEncoded := hex.EncodeToString(td.Value.Bytes()) + hexEncodedNonce := "00" + if td.Nonce != 0 { + hexEncodedNonce = hex.EncodeToString(big.NewInt(int64(td.Nonce)).Bytes()) + } + + txDataField = []byte(strings.Join([]string{string(txDataField), hexEncodedToken, hexEncodedNonce, esdtValueEncoded}, "@")) + } + + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: sndAdr, + RcvAddr: sndAdr, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Data: txDataField, + Value: big.NewInt(0), + Version: 1, + Signature: []byte("dummySig"), + ChainID: []byte(configs.ChainID), + } + + return tx +} + +func TestChainSimulator_IssueToken_EGLDTicker(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpoch := uint32(4) + + baseIssuingCost := "1000" + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.EGLDInMultiTransferEnableEpoch = activationEpoch + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 1) + require.Nil(t, err) + + log.Info("Initial setup: Issue token (before the activation of EGLDInMultiTransferFlag)") + + // issue NFT + nftTicker := []byte("EGLD") + nonce := uint64(0) + tx := issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + log.Info("Issue token (after activation of EGLDInMultiTransferFlag)") + + // should fail issuing token with EGLD ticker + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + errMessage := string(txResult.Logs.Events[0].Topics[1]) + require.Equal(t, vm.ErrCouldNotCreateNewTokenIdentifier.Error(), errMessage) + + require.Equal(t, "success", txResult.Status.String()) +} diff --git a/integrationTests/chainSimulator/vm/esdtImprovements_test.go b/integrationTests/chainSimulator/vm/esdtImprovements_test.go index 0dab09a20d9..254eaa56303 100644 --- a/integrationTests/chainSimulator/vm/esdtImprovements_test.go +++ b/integrationTests/chainSimulator/vm/esdtImprovements_test.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "math/big" + "strings" "testing" "time" @@ -116,11 +117,13 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 1) require.Nil(t, err) - log.Info("Initial setup: Create fungible, NFT, SFT and metaESDT tokens (before the activation of DynamicEsdtFlag)") + log.Info("Initial setup: Create NFT, SFT and metaESDT tokens (before the activation of DynamicEsdtFlag)") // issue metaESDT - metaESDTTicker := []byte("METATTICKER") - tx := issueMetaESDTTx(0, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -133,27 +136,24 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran []byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleTransfer), } - setAddressEsdtRoles(t, cs, addrs[0], metaESDTTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], metaESDTTokenID, roles) + nonce++ - log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) - - // issue fungible - fungibleTicker := []byte("FUNTICKER") - tx = issueTx(1, addrs[0].Bytes, fungibleTicker, baseIssuingCost) + rolesTransfer := [][]byte{[]byte(core.ESDTRoleTransfer)} + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[1].Bytes, metaESDTTokenID, rolesTransfer) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - fungibleTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], fungibleTokenID, roles) - - log.Info("Issued fungible token id", "tokenID", string(fungibleTokenID)) + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) // issue NFT nftTicker := []byte("NFTTICKER") - tx = issueNonFungibleTx(2, addrs[0].Bytes, nftTicker, baseIssuingCost) + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -161,13 +161,23 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran require.Equal(t, "success", txResult.Status.String()) nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[1].Bytes, nftTokenID, rolesTransfer) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) // issue SFT sftTicker := []byte("SFTTICKER") - tx = issueSemiFungibleTx(3, addrs[0].Bytes, sftTicker, baseIssuingCost) + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -175,7 +185,16 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran require.Equal(t, "success", txResult.Status.String()) sftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], sftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], sftTokenID, roles) + nonce++ + + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[1].Bytes, sftTokenID, rolesTransfer) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) @@ -188,26 +207,20 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran esdtMetaData := txsFee.GetDefaultMetaData() esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - fungibleMetaData := txsFee.GetDefaultMetaData() - fungibleMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tokenIDs := [][]byte{ nftTokenID, sftTokenID, metaESDTTokenID, - fungibleTokenID, } tokensMetadata := []*txsFee.MetaData{ nftMetaData, sftMetaData, esdtMetaData, - fungibleMetaData, } - nonce := uint64(4) for i := range tokenIDs { - tx = nftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i]) + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -228,7 +241,6 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData) checkMetaData(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID, esdtMetaData) - checkMetaData(t, cs, core.SystemAccountAddress, fungibleTokenID, shardID, fungibleMetaData) log.Info("Step 2. wait for DynamicEsdtFlag activation") @@ -271,7 +283,6 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData) checkMetaData(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID, esdtMetaData) - checkMetaData(t, cs, core.SystemAccountAddress, fungibleTokenID, shardID, fungibleMetaData) log.Info("Step 5. make an updateTokenID@tokenID function call on the ESDTSystem SC for all token types") @@ -296,21 +307,30 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData) checkMetaData(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID, esdtMetaData) - checkMetaData(t, cs, core.SystemAccountAddress, fungibleTokenID, shardID, fungibleMetaData) log.Info("Step 7. transfer the tokens to another account") nonce = uint64(0) - for _, tokenID := range tokenIDs { - log.Info("transfering token id", "tokenID", tokenID) + if isMultiTransfer { + tx = multiESDTNFTTransferTx(nonce, addrs[1].Bytes, addrs[2].Bytes, tokenIDs) - tx = esdtNFTTransferTx(nonce, addrs[1].Bytes, addrs[2].Bytes, tokenID) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + } else { + for _, tokenID := range tokenIDs { + log.Info("transfering token id", "tokenID", tokenID) - nonce++ + tx = esdtNFTTransferTx(nonce, addrs[1].Bytes, addrs[2].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + } } log.Info("Step 8. check that the metaData for the NFT was removed from the system account and moved to the user account") @@ -330,9 +350,6 @@ func transferAndCheckTokensMetaData(t *testing.T, isCrossShard bool, isMultiTran checkMetaData(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID, esdtMetaData) checkMetaDataNotInAcc(t, cs, addrs[2].Bytes, metaESDTTokenID, shardID) - - checkMetaData(t, cs, core.SystemAccountAddress, fungibleTokenID, shardID, fungibleMetaData) - checkMetaDataNotInAcc(t, cs, addrs[2].Bytes, fungibleTokenID, shardID) } func createAddresses( @@ -359,6 +376,9 @@ func createAddresses( address3, err := cs.GenerateAndMintWalletAddress(shardIDs[2], mintValue) require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + return []dtos.WalletAddress{address, address2, address3} } @@ -382,6 +402,18 @@ func checkMetaData( require.Equal(t, expectedMetaData.Attributes, []byte(hex.EncodeToString(retrievedMetaData.Attributes))) } +func checkReservedField( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + addressBytes []byte, + tokenID []byte, + shardID uint32, + expectedReservedField []byte, +) { + esdtData := getESDTDataFromAcc(t, cs, addressBytes, tokenID, shardID) + require.Equal(t, expectedReservedField, esdtData.Reserved) +} + func checkMetaDataNotInAcc( t *testing.T, cs testsChainSimulator.ChainSimulator, @@ -580,19 +612,20 @@ func updateTokenIDTx(nonce uint64, sndAdr []byte, tokenID []byte) *transaction.T } } -func nftCreateTx( +func esdtNftCreateTx( nonce uint64, sndAdr []byte, tokenID []byte, metaData *txsFee.MetaData, + quantity int64, ) *transaction.Transaction { txDataField := bytes.Join( [][]byte{ []byte(core.BuiltInFunctionESDTNFTCreate), []byte(hex.EncodeToString(tokenID)), - []byte(hex.EncodeToString(big.NewInt(1).Bytes())), // quantity + []byte(hex.EncodeToString(big.NewInt(quantity).Bytes())), // quantity metaData.Name, - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), + metaData.Royalties, metaData.Hash, metaData.Attributes, metaData.Uris[0], @@ -616,6 +649,34 @@ func nftCreateTx( } } +func modifyCreatorTx( + nonce uint64, + sndAdr []byte, + tokenID []byte, +) *transaction.Transaction { + txDataField := bytes.Join( + [][]byte{ + []byte(core.ESDTModifyCreator), + []byte(hex.EncodeToString(tokenID)), + []byte(hex.EncodeToString(big.NewInt(1).Bytes())), + }, + []byte("@"), + ) + + return &transaction.Transaction{ + Nonce: nonce, + SndAddr: sndAdr, + RcvAddr: sndAdr, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, + } +} + func getESDTDataFromAcc( t *testing.T, cs testsChainSimulator.ChainSimulator, @@ -656,41 +717,62 @@ func getMetaDataFromAcc( return esdtData.TokenMetaData } +func setSpecialRoleTx( + nonce uint64, + sndAddr []byte, + address []byte, + token []byte, + roles [][]byte, +) *transaction.Transaction { + txDataBytes := [][]byte{ + []byte("setSpecialRole"), + []byte(hex.EncodeToString(token)), + []byte(hex.EncodeToString(address)), + } + + for _, role := range roles { + txDataBytes = append(txDataBytes, []byte(hex.EncodeToString(role))) + } + + txDataField := bytes.Join( + txDataBytes, + []byte("@"), + ) + + return &transaction.Transaction{ + Nonce: nonce, + SndAddr: sndAddr, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 60_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, + } +} + func setAddressEsdtRoles( t *testing.T, cs testsChainSimulator.ChainSimulator, + nonce uint64, address dtos.WalletAddress, token []byte, roles [][]byte, ) { - marshaller := cs.GetNodeHandler(0).GetCoreComponents().InternalMarshalizer() - - rolesKey := append([]byte(core.ProtectedKeyPrefix), append([]byte(core.ESDTRoleIdentifier), []byte(core.ESDTKeyIdentifier)...)...) - rolesKey = append(rolesKey, token...) - - rolesData := &esdt.ESDTRoles{ - Roles: roles, - } + tx := setSpecialRoleTx(nonce, address.Bytes, address.Bytes, token, roles) - rolesDataBytes, err := marshaller.Marshal(rolesData) + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) - keys := make(map[string]string) - keys[hex.EncodeToString(rolesKey)] = hex.EncodeToString(rolesDataBytes) - - err = cs.SetStateMultiple([]*dtos.AddressState{ - { - Address: address.Bech32, - Balance: "10000000000000000000000", - Pairs: keys, - }, - }) - require.Nil(t, err) + require.Equal(t, "success", txResult.Status.String()) } // Test scenario #3 // -// Initial setup: Create fungible, NFT, SFT and metaESDT tokens +// Initial setup: Create NFT, SFT and metaESDT tokens // (after the activation of DynamicEsdtFlag) // // 1. check that the metaData for the NFT was saved in the user account and not on the system account @@ -700,54 +782,20 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { t.Skip("this is not a short test") } - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } - - activationEpoch := uint32(2) - baseIssuingCost := "1000" - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) - + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() addrs := createAddresses(t, cs, false) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) - - err = cs.GenerateBlocks(10) - require.Nil(t, err) - - log.Info("Initial setup: Create fungible, NFT, SFT and metaESDT tokens (after the activation of DynamicEsdtFlag)") + log.Info("Initial setup: Create NFT, SFT and metaESDT tokens (after the activation of DynamicEsdtFlag)") // issue metaESDT - metaESDTTicker := []byte("METATTICKER") - tx := issueMetaESDTTx(0, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -760,27 +808,15 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { []byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleTransfer), } - setAddressEsdtRoles(t, cs, addrs[0], metaESDTTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], metaESDTTokenID, roles) + nonce++ log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) - // issue fungible - fungibleTicker := []byte("FUNTICKER") - tx = issueTx(1, addrs[0].Bytes, fungibleTicker, baseIssuingCost) - - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - - fungibleTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], fungibleTokenID, roles) - - log.Info("Issued fungible token id", "tokenID", string(fungibleTokenID)) - // issue NFT nftTicker := []byte("NFTTICKER") - tx = issueNonFungibleTx(2, addrs[0].Bytes, nftTicker, baseIssuingCost) + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -788,13 +824,15 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) // issue SFT sftTicker := []byte("SFTTICKER") - tx = issueSemiFungibleTx(3, addrs[0].Bytes, sftTicker, baseIssuingCost) + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -802,7 +840,8 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) sftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], sftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], sftTokenID, roles) + nonce++ log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) @@ -810,7 +849,6 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { nftTokenID, sftTokenID, metaESDTTokenID, - fungibleTokenID, } nftMetaData := txsFee.GetDefaultMetaData() @@ -822,19 +860,14 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { esdtMetaData := txsFee.GetDefaultMetaData() esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - fungibleMetaData := txsFee.GetDefaultMetaData() - fungibleMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tokensMetadata := []*txsFee.MetaData{ nftMetaData, sftMetaData, esdtMetaData, - fungibleMetaData, } - nonce := uint64(4) for i := range tokenIDs { - tx = nftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i]) + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -861,568 +894,459 @@ func TestChainSimulator_CreateTokensAfterActivation(t *testing.T) { checkMetaData(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID, esdtMetaData) checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, metaESDTTokenID, shardID) - - checkMetaData(t, cs, core.SystemAccountAddress, fungibleTokenID, shardID, fungibleMetaData) - checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, fungibleTokenID, shardID) } // Test scenario #4 // -// Initial setup: Create NFT +// Initial setup: Create NFT, SFT, metaESDT tokens // // Call ESDTMetaDataRecreate to rewrite the meta data for the nft // (The sender must have the ESDTMetaDataRecreate role) -func TestChainSimulator_NFT_ESDTMetaDataRecreate(t *testing.T) { +func TestChainSimulator_ESDTMetaDataRecreate(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } - - activationEpoch := uint32(2) - baseIssuingCost := "1000" - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) - + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() - mintValue := big.NewInt(10) - mintValue = mintValue.Mul(oneEGLD, mintValue) - - address, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) - require.Nil(t, err) - - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) - - err = cs.GenerateBlocks(10) - require.Nil(t, err) + log.Info("Initial setup: Create NFT, SFT and metaESDT tokens (after the activation of DynamicEsdtFlag)") - log.Info("Initial setup: Create NFT") + addrs := createAddresses(t, cs, false) - nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, address.Bytes, nftTicker, baseIssuingCost) + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTRecreate), } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, metaESDTTokenID, roles) + nonce++ - nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, address, nftTokenID, roles) - - log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) - tx = nftCreateTx(1, address.Bytes, nftTokenID, nftMetaData) + // issue NFT + nftTicker := []byte("NFTTICKER") + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocks(10) + nftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, nftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - log.Info("Call ESDTMetaDataRecreate to rewrite the meta data for the nft") + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - nonce := []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - nftMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) - nftMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) - nftMetaData.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ - txDataField := bytes.Join( - [][]byte{ - []byte(core.ESDTMetaDataRecreate), - []byte(hex.EncodeToString(nftTokenID)), - nonce, - nftMetaData.Name, - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), - nftMetaData.Hash, - nftMetaData.Attributes, - nftMetaData.Uris[0], - nftMetaData.Uris[1], - nftMetaData.Uris[2], - }, - []byte("@"), - ) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - tx = &transaction.Transaction{ - Nonce: 2, - SndAddr: address.Bytes, - RcvAddr: address.Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, - } + sftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, sftTokenID, roles) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(address.Bytes) + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) - checkMetaData(t, cs, address.Bytes, nftTokenID, shardID, nftMetaData) - require.Equal(t, core.ESDTMetaDataRecreate, txResult.Logs.Events[0].Identifier) -} + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } -// Test scenario #5 -// -// Initial setup: Create NFT -// -// Call ESDTMetaDataUpdate to update some of the meta data parameters -// (The sender must have the ESDTRoleNFTUpdate role) -func TestChainSimulator_NFT_ESDTMetaDataUpdate(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - activationEpoch := uint32(2) + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - baseIssuingCost := "1000" + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) - defer cs.Close() + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - mintValue := big.NewInt(10) - mintValue = mintValue.Mul(oneEGLD, mintValue) + nonce++ - address, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) - require.Nil(t, err) + tx = changeToDynamicTx(nonce, addrs[0].Bytes, tokenIDs[i]) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocks(10) - require.Nil(t, err) + nonce++ - log.Info("Initial setup: Create NFT") + roles := [][]byte{ + []byte(core.ESDTRoleNFTRecreate), + } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, tokenIDs[i], roles) - nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, address.Bytes, nftTicker, baseIssuingCost) + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + require.Equal(t, "success", txResult.Status.String()) - roles := [][]byte{ - []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTUpdate), + nonce++ } - nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, address, nftTokenID, roles) - - log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - - tx = nftCreateTx(1, address.Bytes, nftTokenID, nftMetaData) - - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + err = cs.GenerateBlocks(10) require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - log.Info("Call ESDTMetaDataUpdate to rewrite the meta data for the nft") - - nonce := []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - nftMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) - nftMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) - nftMetaData.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) + log.Info("Call ESDTMetaDataRecreate to rewrite the meta data for the nft") - txDataField := bytes.Join( - [][]byte{ - []byte(core.ESDTMetaDataUpdate), - []byte(hex.EncodeToString(nftTokenID)), - nonce, - nftMetaData.Name, - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), - nftMetaData.Hash, - nftMetaData.Attributes, - nftMetaData.Uris[0], - nftMetaData.Uris[1], - nftMetaData.Uris[2], - }, - []byte("@"), - ) + for i := range tokenIDs { + newMetaData := txsFee.GetDefaultMetaData() + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) + + txDataField := bytes.Join( + [][]byte{ + []byte(core.ESDTMetaDataRecreate), + []byte(hex.EncodeToString(tokenIDs[i])), + newMetaData.Nonce, + newMetaData.Name, + []byte(hex.EncodeToString(big.NewInt(10).Bytes())), + newMetaData.Hash, + newMetaData.Attributes, + newMetaData.Uris[0], + newMetaData.Uris[1], + newMetaData.Uris[2], + }, + []byte("@"), + ) + + tx = &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: addrs[0].Bytes, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, + } - tx = &transaction.Transaction{ - Nonce: 2, - SndAddr: address.Bytes, - RcvAddr: address.Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, - } + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - require.Equal(t, "success", txResult.Status.String()) + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(address.Bytes) + if bytes.Equal(tokenIDs[i], tokenIDs[0]) { // nft token + checkMetaData(t, cs, addrs[0].Bytes, tokenIDs[i], shardID, newMetaData) + } else { + checkMetaData(t, cs, core.SystemAccountAddress, tokenIDs[i], shardID, newMetaData) + } - checkMetaData(t, cs, address.Bytes, nftTokenID, shardID, nftMetaData) - require.Equal(t, core.ESDTMetaDataUpdate, txResult.Logs.Events[0].Identifier) + nonce++ + } } -// Test scenario #6 +// Test scenario #5 // -// Initial setup: Create SFT +// Initial setup: Create NFT, SFT, metaESDT tokens // -// Call ESDTModifyCreator and check that the creator was modified -// (The sender must have the ESDTRoleModifyCreator role) -func TestChainSimulator_NFT_ESDTModifyCreator(t *testing.T) { +// Call ESDTMetaDataUpdate to update some of the meta data parameters +// (The sender must have the ESDTRoleNFTUpdate role) +func TestChainSimulator_ESDTMetaDataUpdate(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } - - activationEpoch := uint32(4) - baseIssuingCost := "1000" - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) - + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() - mintValue := big.NewInt(10) - mintValue = mintValue.Mul(oneEGLD, mintValue) - - shardID := uint32(1) - address, err := cs.GenerateAndMintWalletAddress(shardID, mintValue) - require.Nil(t, err) - - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 2) - require.Nil(t, err) - - err = cs.GenerateBlocks(10) - require.Nil(t, err) + log.Info("Initial setup: Create NFT, SFT and metaESDT tokens (after the activation of DynamicEsdtFlag)") - log.Info("Initial setup: Create SFT") + addrs := createAddresses(t, cs, false) - sftTicker := []byte("SFTTICKER") - tx := issueSemiFungibleTx(0, address.Bytes, sftTicker, baseIssuingCost) + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTUpdate), + []byte(core.ESDTRoleTransfer), } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, metaESDTTokenID, roles) + nonce++ - sft := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, address, sft, roles) - - log.Info("Issued SFT token id", "tokenID", string(sft)) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) - tx = nftCreateTx(1, address.Bytes, sft, nftMetaData) + // issue NFT + nftTicker := []byte("NFTTICKER") + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + nftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, nftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - log.Info("Change to DYNAMIC type") + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - tx = changeToDynamicTx(2, address.Bytes, sft) + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - log.Info("Call ESDTModifyCreator and check that the creator was modified") + sftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, sftTokenID, roles) + nonce++ - newCreatorAddress, err := cs.GenerateAndMintWalletAddress(shardID, mintValue) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocks(10) - require.Nil(t, err) + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) - roles = [][]byte{ - []byte(core.ESDTRoleModifyCreator), + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, } - setAddressEsdtRoles(t, cs, newCreatorAddress, sft, roles) - txDataField := bytes.Join( - [][]byte{ - []byte(core.ESDTModifyCreator), - []byte(hex.EncodeToString(sft)), - []byte(hex.EncodeToString(big.NewInt(1).Bytes())), - }, - []byte("@"), - ) + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tx = &transaction.Transaction{ - Nonce: 0, - SndAddr: newCreatorAddress.Bytes, - RcvAddr: newCreatorAddress.Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, } - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) - require.Equal(t, "success", txResult.Status.String()) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - retrievedMetaData := getMetaDataFromAcc(t, cs, core.SystemAccountAddress, sft, shardID) + require.Equal(t, "success", txResult.Status.String()) - require.Equal(t, newCreatorAddress.Bytes, retrievedMetaData.Creator) - require.Equal(t, core.ESDTModifyCreator, txResult.Logs.Events[0].Identifier) -} + nonce++ -// Test scenario #7 -// -// Initial setup: Create NFT -// -// Call ESDTSetNewURIs and check that the new URIs were set for the NFT -// (The sender must have the ESDTRoleSetNewURI role) -func TestChainSimulator_NFT_ESDTSetNewURIs(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } + tx = changeToDynamicTx(nonce, addrs[0].Bytes, tokenIDs[i]) - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + + roles := [][]byte{ + []byte(core.ESDTRoleNFTUpdate), + } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, tokenIDs[i], roles) + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ } - activationEpoch := uint32(2) + log.Info("Call ESDTMetaDataUpdate to rewrite the meta data for the nft") - baseIssuingCost := "1000" + for i := range tokenIDs { + newMetaData := txsFee.GetDefaultMetaData() + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) + tx = esdtMetaDataUpdateTx(tokenIDs[i], newMetaData, nonce, addrs[0].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - defer cs.Close() + require.Equal(t, "success", txResult.Status.String()) - mintValue := big.NewInt(10) - mintValue = mintValue.Mul(oneEGLD, mintValue) + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - address, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) - require.Nil(t, err) + if bytes.Equal(tokenIDs[i], tokenIDs[0]) { // nft token + checkMetaData(t, cs, addrs[0].Bytes, tokenIDs[i], shardID, newMetaData) + } else { + checkMetaData(t, cs, core.SystemAccountAddress, tokenIDs[i], shardID, newMetaData) + } - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) + nonce++ + } +} - err = cs.GenerateBlocks(10) - require.Nil(t, err) +// Test scenario #6 +// +// Initial setup: Create NFT, SFT, metaESDT tokens +// +// Call ESDTModifyCreator and check that the creator was modified +// (The sender must have the ESDTRoleModifyCreator role) +func TestChainSimulator_ESDTModifyCreator(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } - log.Info("Initial setup: Create NFT") + baseIssuingCost := "1000" - nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, address.Bytes, nftTicker, baseIssuingCost) + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + log.Info("Initial setup: Create NFT, SFT and metaESDT tokens (after the activation of DynamicEsdtFlag). Register NFT directly as dynamic") + + addrs := createAddresses(t, cs, false) + + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[1].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTUpdate), + []byte(core.ESDTRoleTransfer), } - - nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, address, nftTokenID, roles) - - log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - - tx = nftCreateTx(1, address.Bytes, nftTokenID, nftMetaData) + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[1].Bytes, metaESDTTokenID, roles) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - - log.Info("Call ESDTSetNewURIs and check that the new URIs were set for the NFT") - roles = [][]byte{ - []byte(core.ESDTRoleSetNewURI), - } - setAddressEsdtRoles(t, cs, address, nftTokenID, roles) + require.Equal(t, "success", txResult.Status.String()) - nonce := []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - uris := [][]byte{ - []byte(hex.EncodeToString([]byte("uri0"))), - []byte(hex.EncodeToString([]byte("uri1"))), - []byte(hex.EncodeToString([]byte("uri2"))), - } + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) - expUris := [][]byte{ - []byte("uri0"), - []byte("uri1"), - []byte("uri2"), - } + // register dynamic NFT + nftTicker := []byte("NFTTICKER") + nftTokenName := []byte("tokenName") txDataField := bytes.Join( [][]byte{ - []byte(core.ESDTSetNewURIs), - []byte(hex.EncodeToString(nftTokenID)), - nonce, - uris[0], - uris[1], - uris[2], + []byte("registerDynamic"), + []byte(hex.EncodeToString(nftTokenName)), + []byte(hex.EncodeToString(nftTicker)), + []byte(hex.EncodeToString([]byte("NFT"))), }, []byte("@"), ) + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + tx = &transaction.Transaction{ - Nonce: 2, - SndAddr: address.Bytes, - RcvAddr: address.Bytes, - GasLimit: 10_000_000, + Nonce: nonce, + SndAddr: addrs[1].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, GasPrice: minGasPrice, Signature: []byte("dummySig"), Data: txDataField, - Value: big.NewInt(0), + Value: callValue, ChainID: []byte(configs.ChainID), Version: 1, } + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -1430,159 +1354,1840 @@ func TestChainSimulator_NFT_ESDTSetNewURIs(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(address.Bytes) - retrievedMetaData := getMetaDataFromAcc(t, cs, address.Bytes, nftTokenID, shardID) - - require.Equal(t, expUris, retrievedMetaData.URIs) - require.Equal(t, core.ESDTSetNewURIs, txResult.Logs.Events[0].Identifier) -} - -// Test scenario #8 -// -// Initial setup: Create NFT -// -// Call ESDTModifyRoyalties and check that the royalties were changed -// (The sender must have the ESDTRoleModifyRoyalties role) -func TestChainSimulator_NFT_ESDTModifyRoyalties(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } - - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } - - activationEpoch := uint32(2) - - baseIssuingCost := "1000" + nftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[1].Bytes, nftTokenID, roles) + nonce++ - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.NotNil(t, cs) - - defer cs.Close() - - mintValue := big.NewInt(10) - mintValue = mintValue.Mul(oneEGLD, mintValue) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - address, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue) - require.Nil(t, err) + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[1].Bytes, sftTicker, baseIssuingCost) + nonce++ - err = cs.GenerateBlocks(10) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - log.Info("Initial setup: Create NFT") - - nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, address.Bytes, nftTicker, baseIssuingCost) + sftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[1].Bytes, sftTokenID, roles) + nonce++ - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - roles := [][]byte{ - []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTUpdate), - } - - nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, address, nftTokenID, roles) + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) - log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } nftMetaData := txsFee.GetDefaultMetaData() nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tx = nftCreateTx(1, address.Bytes, nftTokenID, nftMetaData) + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - log.Info("Call ESDTModifyRoyalties and check that the royalties were changed") + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } - roles = [][]byte{ - []byte(core.ESDTRoleModifyRoyalties), + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[1].Bytes, tokenIDs[i], tokensMetadata[i], 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ } - setAddressEsdtRoles(t, cs, address, nftTokenID, roles) - nonce := []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - royalties := []byte(hex.EncodeToString(big.NewInt(20).Bytes())) + log.Info("Change to DYNAMIC type") - txDataField := bytes.Join( - [][]byte{ - []byte(core.ESDTModifyRoyalties), - []byte(hex.EncodeToString(nftTokenID)), - nonce, - royalties, - }, - []byte("@"), - ) + for i := range tokenIDs { + tx = changeToDynamicTx(nonce, addrs[1].Bytes, tokenIDs[i]) - tx = &transaction.Transaction{ - Nonce: 2, - SndAddr: address.Bytes, - RcvAddr: address.Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ } - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + log.Info("Call ESDTModifyCreator and check that the creator was modified") - require.Equal(t, "success", txResult.Status.String()) + mintValue := big.NewInt(10) + mintValue = mintValue.Mul(oneEGLD, mintValue) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[1].Bytes) + + for i := range tokenIDs { + log.Info("Modify creator for token", "tokenID", tokenIDs[i]) + + newCreatorAddress, err := cs.GenerateAndMintWalletAddress(shardID, mintValue) + require.Nil(t, err) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + roles = [][]byte{ + []byte(core.ESDTRoleModifyCreator), + } + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, newCreatorAddress.Bytes, tokenIDs[i], roles) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + tx = modifyCreatorTx(0, newCreatorAddress.Bytes, tokenIDs[i]) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + retrievedMetaData := &esdt.MetaData{} + if bytes.Equal(tokenIDs[i], nftTokenID) { + retrievedMetaData = getMetaDataFromAcc(t, cs, newCreatorAddress.Bytes, tokenIDs[i], shardID) + } else { + retrievedMetaData = getMetaDataFromAcc(t, cs, core.SystemAccountAddress, tokenIDs[i], shardID) + } + + require.Equal(t, newCreatorAddress.Bytes, retrievedMetaData.Creator) + } +} + +func TestChainSimulator_ESDTModifyCreator_CrossShard(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[1].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[1].Bytes, metaESDTTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) + + // register dynamic NFT + nftTicker := []byte("NFTTICKER") + nftTokenName := []byte("tokenName") + + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(nftTokenName)), + []byte(hex.EncodeToString(nftTicker)), + []byte(hex.EncodeToString([]byte("NFT"))), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + tx = &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[1].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[1].Bytes, nftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[1].Bytes, sftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + sftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[1].Bytes, sftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) + + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } + + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[1].Bytes, tokenIDs[i], tokensMetadata[i], 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + } + + log.Info("Change to DYNAMIC type") + + for i := range tokenIDs { + tx = changeToDynamicTx(nonce, addrs[1].Bytes, tokenIDs[i]) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + } + + log.Info("Call ESDTModifyCreator and check that the creator was modified") + + mintValue := big.NewInt(10) + mintValue = mintValue.Mul(oneEGLD, mintValue) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[1].Bytes) + + crossShardID := uint32(2) + if shardID == uint32(2) { + crossShardID = uint32(1) + } + + for i := range tokenIDs { + log.Info("Modify creator for token", "tokenID", string(tokenIDs[i])) + + newCreatorAddress, err := cs.GenerateAndMintWalletAddress(crossShardID, mintValue) + require.Nil(t, err) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + roles = [][]byte{ + []byte(core.ESDTRoleModifyCreator), + } + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, newCreatorAddress.Bytes, tokenIDs[i], roles) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("transfering token id", "tokenID", tokenIDs[i]) + + tx = esdtNFTTransferTx(nonce, addrs[1].Bytes, newCreatorAddress.Bytes, tokenIDs[i]) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + tx = modifyCreatorTx(0, newCreatorAddress.Bytes, tokenIDs[i]) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + retrievedMetaData := &esdt.MetaData{} + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(newCreatorAddress.Bytes) + if bytes.Equal(tokenIDs[i], nftTokenID) { + retrievedMetaData = getMetaDataFromAcc(t, cs, newCreatorAddress.Bytes, tokenIDs[i], shardID) + } else { + retrievedMetaData = getMetaDataFromAcc(t, cs, core.SystemAccountAddress, tokenIDs[i], shardID) + } + + require.Equal(t, newCreatorAddress.Bytes, retrievedMetaData.Creator) + + nonce++ + } +} + +// Test scenario #7 +// +// Initial setup: Create NFT, SFT, metaESDT tokens +// +// Call ESDTSetNewURIs and check that the new URIs were set for the token +// (The sender must have the ESDTRoleSetNewURI role) +func TestChainSimulator_ESDTSetNewURIs(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, metaESDTTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) + + // issue NFT + nftTicker := []byte("NFTTICKER") + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, nftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + sftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, sftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) + + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } + + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + + tx = changeToDynamicTx(nonce, addrs[0].Bytes, tokenIDs[i]) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + + roles := [][]byte{ + []byte(core.ESDTRoleSetNewURI), + } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, tokenIDs[i], roles) + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + } + + log.Info("Call ESDTSetNewURIs and check that the new URIs were set for the tokens") + + metaDataNonce := []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + uris := [][]byte{ + []byte(hex.EncodeToString([]byte("uri0"))), + []byte(hex.EncodeToString([]byte("uri1"))), + []byte(hex.EncodeToString([]byte("uri2"))), + } + + expUris := [][]byte{ + []byte("uri0"), + []byte("uri1"), + []byte("uri2"), + } + + for i := range tokenIDs { + log.Info("Set new uris for token", "tokenID", string(tokenIDs[i])) + + txDataField := bytes.Join( + [][]byte{ + []byte(core.ESDTSetNewURIs), + []byte(hex.EncodeToString(tokenIDs[i])), + metaDataNonce, + uris[0], + uris[1], + uris[2], + }, + []byte("@"), + ) + + tx = &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: addrs[0].Bytes, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, + } + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + var retrievedMetaData *esdt.MetaData + if bytes.Equal(tokenIDs[i], tokenIDs[0]) { // nft token + retrievedMetaData = getMetaDataFromAcc(t, cs, addrs[0].Bytes, tokenIDs[i], shardID) + } else { + retrievedMetaData = getMetaDataFromAcc(t, cs, core.SystemAccountAddress, tokenIDs[i], shardID) + } + + require.Equal(t, expUris, retrievedMetaData.URIs) + + nonce++ + } +} + +// Test scenario #8 +// +// Initial setup: Create NFT, SFT, metaESDT tokens +// +// Call ESDTModifyRoyalties and check that the royalties were changed +// (The sender must have the ESDTRoleModifyRoyalties role) +func TestChainSimulator_ESDTModifyRoyalties(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, metaESDTTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) + + // issue NFT + nftTicker := []byte("NFTTICKER") + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, nftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + sftTokenID := txResult.Logs.Events[0].Topics[0] + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, sftTokenID, roles) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) + + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } + + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + + tx = changeToDynamicTx(nonce, addrs[0].Bytes, tokenIDs[i]) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + + roles := [][]byte{ + []byte(core.ESDTRoleModifyRoyalties), + } + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[0].Bytes, tokenIDs[i], roles) + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + nonce++ + } + + log.Info("Call ESDTModifyRoyalties and check that the royalties were changed") + + metaDataNonce := []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + royalties := []byte(hex.EncodeToString(big.NewInt(20).Bytes())) + + for i := range tokenIDs { + log.Info("Set new royalties for token", "tokenID", string(tokenIDs[i])) + + txDataField := bytes.Join( + [][]byte{ + []byte(core.ESDTModifyRoyalties), + []byte(hex.EncodeToString(tokenIDs[i])), + metaDataNonce, + royalties, + }, + []byte("@"), + ) + + tx = &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: addrs[0].Bytes, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, + } + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + shardID := cs.GetNodeHandler(0).GetShardCoordinator().ComputeId(addrs[0].Bytes) + retrievedMetaData := getMetaDataFromAcc(t, cs, addrs[0].Bytes, nftTokenID, shardID) + + require.Equal(t, uint32(big.NewInt(20).Uint64()), retrievedMetaData.Royalties) + + nonce++ + } +} + +// Test scenario #9 +// +// Initial setup: Create NFT +// +// 1. Change the nft to DYNAMIC type - the metadata should be on the system account +// 2. Send the NFT cross shard +// 3. The meta data should still be present on the system account +func TestChainSimulator_NFT_ChangeToDynamicType(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpoch := uint32(4) + + baseIssuingCost := "1000" + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 2) + require.Nil(t, err) + + log.Info("Initial setup: Create NFT") + + nftTicker := []byte("NFTTICKER") + nonce := uint64(0) + tx := issueNonFungibleTx(nonce, addrs[1].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + } + + nftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[1], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[1].Bytes, nftTokenID, nftMetaData, 1) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + log.Info("Step 1. Change the nft to DYNAMIC type - the metadata should be on the system account") + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[1].Bytes) + + tx = changeToDynamicTx(nonce, addrs[1].Bytes, nftTokenID) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + roles = [][]byte{ + []byte(core.ESDTRoleNFTUpdate), + } + + setAddressEsdtRoles(t, cs, nonce, addrs[1], nftTokenID, roles) + nonce++ + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) + + log.Info("Step 2. Send the NFT cross shard") + + tx = esdtNFTTransferTx(nonce, addrs[1].Bytes, addrs[2].Bytes, nftTokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Step 3. The meta data should still be present on the system account") + + checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) +} + +// Test scenario #10 +// +// Initial setup: Create SFT and send in another shard +// +// 1. change the sft meta data (differently from the previous one) in the other shard +// 2. check that the newest metadata is saved +func TestChainSimulator_ChangeMetaData(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + t.Run("sft change metadata", func(t *testing.T) { + testChainSimulatorChangeMetaData(t, issueSemiFungibleTx) + }) + + t.Run("metaESDT change metadata", func(t *testing.T) { + testChainSimulatorChangeMetaData(t, issueMetaESDTTx) + }) +} + +type issueTxFunc func(uint64, []byte, []byte, string) *transaction.Transaction + +func testChainSimulatorChangeMetaData(t *testing.T, issueFn issueTxFunc) { + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Initial setup: Create token and send in another shard") + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTAddQuantity), + } + + ticker := []byte("TICKER") + nonce := uint64(0) + tx := issueFn(nonce, addrs[1].Bytes, ticker, baseIssuingCost) + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + tokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[1], tokenID, roles) + nonce++ + + log.Info("Issued token id", "tokenID", string(tokenID)) + + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[1].Bytes, tokenID, metaData, 2) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + tx = changeToDynamicTx(nonce, addrs[1].Bytes, tokenID) + nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + log.Info("Send to separate shards") + + tx = esdtNFTTransferTx(nonce, addrs[1].Bytes, addrs[2].Bytes, tokenID) + nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + tx = esdtNFTTransferTx(nonce, addrs[1].Bytes, addrs[0].Bytes, tokenID) + nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + roles = [][]byte{ + []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), + } + tx = setSpecialRoleTx(nonce, addrs[1].Bytes, addrs[0].Bytes, tokenID, roles) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + log.Info("Step 1. change the sft meta data in one shard") + + sftMetaData2 := txsFee.GetDefaultMetaData() + sftMetaData2.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + sftMetaData2.Name = []byte(hex.EncodeToString([]byte("name2"))) + sftMetaData2.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + sftMetaData2.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) + + tx = esdtMetaDataUpdateTx(tokenID, sftMetaData2, 0, addrs[0].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("Step 2. check that the newest metadata is saved") + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, shardID, sftMetaData2) + + shard2ID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[2].Bytes) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, shard2ID, metaData) + + log.Info("Step 3. create new wallet is shard 2") + + mintValue := big.NewInt(10) + mintValue = mintValue.Mul(oneEGLD, mintValue) + newShard2Addr, err := cs.GenerateAndMintWalletAddress(2, mintValue) + require.Nil(t, err) + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + log.Info("Step 4. send updated token to shard 2 ") + + tx = esdtNFTTransferTx(1, addrs[0].Bytes, newShard2Addr.Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + err = cs.GenerateBlocks(5) + require.Nil(t, err) + + log.Info("Step 5. check meta data in shard 2 is updated to latest version ") + + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, shard2ID, sftMetaData2) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, shardID, sftMetaData2) + +} + +func TestChainSimulator_NFT_RegisterDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic nft token") + + nftTicker := []byte("NFTTICKER") + nftTokenName := []byte("tokenName") + + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(nftTokenName)), + []byte(hex.EncodeToString(nftTicker)), + []byte(hex.EncodeToString([]byte("NFT"))), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + + checkMetaData(t, cs, addrs[0].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) + + log.Info("Check that token type is Dynamic") + + scQuery := &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + CallValue: big.NewInt(0), + Arguments: [][]byte{nftTokenID}, + } + result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + tokenType := result.ReturnData[1] + require.Equal(t, core.DynamicNFTESDT, string(tokenType)) +} + +func TestChainSimulator_MetaESDT_RegisterDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic metaESDT token") + + metaTicker := []byte("METATICKER") + metaTokenName := []byte("tokenName") + + decimals := big.NewInt(15) + + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(metaTokenName)), + []byte(hex.EncodeToString(metaTicker)), + []byte(hex.EncodeToString([]byte("META"))), + []byte(hex.EncodeToString(decimals.Bytes())), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + + checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) + + log.Info("Check that token type is Dynamic") + + scQuery := &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + CallValue: big.NewInt(0), + Arguments: [][]byte{nftTokenID}, + } + result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + tokenType := result.ReturnData[1] + require.Equal(t, core.Dynamic+core.MetaESDT, string(tokenType)) +} + +func TestChainSimulator_FNG_RegisterDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic fungible token") + + metaTicker := []byte("FNGTICKER") + metaTokenName := []byte("tokenName") + + decimals := big.NewInt(15) + + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(metaTokenName)), + []byte(hex.EncodeToString(metaTicker)), + []byte(hex.EncodeToString([]byte("FNG"))), + []byte(hex.EncodeToString(decimals.Bytes())), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + tx := &transaction.Transaction{ + Nonce: 0, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + signalErrorTopic := string(txResult.Logs.Events[0].Topics[1]) + + require.Equal(t, fmt.Sprintf("cannot create %s tokens as dynamic", core.FungibleESDT), signalErrorTopic) +} + +func TestChainSimulator_NFT_RegisterAndSetAllRolesDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic nft token") + + nftTicker := []byte("NFTTICKER") + nftTokenName := []byte("tokenName") + + txDataField := bytes.Join( + [][]byte{ + []byte("registerAndSetAllRolesDynamic"), + []byte(hex.EncodeToString(nftTokenName)), + []byte(hex.EncodeToString(nftTicker)), + []byte(hex.EncodeToString([]byte("NFT"))), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + + checkMetaData(t, cs, addrs[0].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) + + log.Info("Check that token type is Dynamic") + + scQuery := &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + CallValue: big.NewInt(0), + Arguments: [][]byte{nftTokenID}, + } + result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + tokenType := result.ReturnData[1] + require.Equal(t, core.DynamicNFTESDT, string(tokenType)) + + log.Info("Check token roles") + + scQuery = &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getAllAddressesAndRoles", + CallValue: big.NewInt(0), + Arguments: [][]byte{nftTokenID}, + } + result, _, err = cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + expectedRoles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTBurn), + []byte(core.ESDTRoleNFTUpdateAttributes), + []byte(core.ESDTRoleNFTAddURI), + []byte(core.ESDTRoleNFTRecreate), + []byte(core.ESDTRoleModifyCreator), + []byte(core.ESDTRoleModifyRoyalties), + []byte(core.ESDTRoleSetNewURI), + []byte(core.ESDTRoleNFTUpdate), + } + + checkTokenRoles(t, result.ReturnData, expectedRoles) +} + +func TestChainSimulator_SFT_RegisterAndSetAllRolesDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic sft token") + + sftTicker := []byte("SFTTICKER") + sftTokenName := []byte("tokenName") + + txDataField := bytes.Join( + [][]byte{ + []byte("registerAndSetAllRolesDynamic"), + []byte(hex.EncodeToString(sftTokenName)), + []byte(hex.EncodeToString(sftTicker)), + []byte(hex.EncodeToString([]byte("SFT"))), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + sftTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], sftTokenID, roles) + nonce++ + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, sftTokenID, nftMetaData, 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + + checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, nftMetaData) + + log.Info("Check that token type is Dynamic") + + scQuery := &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + CallValue: big.NewInt(0), + Arguments: [][]byte{sftTokenID}, + } + result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + tokenType := result.ReturnData[1] + require.Equal(t, core.DynamicSFTESDT, string(tokenType)) + + log.Info("Check token roles") + + scQuery = &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getAllAddressesAndRoles", + CallValue: big.NewInt(0), + Arguments: [][]byte{sftTokenID}, + } + result, _, err = cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + expectedRoles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTBurn), + []byte(core.ESDTRoleNFTUpdateAttributes), + []byte(core.ESDTRoleNFTAddURI), + []byte(core.ESDTRoleNFTRecreate), + []byte(core.ESDTRoleModifyCreator), + []byte(core.ESDTRoleModifyRoyalties), + []byte(core.ESDTRoleSetNewURI), + []byte(core.ESDTRoleNFTUpdate), + } + + checkTokenRoles(t, result.ReturnData, expectedRoles) +} + +func TestChainSimulator_FNG_RegisterAndSetAllRolesDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic fungible token") + + fngTicker := []byte("FNGTICKER") + fngTokenName := []byte("tokenName") + + txDataField := bytes.Join( + [][]byte{ + []byte("registerAndSetAllRolesDynamic"), + []byte(hex.EncodeToString(fngTokenName)), + []byte(hex.EncodeToString(fngTicker)), + []byte(hex.EncodeToString([]byte("FNG"))), + []byte(hex.EncodeToString(big.NewInt(10).Bytes())), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + tx := &transaction.Transaction{ + Nonce: 0, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + signalErrorTopic := string(txResult.Logs.Events[0].Topics[1]) + + require.Equal(t, fmt.Sprintf("cannot create %s tokens as dynamic", core.FungibleESDT), signalErrorTopic) +} + +func TestChainSimulator_MetaESDT_RegisterAndSetAllRolesDynamic(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, true) + + log.Info("Register dynamic meta esdt token") + + ticker := []byte("META" + "TICKER") + tokenName := []byte("tokenName") + + decimals := big.NewInt(10) + + txDataField := bytes.Join( + [][]byte{ + []byte("registerAndSetAllRolesDynamic"), + []byte(hex.EncodeToString(tokenName)), + []byte(hex.EncodeToString(ticker)), + []byte(hex.EncodeToString([]byte("META"))), + []byte(hex.EncodeToString(decimals.Bytes())), + }, + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + metaTokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], metaTokenID, roles) + nonce++ + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, metaTokenID, nftMetaData, 1) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + + checkMetaData(t, cs, core.SystemAccountAddress, metaTokenID, shardID, nftMetaData) + + log.Info("Check that token type is Dynamic") + + scQuery := &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getTokenProperties", + CallValue: big.NewInt(0), + Arguments: [][]byte{metaTokenID}, + } + result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + tokenType := result.ReturnData[1] + require.Equal(t, core.Dynamic+core.MetaESDT, string(tokenType)) + + log.Info("Check token roles") + + scQuery = &process.SCQuery{ + ScAddress: vm.ESDTSCAddress, + FuncName: "getAllAddressesAndRoles", + CallValue: big.NewInt(0), + Arguments: [][]byte{metaTokenID}, + } + result, _, err = cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "", result.ReturnMessage) + require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + + expectedRoles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTBurn), + []byte(core.ESDTRoleNFTAddQuantity), + []byte(core.ESDTRoleNFTUpdateAttributes), + []byte(core.ESDTRoleNFTAddURI), + } + + checkTokenRoles(t, result.ReturnData, expectedRoles) +} + +func checkTokenRoles(t *testing.T, returnData [][]byte, expectedRoles [][]byte) { + for _, expRole := range expectedRoles { + found := false + + for _, item := range returnData { + if bytes.Equal(expRole, item) { + found = true + } + } + + require.True(t, found) + } +} + +func TestChainSimulator_NFTcreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + log.Info("Initial setup: Create NFT that will have it's metadata saved to the user account") + + nftTicker := []byte("NFTTICKER") + tx := issueNonFungibleTx(0, addrs[0].Bytes, nftTicker, baseIssuingCost) + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + nftTokenID := txResult.Logs.Events[0].Topics[0] + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, nftTokenID, nftMetaData, epochForDynamicNFT, addrs[0]) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + checkMetaData(t, cs, addrs[1].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, nftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) +} + +func TestChainSimulator_SFTcreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + log.Info("Initial setup: Create SFT that will have it's metadata saved to the user account") + + sftTicker := []byte("SFTTICKER") + tx := issueSemiFungibleTx(0, addrs[0].Bytes, sftTicker, baseIssuingCost) + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + sftTokenID := txResult.Logs.Events[0].Topics[0] + + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) + + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, sftTokenID, metaData, epochForDynamicNFT, addrs[0]) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + + checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, metaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, sftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, sftTokenID, shardID) +} + +func TestChainSimulator_MetaESDTCreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + baseIssuingCost := "1000" + cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) + defer cs.Close() + + addrs := createAddresses(t, cs, false) + + log.Info("Initial setup: Create MetaESDT that will have it's metadata saved to the user account") + + metaTicker := []byte("METATICKER") + tx := issueMetaESDTTx(0, addrs[0].Bytes, metaTicker, baseIssuingCost) + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + metaTokenID := txResult.Logs.Events[0].Topics[0] + + log.Info("Issued MetaESDT token id", "tokenID", string(metaTokenID)) + + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + + createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, metaTokenID, metaData, epochForDynamicNFT, addrs[0]) + + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + checkMetaData(t, cs, core.SystemAccountAddress, metaTokenID, shardID, metaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, metaTokenID, shardID) + checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, metaTokenID, shardID) +} + +func getTestChainSimulatorWithDynamicNFTEnabled(t *testing.T, baseIssuingCost string) (testsChainSimulator.ChainSimulator, int32) { + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + + activationEpochForDynamicNFT := uint32(2) + + numOfShards := uint32(3) + cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: numOfShards, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 3, + MetaChainMinNodes: 3, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpochForDynamicNFT + cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) - shardID := cs.GetNodeHandler(0).GetShardCoordinator().ComputeId(address.Bytes) - retrievedMetaData := getMetaDataFromAcc(t, cs, address.Bytes, nftTokenID, shardID) + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpochForDynamicNFT)) + require.Nil(t, err) - require.Equal(t, uint32(big.NewInt(20).Uint64()), retrievedMetaData.Royalties) - require.Equal(t, core.ESDTModifyRoyalties, txResult.Logs.Events[0].Identifier) + return cs, int32(activationEpochForDynamicNFT) } -// Test scenario #9 -// -// Initial setup: Create NFT -// -// 1. Change the nft to DYNAMIC type - the metadata should be on the system account -// 2. Send the NFT cross shard -// 3. The meta data should still be present on the system account -func TestChainSimulator_NFT_ChangeToDynamicType(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } - +func getTestChainSimulatorWithSaveToSystemAccountDisabled(t *testing.T, baseIssuingCost string) (testsChainSimulator.ChainSimulator, int32) { startTime := time.Now().Unix() roundDurationInMillis := uint64(6000) roundsPerEpoch := core.OptionalUint64{ @@ -1590,9 +3195,8 @@ func TestChainSimulator_NFT_ChangeToDynamicType(t *testing.T) { Value: 20, } - activationEpoch := uint32(4) - - baseIssuingCost := "1000" + activationEpochForSaveToSystemAccount := uint32(4) + activationEpochForDynamicNFT := uint32(6) numOfShards := uint32(3) cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ @@ -1609,339 +3213,242 @@ func TestChainSimulator_NFT_ChangeToDynamicType(t *testing.T) { NumNodesWaitingListMeta: 0, NumNodesWaitingListShard: 0, AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch + cfg.EpochConfig.EnableEpochs.OptimizeNFTStoreEnableEpoch = activationEpochForSaveToSystemAccount + cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpochForDynamicNFT cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost }, }) require.Nil(t, err) require.NotNil(t, cs) - defer cs.Close() - - addrs := createAddresses(t, cs, true) - - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 2) + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpochForSaveToSystemAccount) - 2) require.Nil(t, err) - log.Info("Initial setup: Create NFT") - - nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, addrs[1].Bytes, nftTicker, baseIssuingCost) - - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + return cs, int32(activationEpochForDynamicNFT) +} +func createTokenUpdateTokenIDAndTransfer( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + originAddress []byte, + targetAddress []byte, + tokenID []byte, + metaData *txsFee.MetaData, + epochForDynamicNFT int32, + walletWithRoles dtos.WalletAddress, +) { roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTUpdate), + []byte(core.ESDTRoleTransfer), } + setAddressEsdtRoles(t, cs, 1, walletWithRoles, tokenID, roles) - nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[1], nftTokenID, roles) - - log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - - tx = nftCreateTx(1, addrs[1].Bytes, nftTokenID, nftMetaData) + tx := esdtNftCreateTx(2, originAddress, tokenID, metaData, 1) - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) + log.Info("check that the metadata is saved on the user account") + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(originAddress) + checkMetaData(t, cs, originAddress, tokenID, shardID, metaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, shardID) - log.Info("Step 1. Change the nft to DYNAMIC type - the metadata should be on the system account") + err = cs.GenerateBlocksUntilEpochIsReached(epochForDynamicNFT) + require.Nil(t, err) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[1].Bytes) + tx = updateTokenIDTx(3, originAddress, tokenID) - tx = changeToDynamicTx(2, addrs[1].Bytes, nftTokenID) + log.Info("updating token id", "tokenID", tokenID) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) err = cs.GenerateBlocks(10) require.Nil(t, err) - checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) - - log.Info("Step 2. Send the NFT cross shard") + log.Info("transferring token id", "tokenID", tokenID) - tx = esdtNFTTransferTx(3, addrs[1].Bytes, addrs[2].Bytes, nftTokenID) + tx = esdtNFTTransferTx(4, originAddress, targetAddress, tokenID) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - - log.Info("Step 3. The meta data should still be present on the system account") - - checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) } -// Test scenario #10 -// -// Initial setup: Create SFT and send in 2 shards -// -// 1. change the sft meta data in one shard -// 2. change the sft meta data (differently from the previous one) in the other shard -// 3. send sft from one shard to another -// 4. check that the newest metadata is saved -func TestChainSimulator_SFT_ChangeMetaData(t *testing.T) { +func TestChainSimulator_ChangeToDynamic_OldTokens(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } - - activationEpoch := uint32(2) - baseIssuingCost := "1000" - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) - + cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) defer cs.Close() - addrs := createAddresses(t, cs, true) + addrs := createAddresses(t, cs, false) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) + // issue metaESDT + metaESDTTicker := []byte("METATICKER") + nonce := uint64(0) + tx := issueMetaESDTTx(nonce, addrs[0].Bytes, metaESDTTicker, baseIssuingCost) + nonce++ - err = cs.GenerateBlocks(10) + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - log.Info("Initial setup: Create SFT and send in 2 shards") + metaESDTTokenID := txResult.Logs.Events[0].Topics[0] roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTUpdate), - []byte(core.ESDTRoleNFTAddQuantity), + []byte(core.ESDTRoleTransfer), } + setAddressEsdtRoles(t, cs, nonce, addrs[0], metaESDTTokenID, roles) + nonce++ - sftTicker := []byte("SFTTICKER") - tx := issueSemiFungibleTx(0, addrs[1].Bytes, sftTicker, baseIssuingCost) + log.Info("Issued metaESDT token id", "tokenID", string(metaESDTTokenID)) - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + // issue NFT + nftTicker := []byte("NFTTICKER") + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - sftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[1], sftTokenID, roles) - - setAddressEsdtRoles(t, cs, addrs[0], sftTokenID, roles) - setAddressEsdtRoles(t, cs, addrs[2], sftTokenID, roles) - - log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) - - sftMetaData := txsFee.GetDefaultMetaData() - sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + nftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ - txDataField := bytes.Join( - [][]byte{ - []byte(core.BuiltInFunctionESDTNFTCreate), - []byte(hex.EncodeToString(sftTokenID)), - []byte(hex.EncodeToString(big.NewInt(2).Bytes())), // quantity - sftMetaData.Name, - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), - sftMetaData.Hash, - sftMetaData.Attributes, - sftMetaData.Uris[0], - sftMetaData.Uris[1], - sftMetaData.Uris[2], - }, - []byte("@"), - ) + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) - tx = &transaction.Transaction{ - Nonce: 1, - SndAddr: addrs[1].Bytes, - RcvAddr: addrs[1].Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, - } + // issue SFT + sftTicker := []byte("SFTTICKER") + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocks(10) - require.Nil(t, err) + sftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[0], sftTokenID, roles) + nonce++ - log.Info("Send to separate shards") + log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) - tx = esdtNFTTransferTx(2, addrs[1].Bytes, addrs[2].Bytes, sftTokenID) - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + tokenIDs := [][]byte{ + nftTokenID, + sftTokenID, + metaESDTTokenID, + } - tx = esdtNFTTransferTx(3, addrs[1].Bytes, addrs[0].Bytes, sftTokenID) - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + nftMetaData := txsFee.GetDefaultMetaData() + nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - require.Equal(t, "success", txResult.Status.String()) + sftMetaData := txsFee.GetDefaultMetaData() + sftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - err = cs.GenerateBlocks(10) - require.Nil(t, err) + esdtMetaData := txsFee.GetDefaultMetaData() + esdtMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - log.Info("Step 1. change the sft meta data in one shard") + tokensMetadata := []*txsFee.MetaData{ + nftMetaData, + sftMetaData, + esdtMetaData, + } - sftMetaData2 := txsFee.GetDefaultMetaData() - sftMetaData2.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + for i := range tokenIDs { + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) - sftMetaData2.Name = []byte(hex.EncodeToString([]byte("name2"))) - sftMetaData2.Hash = []byte(hex.EncodeToString([]byte("hash2"))) - sftMetaData2.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - txDataField = bytes.Join( - [][]byte{ - []byte(core.ESDTMetaDataUpdate), - []byte(hex.EncodeToString(sftTokenID)), - sftMetaData2.Nonce, - sftMetaData2.Name, - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), - sftMetaData2.Hash, - sftMetaData2.Attributes, - sftMetaData2.Uris[0], - sftMetaData2.Uris[1], - sftMetaData2.Uris[2], - }, - []byte("@"), - ) + require.Equal(t, "success", txResult.Status.String()) - tx = &transaction.Transaction{ - Nonce: 0, - SndAddr: addrs[0].Bytes, - RcvAddr: addrs[0].Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, + nonce++ } - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - require.Equal(t, "success", txResult.Status.String()) + // meta data should be saved on account, since it is before `OptimizeNFTStoreEnableEpoch` + checkMetaData(t, cs, addrs[0].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + checkMetaData(t, cs, addrs[0].Bytes, sftTokenID, shardID, sftMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, sftTokenID, shardID) - checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData2) + checkMetaData(t, cs, addrs[0].Bytes, metaESDTTokenID, shardID, esdtMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID) - log.Info("Step 2. change the sft meta data (differently from the previous one) in the other shard") + err = cs.GenerateBlocksUntilEpochIsReached(epochForDynamicNFT) + require.Nil(t, err) - sftMetaData3 := txsFee.GetDefaultMetaData() - sftMetaData3.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + log.Info("Change to DYNAMIC type") - sftMetaData3.Name = []byte(hex.EncodeToString([]byte("name3"))) - sftMetaData3.Hash = []byte(hex.EncodeToString([]byte("hash3"))) - sftMetaData3.Attributes = []byte(hex.EncodeToString([]byte("attributes3"))) + // it will not be able to change nft to dynamic type + for i := range tokenIDs { + tx = changeToDynamicTx(nonce, addrs[0].Bytes, tokenIDs[i]) - txDataField = bytes.Join( - [][]byte{ - []byte(core.ESDTMetaDataUpdate), - []byte(hex.EncodeToString(sftTokenID)), - sftMetaData3.Nonce, - sftMetaData3.Name, - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), - sftMetaData3.Hash, - sftMetaData3.Attributes, - sftMetaData3.Uris[0], - sftMetaData3.Uris[1], - sftMetaData3.Uris[2], - }, - []byte("@"), - ) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - tx = &transaction.Transaction{ - Nonce: 0, - SndAddr: addrs[2].Bytes, - RcvAddr: addrs[2].Bytes, - GasLimit: 10_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: big.NewInt(0), - ChainID: []byte(configs.ChainID), - Version: 1, + require.Equal(t, "success", txResult.Status.String()) + + nonce++ } - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + for _, tokenID := range tokenIDs { + tx = updateTokenIDTx(nonce, addrs[0].Bytes, tokenID) - require.Equal(t, "success", txResult.Status.String()) + log.Info("updating token id", "tokenID", tokenID) - shardID = cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[2].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData3) + nonce++ + } - log.Info("Step 3. send sft from one shard to another") + for _, tokenID := range tokenIDs { + log.Info("transfering token id", "tokenID", tokenID) - tx = esdtNFTTransferTx(1, addrs[0].Bytes, addrs[2].Bytes, sftTokenID) - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) + tx = esdtNFTTransferTx(nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocks(10) - require.Nil(t, err) + nonce++ + } - log.Info("Step 4. check that the newest metadata is saved") + checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, sftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, sftTokenID, shardID) - shardID = cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[2].Bytes) + checkMetaData(t, cs, core.SystemAccountAddress, metaESDTTokenID, shardID, esdtMetaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, metaESDTTokenID, shardID) + checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, metaESDTTokenID, shardID) - checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, sftMetaData2) + checkMetaData(t, cs, addrs[1].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, nftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) } -func TestChainSimulator_NFT_RegisterDynamic(t *testing.T) { +func TestChainSimulator_CreateAndPause_NFT(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } @@ -1953,7 +3460,7 @@ func TestChainSimulator_NFT_RegisterDynamic(t *testing.T) { Value: 20, } - activationEpoch := uint32(2) + activationEpoch := uint32(4) baseIssuingCost := "1000" @@ -1981,32 +3488,31 @@ func TestChainSimulator_NFT_RegisterDynamic(t *testing.T) { defer cs.Close() - addrs := createAddresses(t, cs, true) + addrs := createAddresses(t, cs, false) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 1) require.Nil(t, err) - log.Info("Register dynamic nft token") - + // issue NFT nftTicker := []byte("NFTTICKER") - nftTokenName := []byte("tokenName") + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) txDataField := bytes.Join( [][]byte{ - []byte("registerDynamic"), - []byte(hex.EncodeToString(nftTokenName)), + []byte("issueNonFungible"), + []byte(hex.EncodeToString([]byte("asdname"))), []byte(hex.EncodeToString(nftTicker)), - []byte(hex.EncodeToString([]byte("NFT"))), + []byte(hex.EncodeToString([]byte("canPause"))), + []byte(hex.EncodeToString([]byte("true"))), }, []byte("@"), ) - callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) - + nonce := uint64(0) tx := &transaction.Transaction{ - Nonce: 0, + Nonce: nonce, SndAddr: addrs[0].Bytes, - RcvAddr: vm.ESDTSCAddress, + RcvAddr: core.ESDTSCAddress, GasLimit: 100_000_000, GasPrice: minGasPrice, Signature: []byte("dummySig"), @@ -2015,24 +3521,28 @@ func TestChainSimulator_NFT_RegisterDynamic(t *testing.T) { ChainID: []byte(configs.ChainID), Version: 1, } + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - nftTokenID := txResult.Logs.Events[0].Topics[0] roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleTransfer), } - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + nftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) nftMetaData := txsFee.GetDefaultMetaData() nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tx = nftCreateTx(1, addrs[0].Bytes, nftTokenID, nftMetaData) + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -2043,28 +3553,73 @@ func TestChainSimulator_NFT_RegisterDynamic(t *testing.T) { err = cs.GenerateBlocks(10) require.Nil(t, err) + log.Info("check that the metadata for all tokens is saved on the system account") + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) - log.Info("Check that token type is Dynamic") + log.Info("Pause all tokens") scQuery := &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getTokenProperties", - CallValue: big.NewInt(0), - Arguments: [][]byte{nftTokenID}, + ScAddress: vm.ESDTSCAddress, + CallerAddr: addrs[0].Bytes, + FuncName: "pause", + CallValue: big.NewInt(0), + Arguments: [][]byte{nftTokenID}, } result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) require.Nil(t, err) require.Equal(t, "", result.ReturnMessage) require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) - tokenType := result.ReturnData[1] - require.Equal(t, core.DynamicNFTESDT, string(tokenType)) + log.Info("wait for DynamicEsdtFlag activation") + + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) + + log.Info("make an updateTokenID@tokenID function call on the ESDTSystem SC for all token types") + + tx = updateTokenIDTx(nonce, addrs[0].Bytes, nftTokenID) + nonce++ + + log.Info("updating token id", "tokenID", nftTokenID) + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("check that the metadata for all tokens is saved on the system account") + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) + + log.Info("transfer the tokens to another account") + + log.Info("transfering token id", "tokenID", nftTokenID) + + tx = esdtNFTTransferTx(nonce, addrs[0].Bytes, addrs[1].Bytes, nftTokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("check that the metaData for the NFT is still on the system account") + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID = cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[2].Bytes) + + checkMetaData(t, cs, addrs[1].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, nftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) } -func TestChainSimulator_MetaESDT_RegisterDynamic(t *testing.T) { +func TestChainSimulator_CreateAndPauseTokens_DynamicNFT(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } @@ -2076,7 +3631,7 @@ func TestChainSimulator_MetaESDT_RegisterDynamic(t *testing.T) { Value: 20, } - activationEpoch := uint32(2) + activationEpoch := uint32(4) baseIssuingCost := "1000" @@ -2104,33 +3659,37 @@ func TestChainSimulator_MetaESDT_RegisterDynamic(t *testing.T) { defer cs.Close() - addrs := createAddresses(t, cs, true) + addrs := createAddresses(t, cs, false) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch) - 1) require.Nil(t, err) - log.Info("Register dynamic metaESDT token") + log.Info("Step 2. wait for DynamicEsdtFlag activation") - metaTicker := []byte("METATICKER") - metaTokenName := []byte("tokenName") + err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + require.Nil(t, err) - decimals := big.NewInt(15) + // register dynamic NFT + nftTicker := []byte("NFTTICKER") + nftTokenName := []byte("tokenName") txDataField := bytes.Join( [][]byte{ []byte("registerDynamic"), - []byte(hex.EncodeToString(metaTokenName)), - []byte(hex.EncodeToString(metaTicker)), - []byte(hex.EncodeToString([]byte("META"))), - []byte(hex.EncodeToString(decimals.Bytes())), + []byte(hex.EncodeToString(nftTokenName)), + []byte(hex.EncodeToString(nftTicker)), + []byte(hex.EncodeToString([]byte("NFT"))), + []byte(hex.EncodeToString([]byte("canPause"))), + []byte(hex.EncodeToString([]byte("true"))), }, []byte("@"), ) callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + nonce := uint64(0) tx := &transaction.Transaction{ - Nonce: 0, + Nonce: nonce, SndAddr: addrs[0].Bytes, RcvAddr: vm.ESDTSCAddress, GasLimit: 100_000_000, @@ -2141,6 +3700,7 @@ func TestChainSimulator_MetaESDT_RegisterDynamic(t *testing.T) { ChainID: []byte(configs.ChainID), Version: 1, } + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -2148,106 +3708,187 @@ func TestChainSimulator_MetaESDT_RegisterDynamic(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) - nftTokenID := txResult.Logs.Events[0].Topics[0] roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), } - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) nftMetaData := txsFee.GetDefaultMetaData() nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tx = nftCreateTx(1, addrs[0].Bytes, nftTokenID, nftMetaData) + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) err = cs.GenerateBlocks(10) require.Nil(t, err) + log.Info("Step 1. check that the metadata for the Dynamic NFT is saved on the user account") + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) + checkMetaData(t, cs, addrs[0].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) - log.Info("Check that token type is Dynamic") + log.Info("Step 1b. Pause all tokens") scQuery := &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getTokenProperties", - CallValue: big.NewInt(0), - Arguments: [][]byte{nftTokenID}, + ScAddress: vm.ESDTSCAddress, + CallerAddr: addrs[0].Bytes, + FuncName: "pause", + CallValue: big.NewInt(0), + Arguments: [][]byte{nftTokenID}, } result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) require.Nil(t, err) require.Equal(t, "", result.ReturnMessage) require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) - tokenType := result.ReturnData[1] - require.Equal(t, core.Dynamic+core.MetaESDT, string(tokenType)) + log.Info("check that the metadata for the Dynamic NFT is saved on the user account") + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + checkMetaData(t, cs, addrs[0].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) + + log.Info("transfering token id", "tokenID", nftTokenID) + + tx = esdtNFTTransferTx(nonce, addrs[0].Bytes, addrs[1].Bytes, nftTokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + log.Info("check that the metaData for the NFT is on the new user account") + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + shardID = cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[2].Bytes) + + checkMetaData(t, cs, addrs[1].Bytes, nftTokenID, shardID, nftMetaData) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, nftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) } -func TestChainSimulator_FNG_RegisterDynamic(t *testing.T) { +func TestChainSimulator_CheckRolesWhichHasToBeSingular(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } + baseIssuingCost := "1000" - activationEpoch := uint32(2) + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() - baseIssuingCost := "1000" + addrs := createAddresses(t, cs, true) - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost + // register dynamic NFT + nftTicker := []byte("NFTTICKER") + nftTokenName := []byte("tokenName") + + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(nftTokenName)), + []byte(hex.EncodeToString(nftTicker)), + []byte(hex.EncodeToString([]byte("NFT"))), + []byte(hex.EncodeToString([]byte("canPause"))), + []byte(hex.EncodeToString([]byte("true"))), }, - }) + []byte("@"), + ) + + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + + nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + nonce++ + + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.NotNil(t, cs) + require.NotNil(t, txResult) - defer cs.Close() + require.Equal(t, "success", txResult.Status.String()) + + nftTokenID := txResult.Logs.Events[0].Topics[0] + + log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTUpdateAttributes), + []byte(core.ESDTRoleNFTAddURI), + []byte(core.ESDTRoleSetNewURI), + []byte(core.ESDTRoleModifyCreator), + []byte(core.ESDTRoleModifyRoyalties), + []byte(core.ESDTRoleNFTRecreate), + []byte(core.ESDTRoleNFTUpdate), + } + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ + + for _, role := range roles { + tx = setSpecialRoleTx(nonce, addrs[0].Bytes, addrs[1].Bytes, nftTokenID, [][]byte{role}) + nonce++ + + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + + if txResult.Logs != nil && len(txResult.Logs.Events) > 0 { + returnMessage := string(txResult.Logs.Events[0].Topics[1]) + require.True(t, strings.Contains(returnMessage, "already exists")) + } else { + require.Fail(t, "should have been return error message") + } + } +} + +func TestChainSimulator_metaESDT_mergeMetaDataFromMultipleUpdates(t *testing.T) { + t.Parallel() + baseIssuingCost := "1000" + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) + defer cs.Close() + marshaller := cs.GetNodeHandler(0).GetCoreComponents().InternalMarshalizer() addrs := createAddresses(t, cs, true) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) - - log.Info("Register dynamic fungible token") + log.Info("Register dynamic metaESDT token") - metaTicker := []byte("FNGTICKER") + metaTicker := []byte("METATICKER") metaTokenName := []byte("tokenName") decimals := big.NewInt(15) - txDataField := bytes.Join( [][]byte{ []byte("registerDynamic"), []byte(hex.EncodeToString(metaTokenName)), []byte(hex.EncodeToString(metaTicker)), - []byte(hex.EncodeToString([]byte("FNG"))), + []byte(hex.EncodeToString([]byte("META"))), []byte(hex.EncodeToString(decimals.Bytes())), }, []byte("@"), @@ -2255,8 +3896,9 @@ func TestChainSimulator_FNG_RegisterDynamic(t *testing.T) { callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + shard0Nonce := uint64(0) tx := &transaction.Transaction{ - Nonce: 0, + Nonce: shard0Nonce, SndAddr: addrs[0].Bytes, RcvAddr: vm.ESDTSCAddress, GasLimit: 100_000_000, @@ -2267,229 +3909,363 @@ func TestChainSimulator_FNG_RegisterDynamic(t *testing.T) { ChainID: []byte(configs.ChainID), Version: 1, } + shard0Nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - signalErrorTopic := string(txResult.Logs.Events[0].Topics[1]) - - require.Equal(t, fmt.Sprintf("cannot create %s tokens as dynamic", core.FungibleESDT), signalErrorTopic) -} - -func TestChainSimulator_NFT_RegisterAndSetAllRolesDynamic(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } + require.Equal(t, "success", txResult.Status.String()) - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, + tokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTAddQuantity), + []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), } + setAddressEsdtRoles(t, cs, shard0Nonce, addrs[0], tokenID, roles) + shard0Nonce++ - activationEpoch := uint32(2) + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - baseIssuingCost := "1000" + tx = esdtNftCreateTx(shard0Nonce, addrs[0].Bytes, tokenID, metaData, 2) + shard0Nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) + err = cs.GenerateBlocks(10) require.Nil(t, err) - require.NotNil(t, cs) - defer cs.Close() + shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - addrs := createAddresses(t, cs, true) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, shardID, metaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, shardID, []byte{1}) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + log.Info("send metaEsdt cross shard") + + tx = esdtNFTTransferTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + shard0Nonce++ - log.Info("Register dynamic nft token") + err = cs.GenerateBlocks(10) + require.Nil(t, err) - nftTicker := []byte("NFTTICKER") - nftTokenName := []byte("tokenName") + log.Info("update metaData on shard 0") - txDataField := bytes.Join( - [][]byte{ - []byte("registerAndSetAllRolesDynamic"), - []byte(hex.EncodeToString(nftTokenName)), - []byte(hex.EncodeToString(nftTicker)), - []byte(hex.EncodeToString([]byte("NFT"))), - }, - []byte("@"), - ) + newMetaData := &txsFee.MetaData{} + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Attributes = []byte(hex.EncodeToString([]byte("attributes2"))) - callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + tx = esdtMetaDataUpdateTx(tokenID, newMetaData, shard0Nonce, addrs[0].Bytes) + shard0Nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - tx := &transaction.Transaction{ - Nonce: 0, - SndAddr: addrs[0].Bytes, - RcvAddr: vm.ESDTSCAddress, - GasLimit: 100_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: callValue, - ChainID: []byte(configs.ChainID), - Version: 1, + expectedMetaData := txsFee.GetDefaultMetaData() + expectedMetaData.Nonce = newMetaData.Nonce + expectedMetaData.Name = newMetaData.Name + expectedMetaData.Hash = newMetaData.Hash + expectedMetaData.Attributes = newMetaData.Attributes + + round := cs.GetNodeHandler(0).GetChainHandler().GetCurrentBlockHeader().GetRound() + reserved := &esdt.MetaDataVersion{ + Name: round, + Creator: round, + Hash: round, + Attributes: round, } + firstVersion, _ := marshaller.Marshal(reserved) - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 0, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 0, firstVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 1, metaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 1, []byte{1}) + + log.Info("send the update role to shard 2") + + shard0Nonce = transferSpecialRoleToAddr(t, cs, shard0Nonce, tokenID, addrs[0].Bytes, addrs[2].Bytes, []byte(core.ESDTRoleNFTUpdate)) + + err = cs.GenerateBlocks(10) require.Nil(t, err) - require.NotNil(t, txResult) + log.Info("update metaData on shard 2") + + newMetaData2 := &txsFee.MetaData{} + newMetaData2.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData2.Uris = [][]byte{[]byte(hex.EncodeToString([]byte("uri5"))), []byte(hex.EncodeToString([]byte("uri6"))), []byte(hex.EncodeToString([]byte("uri7")))} + newMetaData2.Royalties = []byte(hex.EncodeToString(big.NewInt(15).Bytes())) + + tx = esdtMetaDataUpdateTx(tokenID, newMetaData2, 0, addrs[2].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - nftTokenID := txResult.Logs.Events[0].Topics[0] - roles := [][]byte{ - []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleTransfer), + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 0, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 0, firstVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 1, metaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 1, []byte{1}) + + retrievedMetaData := getMetaDataFromAcc(t, cs, core.SystemAccountAddress, tokenID, 2) + require.Equal(t, uint64(1), retrievedMetaData.Nonce) + require.Equal(t, 0, len(retrievedMetaData.Name)) + require.Equal(t, addrs[2].Bytes, retrievedMetaData.Creator) + require.Equal(t, newMetaData2.Royalties, []byte(hex.EncodeToString(big.NewInt(int64(retrievedMetaData.Royalties)).Bytes()))) + require.Equal(t, 0, len(retrievedMetaData.Hash)) + require.Equal(t, 3, len(retrievedMetaData.URIs)) + for i, uri := range newMetaData2.Uris { + require.Equal(t, uri, []byte(hex.EncodeToString(retrievedMetaData.URIs[i]))) } - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + require.Equal(t, 0, len(retrievedMetaData.Attributes)) - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + round2 := cs.GetNodeHandler(2).GetChainHandler().GetCurrentBlockHeader().GetRound() + reserved = &esdt.MetaDataVersion{ + URIs: round2, + Creator: round2, + Royalties: round2, + } + secondVersion, _ := cs.GetNodeHandler(shardID).GetCoreComponents().InternalMarshalizer().Marshal(reserved) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 2, secondVersion) - tx = nftCreateTx(1, addrs[0].Bytes, nftTokenID, nftMetaData) + log.Info("transfer from shard 0 to shard 1 - should merge metaData") + tx = esdtNFTTransferTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + shard0Nonce++ err = cs.GenerateBlocks(10) require.Nil(t, err) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 0, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 0, firstVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 1, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 1, firstVersion) - checkMetaData(t, cs, core.SystemAccountAddress, nftTokenID, shardID, nftMetaData) + log.Info("transfer from shard 1 to shard 2 - should merge metaData") - log.Info("Check that token type is Dynamic") + tx = setSpecialRoleTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID, [][]byte{[]byte(core.ESDTRoleTransfer)}) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + shard0Nonce++ - scQuery := &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getTokenProperties", - CallValue: big.NewInt(0), - Arguments: [][]byte{nftTokenID}, + tx = esdtNFTTransferTx(0, addrs[1].Bytes, addrs[2].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 0, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 0, firstVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 1, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 1, firstVersion) + + latestMetaData := txsFee.GetDefaultMetaData() + latestMetaData.Nonce = expectedMetaData.Nonce + latestMetaData.Name = expectedMetaData.Name + latestMetaData.Royalties = newMetaData2.Royalties + latestMetaData.Hash = expectedMetaData.Hash + latestMetaData.Attributes = expectedMetaData.Attributes + latestMetaData.Uris = newMetaData2.Uris + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 2, latestMetaData) + + reserved = &esdt.MetaDataVersion{ + Name: round, + Creator: round2, + Royalties: round2, + Hash: round, + URIs: round2, + Attributes: round, } - result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + thirdVersion, _ := cs.GetNodeHandler(shardID).GetCoreComponents().InternalMarshalizer().Marshal(reserved) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 2, thirdVersion) + + log.Info("transfer from shard 2 to shard 0 - should update metaData") + + tx = setSpecialRoleTx(shard0Nonce, addrs[0].Bytes, addrs[2].Bytes, tokenID, [][]byte{[]byte(core.ESDTRoleTransfer)}) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.Equal(t, "", result.ReturnMessage) - require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - tokenType := result.ReturnData[1] - require.Equal(t, core.DynamicNFTESDT, string(tokenType)) + tx = esdtNFTTransferTx(1, addrs[2].Bytes, addrs[0].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - log.Info("Check token roles") + err = cs.GenerateBlocks(10) + require.Nil(t, err) - scQuery = &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getAllAddressesAndRoles", - CallValue: big.NewInt(0), - Arguments: [][]byte{nftTokenID}, - } - result, _, err = cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 0, latestMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 0, thirdVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 1, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 1, firstVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 2, latestMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 2, thirdVersion) + + log.Info("transfer from shard 1 to shard 0 - liquidity should be updated") + + tx = esdtNFTTransferTx(1, addrs[1].Bytes, addrs[0].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.Equal(t, "", result.ReturnMessage) - require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - expectedRoles := [][]byte{ - []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTBurn), - []byte(core.ESDTRoleNFTUpdateAttributes), - []byte(core.ESDTRoleNFTAddURI), - []byte(core.ESDTRoleNFTRecreate), - []byte(core.ESDTRoleModifyCreator), - []byte(core.ESDTRoleModifyRoyalties), - []byte(core.ESDTRoleSetNewURI), + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 0, latestMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 0, thirdVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 1, expectedMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 1, firstVersion) + checkMetaData(t, cs, core.SystemAccountAddress, tokenID, 2, latestMetaData) + checkReservedField(t, cs, core.SystemAccountAddress, tokenID, 2, thirdVersion) +} + +func unsetSpecialRole( + nonce uint64, + sndAddr []byte, + address []byte, + token []byte, + role []byte, +) *transaction.Transaction { + txDataBytes := [][]byte{ + []byte("unSetSpecialRole"), + []byte(hex.EncodeToString(token)), + []byte(hex.EncodeToString(address)), + []byte(hex.EncodeToString(role)), } - checkTokenRoles(t, result.ReturnData, expectedRoles) + txDataField := bytes.Join( + txDataBytes, + []byte("@"), + ) + + return &transaction.Transaction{ + Nonce: nonce, + SndAddr: sndAddr, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 60_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, + } } -func TestChainSimulator_SFT_RegisterAndSetAllRolesDynamic(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") +func esdtMetaDataUpdateTx(tokenID []byte, metaData *txsFee.MetaData, nonce uint64, address []byte) *transaction.Transaction { + txData := [][]byte{ + []byte(core.ESDTMetaDataUpdate), + []byte(hex.EncodeToString(tokenID)), + metaData.Nonce, + metaData.Name, + metaData.Royalties, + metaData.Hash, + metaData.Attributes, + } + if len(metaData.Uris) > 0 { + txData = append(txData, metaData.Uris...) + } else { + txData = append(txData, nil) } - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, + txDataField := bytes.Join( + txData, + []byte("@"), + ) + + tx := &transaction.Transaction{ + Nonce: nonce, + SndAddr: address, + RcvAddr: address, + GasLimit: 10_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: big.NewInt(0), + ChainID: []byte(configs.ChainID), + Version: 1, } - activationEpoch := uint32(2) + return tx +} - baseIssuingCost := "1000" +func transferSpecialRoleToAddr( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + nonce uint64, + tokenID []byte, + sndAddr []byte, + dstAddr []byte, + role []byte, +) uint64 { + tx := unsetSpecialRole(nonce, sndAddr, sndAddr, tokenID, role) + nonce++ + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) + tx = setSpecialRoleTx(nonce, sndAddr, dstAddr, tokenID, [][]byte{role}) + nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.NotNil(t, cs) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) + + return nonce +} +func TestChainSimulator_dynamicNFT_mergeMetaDataFromMultipleUpdates(t *testing.T) { + t.Parallel() + + baseIssuingCost := "1000" + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() addrs := createAddresses(t, cs, true) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) - - log.Info("Register dynamic sft token") + log.Info("Register dynamic NFT token") - sftTicker := []byte("SFTTICKER") - sftTokenName := []byte("tokenName") + ticker := []byte("NFTTICKER") + tokenName := []byte("tokenName") txDataField := bytes.Join( [][]byte{ - []byte("registerAndSetAllRolesDynamic"), - []byte(hex.EncodeToString(sftTokenName)), - []byte(hex.EncodeToString(sftTicker)), - []byte(hex.EncodeToString([]byte("SFT"))), + []byte("registerDynamic"), + []byte(hex.EncodeToString(tokenName)), + []byte(hex.EncodeToString(ticker)), + []byte(hex.EncodeToString([]byte("NFT"))), }, []byte("@"), ) callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + shard0Nonce := uint64(0) tx := &transaction.Transaction{ - Nonce: 0, + Nonce: shard0Nonce, SndAddr: addrs[0].Bytes, RcvAddr: vm.ESDTSCAddress, GasLimit: 100_000_000, @@ -2500,6 +4276,7 @@ func TestChainSimulator_SFT_RegisterAndSetAllRolesDynamic(t *testing.T) { ChainID: []byte(configs.ChainID), Version: 1, } + shard0Nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -2507,226 +4284,136 @@ func TestChainSimulator_SFT_RegisterAndSetAllRolesDynamic(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) - sftTokenID := txResult.Logs.Events[0].Topics[0] + tokenID := txResult.Logs.Events[0].Topics[0] roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), } - setAddressEsdtRoles(t, cs, addrs[0], sftTokenID, roles) + setAddressEsdtRoles(t, cs, shard0Nonce, addrs[0], tokenID, roles) + shard0Nonce++ - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + err = cs.GenerateBlocks(10) + require.Nil(t, err) - tx = nftCreateTx(1, addrs[0].Bytes, sftTokenID, nftMetaData) + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + tx = esdtNftCreateTx(shard0Nonce, addrs[0].Bytes, tokenID, metaData, 1) + shard0Nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) err = cs.GenerateBlocks(10) require.Nil(t, err) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - - checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, nftMetaData) - - log.Info("Check that token type is Dynamic") + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaData(t, cs, addrs[0].Bytes, tokenID, 0, metaData) - scQuery := &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getTokenProperties", - CallValue: big.NewInt(0), - Arguments: [][]byte{sftTokenID}, - } - result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) - require.Nil(t, err) - require.Equal(t, "", result.ReturnMessage) - require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) + log.Info("give update role to another account and update metaData") - tokenType := result.ReturnData[1] - require.Equal(t, core.DynamicSFTESDT, string(tokenType)) + shard0Nonce = transferSpecialRoleToAddr(t, cs, shard0Nonce, tokenID, addrs[0].Bytes, addrs[1].Bytes, []byte(core.ESDTRoleNFTUpdate)) - log.Info("Check token roles") + newMetaData := &txsFee.MetaData{} + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Royalties = []byte(hex.EncodeToString(big.NewInt(15).Bytes())) - scQuery = &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getAllAddressesAndRoles", - CallValue: big.NewInt(0), - Arguments: [][]byte{sftTokenID}, - } - result, _, err = cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) + tx = esdtMetaDataUpdateTx(tokenID, newMetaData, 0, addrs[1].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.Equal(t, "", result.ReturnMessage) - require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) - - expectedRoles := [][]byte{ - []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTBurn), - []byte(core.ESDTRoleNFTUpdateAttributes), - []byte(core.ESDTRoleNFTAddURI), - []byte(core.ESDTRoleNFTRecreate), - []byte(core.ESDTRoleModifyCreator), - []byte(core.ESDTRoleModifyRoyalties), - []byte(core.ESDTRoleSetNewURI), - } - - checkTokenRoles(t, result.ReturnData, expectedRoles) -} - -func TestChainSimulator_FNG_RegisterAndSetAllRolesDynamic(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } - - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - activationEpoch := uint32(2) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + checkMetaData(t, cs, addrs[0].Bytes, tokenID, 0, metaData) + newMetaData.Attributes = []byte{} + checkMetaData(t, cs, addrs[1].Bytes, tokenID, 1, newMetaData) - baseIssuingCost := "1000" + log.Info("transfer nft - should merge metaData") - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) + tx = esdtNFTTransferTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID) + shard0Nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) - require.NotNil(t, cs) - - defer cs.Close() - - addrs := createAddresses(t, cs, true) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) + err = cs.GenerateBlocks(10) require.Nil(t, err) - log.Info("Register dynamic fungible token") + mergedMetaData := txsFee.GetDefaultMetaData() + mergedMetaData.Nonce = metaData.Nonce + mergedMetaData.Name = newMetaData.Name + mergedMetaData.Hash = newMetaData.Hash + mergedMetaData.Royalties = newMetaData.Royalties - fngTicker := []byte("FNGTICKER") - fngTokenName := []byte("tokenName") + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, tokenID, 0) + checkMetaData(t, cs, addrs[1].Bytes, tokenID, 1, mergedMetaData) - txDataField := bytes.Join( - [][]byte{ - []byte("registerAndSetAllRolesDynamic"), - []byte(hex.EncodeToString(fngTokenName)), - []byte(hex.EncodeToString(fngTicker)), - []byte(hex.EncodeToString([]byte("FNG"))), - []byte(hex.EncodeToString(big.NewInt(10).Bytes())), - }, - []byte("@"), - ) + log.Info("transfer nft - should remove metaData from sender") - callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + tx = setSpecialRoleTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID, [][]byte{[]byte(core.ESDTRoleTransfer)}) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - tx := &transaction.Transaction{ - Nonce: 0, - SndAddr: addrs[0].Bytes, - RcvAddr: vm.ESDTSCAddress, - GasLimit: 100_000_000, - GasPrice: minGasPrice, - Signature: []byte("dummySig"), - Data: txDataField, - Value: callValue, - ChainID: []byte(configs.ChainID), - Version: 1, - } + err = cs.GenerateBlocks(10) + require.Nil(t, err) - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + tx = esdtNFTTransferTx(1, addrs[1].Bytes, addrs[2].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - signalErrorTopic := string(txResult.Logs.Events[0].Topics[1]) + err = cs.GenerateBlocks(10) + require.Nil(t, err) - require.Equal(t, fmt.Sprintf("cannot create %s tokens as dynamic", core.FungibleESDT), signalErrorTopic) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 2) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, tokenID, 0) + checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, tokenID, 1) + checkMetaData(t, cs, addrs[2].Bytes, tokenID, 2, mergedMetaData) } -func TestChainSimulator_MetaESDT_RegisterAndSetAllRolesDynamic(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } - - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, - } - - activationEpoch := uint32(2) +func TestChainSimulator_dynamicNFT_changeMetaDataForOneNFTShouldNotChangeOtherNonces(t *testing.T) { + t.Parallel() baseIssuingCost := "1000" - - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpoch - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) - + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() addrs := createAddresses(t, cs, true) - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpoch)) - require.Nil(t, err) - - log.Info("Register dynamic meta esdt token") + log.Info("Register dynamic NFT token") - ticker := []byte("META" + "TICKER") + ticker := []byte("NFTTICKER") tokenName := []byte("tokenName") - decimals := big.NewInt(10) - txDataField := bytes.Join( [][]byte{ - []byte("registerAndSetAllRolesDynamic"), + []byte("registerDynamic"), []byte(hex.EncodeToString(tokenName)), []byte(hex.EncodeToString(ticker)), - []byte(hex.EncodeToString([]byte("META"))), - []byte(hex.EncodeToString(decimals.Bytes())), + []byte(hex.EncodeToString([]byte("NFT"))), }, []byte("@"), ) callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) + shard0Nonce := uint64(0) tx := &transaction.Transaction{ - Nonce: 0, + Nonce: shard0Nonce, SndAddr: addrs[0].Bytes, RcvAddr: vm.ESDTSCAddress, GasLimit: 100_000_000, @@ -2737,6 +4424,7 @@ func TestChainSimulator_MetaESDT_RegisterAndSetAllRolesDynamic(t *testing.T) { ChainID: []byte(configs.ChainID), Version: 1, } + shard0Nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -2744,303 +4432,260 @@ func TestChainSimulator_MetaESDT_RegisterAndSetAllRolesDynamic(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) - metaTokenID := txResult.Logs.Events[0].Topics[0] + tokenID := txResult.Logs.Events[0].Topics[0] roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleTransfer), - } - setAddressEsdtRoles(t, cs, addrs[0], metaTokenID, roles) - - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - - tx = nftCreateTx(1, addrs[0].Bytes, metaTokenID, nftMetaData) - - txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - - require.Equal(t, "success", txResult.Status.String()) - - err = cs.GenerateBlocks(10) - require.Nil(t, err) - - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - - checkMetaData(t, cs, core.SystemAccountAddress, metaTokenID, shardID, nftMetaData) - - log.Info("Check that token type is Dynamic") - - scQuery := &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getTokenProperties", - CallValue: big.NewInt(0), - Arguments: [][]byte{metaTokenID}, - } - result, _, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) - require.Nil(t, err) - require.Equal(t, "", result.ReturnMessage) - require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) - - tokenType := result.ReturnData[1] - require.Equal(t, core.Dynamic+core.MetaESDT, string(tokenType)) - - log.Info("Check token roles") - - scQuery = &process.SCQuery{ - ScAddress: vm.ESDTSCAddress, - FuncName: "getAllAddressesAndRoles", - CallValue: big.NewInt(0), - Arguments: [][]byte{metaTokenID}, - } - result, _, err = cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().ExecuteSCQuery(scQuery) - require.Nil(t, err) - require.Equal(t, "", result.ReturnMessage) - require.Equal(t, testsChainSimulator.OkReturnCode, result.ReturnCode) - - expectedRoles := [][]byte{ - []byte(core.ESDTRoleNFTCreate), - []byte(core.ESDTRoleNFTBurn), - []byte(core.ESDTRoleNFTAddQuantity), - []byte(core.ESDTRoleNFTUpdateAttributes), - []byte(core.ESDTRoleNFTAddURI), + []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), } + setAddressEsdtRoles(t, cs, shard0Nonce, addrs[0], tokenID, roles) + shard0Nonce++ - checkTokenRoles(t, result.ReturnData, expectedRoles) -} - -func checkTokenRoles(t *testing.T, returnData [][]byte, expectedRoles [][]byte) { - for _, expRole := range expectedRoles { - found := false + err = cs.GenerateBlocks(10) + require.Nil(t, err) - for _, item := range returnData { - if bytes.Equal(expRole, item) { - found = true - } - } + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - require.True(t, found) - } -} + tx = esdtNftCreateTx(shard0Nonce, addrs[0].Bytes, tokenID, metaData, 1) + shard0Nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) -func TestChainSimulator_NFTcreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(2).Bytes())) + tx = esdtNftCreateTx(shard0Nonce, addrs[0].Bytes, tokenID, metaData, 1) + shard0Nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - baseIssuingCost := "1000" - cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) - defer cs.Close() + err = cs.GenerateBlocks(10) + require.Nil(t, err) - addrs := createAddresses(t, cs, false) + log.Info("give update role to another account and update metaData for nonce 2") - log.Info("Initial setup: Create NFT that will have it's metadata saved to the user account") + shard0Nonce = transferSpecialRoleToAddr(t, cs, shard0Nonce, tokenID, addrs[0].Bytes, addrs[1].Bytes, []byte(core.ESDTRoleNFTUpdate)) - nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, addrs[0].Bytes, nftTicker, baseIssuingCost) + newMetaData := &txsFee.MetaData{} + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(2).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Royalties = []byte(hex.EncodeToString(big.NewInt(15).Bytes())) - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + tx = esdtMetaDataUpdateTx(tokenID, newMetaData, 0, addrs[1].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - nftTokenID := txResult.Logs.Events[0].Topics[0] - log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) + log.Info("transfer nft with nonce 1 - should not merge metaData") - nftMetaData := txsFee.GetDefaultMetaData() - nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + tx = esdtNFTTransferTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, nftTokenID, nftMetaData, epochForDynamicNFT, addrs[0]) + err = cs.GenerateBlocks(10) + require.Nil(t, err) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - checkMetaData(t, cs, addrs[1].Bytes, nftTokenID, shardID, nftMetaData) - checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, nftTokenID, shardID) - checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, nftTokenID, shardID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + checkMetaData(t, cs, addrs[1].Bytes, tokenID, 1, metaData) } -func TestChainSimulator_SFTcreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } +func TestChainSimulator_dynamicNFT_updateBeforeCreateOnSameAccountShouldOverwrite(t *testing.T) { + t.Parallel() baseIssuingCost := "1000" - cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() - addrs := createAddresses(t, cs, false) - - log.Info("Initial setup: Create SFT that will have it's metadata saved to the user account") + addrs := createAddresses(t, cs, true) - sftTicker := []byte("SFTTICKER") - tx := issueSemiFungibleTx(0, addrs[0].Bytes, sftTicker, baseIssuingCost) + log.Info("Register dynamic NFT token") - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) - sftTokenID := txResult.Logs.Events[0].Topics[0] + ticker := []byte("NFTTICKER") + tokenName := []byte("tokenName") - log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(tokenName)), + []byte(hex.EncodeToString(ticker)), + []byte(hex.EncodeToString([]byte("NFT"))), + }, + []byte("@"), + ) - metaData := txsFee.GetDefaultMetaData() - metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) - createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, sftTokenID, metaData, epochForDynamicNFT, addrs[0]) + shard0Nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: shard0Nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, + } + shard0Nonce++ - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) - checkMetaData(t, cs, core.SystemAccountAddress, sftTokenID, shardID, metaData) - checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, sftTokenID, shardID) - checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, sftTokenID, shardID) -} + require.Equal(t, "success", txResult.Status.String()) -func TestChainSimulator_FungibleCreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") + tokenID := txResult.Logs.Events[0].Topics[0] + roles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), } + setAddressEsdtRoles(t, cs, shard0Nonce, addrs[0], tokenID, roles) + shard0Nonce++ - baseIssuingCost := "1000" - cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) - defer cs.Close() - - addrs := createAddresses(t, cs, false) + err = cs.GenerateBlocks(10) + require.Nil(t, err) - log.Info("Initial setup: Create FungibleESDT that will have it's metadata saved to the user account") + log.Info("update meta data for a token that is not yet created") - funTicker := []byte("FUNTICKER") - tx := issueTx(0, addrs[0].Bytes, funTicker, baseIssuingCost) + newMetaData := &txsFee.MetaData{} + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Royalties = []byte(hex.EncodeToString(big.NewInt(15).Bytes())) - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + tx = esdtMetaDataUpdateTx(tokenID, newMetaData, shard0Nonce, addrs[0].Bytes) + shard0Nonce++ + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) - funTokenID := txResult.Logs.Events[0].Topics[0] - log.Info("Issued FungibleESDT token id", "tokenID", string(funTokenID)) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + newMetaData.Attributes = []byte{} + newMetaData.Uris = [][]byte{} + checkMetaData(t, cs, addrs[0].Bytes, tokenID, 0, newMetaData) + + log.Info("create nft with the same nonce - should overwrite the metadata") metaData := txsFee.GetDefaultMetaData() metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, funTokenID, metaData, epochForDynamicNFT, addrs[0]) + tx = esdtNftCreateTx(shard0Nonce, addrs[0].Bytes, tokenID, metaData, 1) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) + err = cs.GenerateBlocks(10) + require.Nil(t, err) - checkMetaData(t, cs, core.SystemAccountAddress, funTokenID, shardID, metaData) - checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, funTokenID, shardID) - checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, funTokenID, shardID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaData(t, cs, addrs[0].Bytes, tokenID, 0, metaData) } -func TestChainSimulator_MetaESDTCreatedBeforeSaveToSystemAccountEnabled(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } +func TestChainSimulator_dynamicNFT_updateBeforeCreateOnDifferentAccountsShouldMergeMetaDataWhenTransferred(t *testing.T) { + t.Parallel() baseIssuingCost := "1000" - cs, epochForDynamicNFT := getTestChainSimulatorWithSaveToSystemAccountDisabled(t, baseIssuingCost) + cs, _ := getTestChainSimulatorWithDynamicNFTEnabled(t, baseIssuingCost) defer cs.Close() - addrs := createAddresses(t, cs, false) - - log.Info("Initial setup: Create MetaESDT that will have it's metadata saved to the user account") - - metaTicker := []byte("METATICKER") - tx := issueMetaESDTTx(0, addrs[0].Bytes, metaTicker, baseIssuingCost) - - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) - require.Nil(t, err) - require.NotNil(t, txResult) - - metaTokenID := txResult.Logs.Events[0].Topics[0] + addrs := createAddresses(t, cs, true) - log.Info("Issued MetaESDT token id", "tokenID", string(metaTokenID)) + log.Info("Register dynamic NFT token") - metaData := txsFee.GetDefaultMetaData() - metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + ticker := []byte("NFTTICKER") + tokenName := []byte("tokenName") - createTokenUpdateTokenIDAndTransfer(t, cs, addrs[0].Bytes, addrs[1].Bytes, metaTokenID, metaData, epochForDynamicNFT, addrs[0]) + txDataField := bytes.Join( + [][]byte{ + []byte("registerDynamic"), + []byte(hex.EncodeToString(tokenName)), + []byte(hex.EncodeToString(ticker)), + []byte(hex.EncodeToString([]byte("NFT"))), + }, + []byte("@"), + ) - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(addrs[0].Bytes) - checkMetaData(t, cs, core.SystemAccountAddress, metaTokenID, shardID, metaData) - checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, metaTokenID, shardID) - checkMetaDataNotInAcc(t, cs, addrs[1].Bytes, metaTokenID, shardID) -} + callValue, _ := big.NewInt(0).SetString(baseIssuingCost, 10) -func getTestChainSimulatorWithSaveToSystemAccountDisabled(t *testing.T, baseIssuingCost string) (testsChainSimulator.ChainSimulator, int32) { - startTime := time.Now().Unix() - roundDurationInMillis := uint64(6000) - roundsPerEpoch := core.OptionalUint64{ - HasValue: true, - Value: 20, + shard0Nonce := uint64(0) + tx := &transaction.Transaction{ + Nonce: shard0Nonce, + SndAddr: addrs[0].Bytes, + RcvAddr: vm.ESDTSCAddress, + GasLimit: 100_000_000, + GasPrice: minGasPrice, + Signature: []byte("dummySig"), + Data: txDataField, + Value: callValue, + ChainID: []byte(configs.ChainID), + Version: 1, } + shard0Nonce++ - activationEpochForSaveToSystemAccount := uint32(2) - activationEpochForDynamicNFT := uint32(4) - - numOfShards := uint32(3) - cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ - BypassTxSignatureCheck: true, - TempDir: t.TempDir(), - PathToInitialConfig: defaultPathToInitialConfig, - NumOfShards: numOfShards, - GenesisTimestamp: startTime, - RoundDurationInMillis: roundDurationInMillis, - RoundsPerEpoch: roundsPerEpoch, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 3, - MetaChainMinNodes: 3, - NumNodesWaitingListMeta: 0, - NumNodesWaitingListShard: 0, - AlterConfigsFunction: func(cfg *config.Configs) { - cfg.EpochConfig.EnableEpochs.OptimizeNFTStoreEnableEpoch = activationEpochForSaveToSystemAccount - cfg.EpochConfig.EnableEpochs.DynamicESDTEnableEpoch = activationEpochForDynamicNFT - cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = baseIssuingCost - }, - }) - require.Nil(t, err) - require.NotNil(t, cs) - - err = cs.GenerateBlocksUntilEpochIsReached(int32(activationEpochForSaveToSystemAccount) - 1) + txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) - return cs, int32(activationEpochForDynamicNFT) -} + require.Equal(t, "success", txResult.Status.String()) -func createTokenUpdateTokenIDAndTransfer( - t *testing.T, - cs testsChainSimulator.ChainSimulator, - originAddress []byte, - targetAddress []byte, - tokenID []byte, - metaData *txsFee.MetaData, - epochForDynamicNFT int32, - walletWithRoles dtos.WalletAddress, -) { + tokenID := txResult.Logs.Events[0].Topics[0] roles := [][]byte{ []byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleTransfer), + []byte(core.ESDTRoleNFTUpdate), } - setAddressEsdtRoles(t, cs, walletWithRoles, tokenID, roles) - - tx := nftCreateTx(1, originAddress, tokenID, metaData) + setAddressEsdtRoles(t, cs, shard0Nonce, addrs[0], tokenID, roles) + shard0Nonce++ - txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) + err = cs.GenerateBlocks(10) require.Nil(t, err) - require.NotNil(t, txResult) - require.Equal(t, "success", txResult.Status.String()) + log.Info("transfer update role to another address") - log.Info("check that the metadata is saved on the user account") - shardID := cs.GetNodeHandler(0).GetProcessComponents().ShardCoordinator().ComputeId(originAddress) - checkMetaData(t, cs, originAddress, tokenID, shardID, metaData) - checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, shardID) + shard0Nonce = transferSpecialRoleToAddr(t, cs, shard0Nonce, tokenID, addrs[0].Bytes, addrs[1].Bytes, []byte(core.ESDTRoleNFTUpdate)) - err = cs.GenerateBlocksUntilEpochIsReached(epochForDynamicNFT) + log.Info("update meta data for a token that is not yet created") + + newMetaData := &txsFee.MetaData{} + newMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + newMetaData.Name = []byte(hex.EncodeToString([]byte("name2"))) + newMetaData.Hash = []byte(hex.EncodeToString([]byte("hash2"))) + newMetaData.Royalties = []byte(hex.EncodeToString(big.NewInt(15).Bytes())) + + tx = esdtMetaDataUpdateTx(tokenID, newMetaData, 0, addrs[1].Bytes) + txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) + require.NotNil(t, txResult) + require.Equal(t, "success", txResult.Status.String()) - tx = updateTokenIDTx(2, originAddress, tokenID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, tokenID, 0) + newMetaData.Attributes = []byte{} + newMetaData.Uris = [][]byte{} + checkMetaData(t, cs, addrs[1].Bytes, tokenID, 1, newMetaData) - log.Info("updating token id", "tokenID", tokenID) + log.Info("create nft with the same nonce on different account") + + metaData := txsFee.GetDefaultMetaData() + metaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) + tx = esdtNftCreateTx(shard0Nonce, addrs[0].Bytes, tokenID, metaData, 1) + shard0Nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) @@ -3049,11 +4694,26 @@ func createTokenUpdateTokenIDAndTransfer( err = cs.GenerateBlocks(10) require.Nil(t, err) - log.Info("transferring token id", "tokenID", tokenID) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + checkMetaData(t, cs, addrs[0].Bytes, tokenID, 0, metaData) + checkMetaData(t, cs, addrs[1].Bytes, tokenID, 1, newMetaData) + + log.Info("transfer dynamic NFT to the updated account") - tx = esdtNFTTransferTx(3, originAddress, targetAddress, tokenID) + tx = esdtNFTTransferTx(shard0Nonce, addrs[0].Bytes, addrs[1].Bytes, tokenID) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) + + err = cs.GenerateBlocks(10) + require.Nil(t, err) + + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 0) + checkMetaDataNotInAcc(t, cs, core.SystemAccountAddress, tokenID, 1) + checkMetaDataNotInAcc(t, cs, addrs[0].Bytes, tokenID, 0) + newMetaData.Attributes = metaData.Attributes + newMetaData.Uris = metaData.Uris + checkMetaData(t, cs, addrs[1].Bytes, tokenID, 1, newMetaData) } diff --git a/integrationTests/chainSimulator/vm/esdtTokens_test.go b/integrationTests/chainSimulator/vm/esdtTokens_test.go index 00f5e3344f6..a5505de14ff 100644 --- a/integrationTests/chainSimulator/vm/esdtTokens_test.go +++ b/integrationTests/chainSimulator/vm/esdtTokens_test.go @@ -82,7 +82,9 @@ func TestChainSimulator_Api_TokenType(t *testing.T) { // issue fungible fungibleTicker := []byte("FUNTICKER") - tx := issueTx(0, addrs[0].Bytes, fungibleTicker, baseIssuingCost) + nonce := uint64(0) + tx := issueTx(nonce, addrs[0].Bytes, fungibleTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -90,27 +92,36 @@ func TestChainSimulator_Api_TokenType(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) fungibleTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], fungibleTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], fungibleTokenID, roles) + nonce++ log.Info("Issued fungible token id", "tokenID", string(fungibleTokenID)) // issue NFT nftTicker := []byte("NFTTICKER") - tx = issueNonFungibleTx(1, addrs[0].Bytes, nftTicker, baseIssuingCost) + tx = issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) require.Equal(t, "success", txResult.Status.String()) + scrs, err := cs.GetNodeHandler(core.MetachainShardId).GetFacadeHandler().GetSCRsByTxHash(txResult.Hash, txResult.SmartContractResults[0].Hash) + require.Nil(t, err) + require.NotNil(t, scrs) + require.Equal(t, len(txResult.SmartContractResults), len(scrs)) + nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) // issue SFT sftTicker := []byte("SFTTICKER") - tx = issueSemiFungibleTx(2, addrs[0].Bytes, sftTicker, baseIssuingCost) + tx = issueSemiFungibleTx(nonce, addrs[0].Bytes, sftTicker, baseIssuingCost) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -118,7 +129,8 @@ func TestChainSimulator_Api_TokenType(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) sftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], sftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], sftTokenID, roles) + nonce++ log.Info("Issued SFT token id", "tokenID", string(sftTokenID)) @@ -141,9 +153,8 @@ func TestChainSimulator_Api_TokenType(t *testing.T) { sftMetaData, } - nonce := uint64(3) for i := range tokenIDs { - tx = nftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i]) + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, tokenIDs[i], tokensMetadata[i], 1) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -244,7 +255,9 @@ func TestChainSimulator_Api_NFTToken(t *testing.T) { // issue NFT nftTicker := []byte("NFTTICKER") - tx := issueNonFungibleTx(0, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce := uint64(0) + tx := issueNonFungibleTx(nonce, addrs[0].Bytes, nftTicker, baseIssuingCost) + nonce++ txResult, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -252,14 +265,16 @@ func TestChainSimulator_Api_NFTToken(t *testing.T) { require.Equal(t, "success", txResult.Status.String()) nftTokenID := txResult.Logs.Events[0].Topics[0] - setAddressEsdtRoles(t, cs, addrs[0], nftTokenID, roles) + setAddressEsdtRoles(t, cs, nonce, addrs[0], nftTokenID, roles) + nonce++ log.Info("Issued NFT token id", "tokenID", string(nftTokenID)) nftMetaData := txsFee.GetDefaultMetaData() nftMetaData.Nonce = []byte(hex.EncodeToString(big.NewInt(1).Bytes())) - tx = nftCreateTx(1, addrs[0].Bytes, nftTokenID, nftMetaData) + tx = esdtNftCreateTx(nonce, addrs[0].Bytes, nftTokenID, nftMetaData, 1) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -309,7 +324,8 @@ func TestChainSimulator_Api_NFTToken(t *testing.T) { log.Info("Update token id", "tokenID", nftTokenID) - tx = updateTokenIDTx(2, addrs[0].Bytes, nftTokenID) + tx = updateTokenIDTx(nonce, addrs[0].Bytes, nftTokenID) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) @@ -330,7 +346,8 @@ func TestChainSimulator_Api_NFTToken(t *testing.T) { log.Info("Transfer token id", "tokenID", nftTokenID) - tx = esdtNFTTransferTx(3, addrs[0].Bytes, addrs[1].Bytes, nftTokenID) + tx = esdtNFTTransferTx(nonce, addrs[0].Bytes, addrs[1].Bytes, nftTokenID) + nonce++ txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) require.NotNil(t, txResult) @@ -351,7 +368,7 @@ func TestChainSimulator_Api_NFTToken(t *testing.T) { log.Info("Change to DYNAMIC type") - tx = changeToDynamicTx(4, addrs[0].Bytes, nftTokenID) + tx = changeToDynamicTx(nonce, addrs[0].Bytes, nftTokenID) txResult, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlockToGenerateWhenExecutingTx) require.Nil(t, err) diff --git a/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go b/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go index 2e9cb01e72a..704db5455b8 100644 --- a/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go +++ b/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go @@ -37,7 +37,12 @@ func TestBootstrapComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) require.NotNil(t, managedBootstrapComponents) diff --git a/integrationTests/factory/consensusComponents/consensusComponents_test.go b/integrationTests/factory/consensusComponents/consensusComponents_test.go index d4b120a9636..a7eec6bde69 100644 --- a/integrationTests/factory/consensusComponents/consensusComponents_test.go +++ b/integrationTests/factory/consensusComponents/consensusComponents_test.go @@ -40,9 +40,19 @@ func TestConsensusComponents_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents) require.Nil(t, err) diff --git a/integrationTests/factory/dataComponents/dataComponents_test.go b/integrationTests/factory/dataComponents/dataComponents_test.go index d26cf7aa01f..d4727818994 100644 --- a/integrationTests/factory/dataComponents/dataComponents_test.go +++ b/integrationTests/factory/dataComponents/dataComponents_test.go @@ -6,11 +6,10 @@ import ( "time" "github.com/multiversx/mx-chain-core-go/data/endProcess" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/integrationTests/factory" "github.com/multiversx/mx-chain-go/node" "github.com/multiversx/mx-chain-go/testscommon/goroutines" + "github.com/stretchr/testify/require" ) func TestDataComponents_Create_Close_ShouldWork(t *testing.T) { @@ -37,10 +36,19 @@ func TestDataComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) require.Nil(t, err) require.NotNil(t, managedDataComponents) diff --git a/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go b/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go index 889c4ff38f8..d296be05b04 100644 --- a/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go +++ b/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go @@ -40,9 +40,19 @@ func TestHeartbeatComponents_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents) require.Nil(t, err) diff --git a/integrationTests/factory/processComponents/processComponents_test.go b/integrationTests/factory/processComponents/processComponents_test.go index 110a8869878..6f82bbf1188 100644 --- a/integrationTests/factory/processComponents/processComponents_test.go +++ b/integrationTests/factory/processComponents/processComponents_test.go @@ -41,9 +41,19 @@ func TestProcessComponents_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents) require.Nil(t, err) diff --git a/integrationTests/factory/stateComponents/stateComponents_test.go b/integrationTests/factory/stateComponents/stateComponents_test.go index ba93bdf8263..820694aa55e 100644 --- a/integrationTests/factory/stateComponents/stateComponents_test.go +++ b/integrationTests/factory/stateComponents/stateComponents_test.go @@ -37,9 +37,20 @@ func TestStateComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents) require.Nil(t, err) diff --git a/integrationTests/factory/statusComponents/statusComponents_test.go b/integrationTests/factory/statusComponents/statusComponents_test.go index 38527da6a41..488d20baea7 100644 --- a/integrationTests/factory/statusComponents/statusComponents_test.go +++ b/integrationTests/factory/statusComponents/statusComponents_test.go @@ -41,9 +41,19 @@ func TestStatusComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents) require.Nil(t, err) diff --git a/integrationTests/interface.go b/integrationTests/interface.go index e4be7fe388c..ad90ffbb6a3 100644 --- a/integrationTests/interface.go +++ b/integrationTests/interface.go @@ -118,5 +118,6 @@ type Facade interface { GetEligibleManagedKeys() ([]string, error) GetWaitingManagedKeys() ([]string, error) GetWaitingEpochsLeftForPublicKey(publicKey string) (uint32, error) + GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) IsInterfaceNil() bool } diff --git a/integrationTests/mock/transactionCoordinatorMock.go b/integrationTests/mock/transactionCoordinatorMock.go index c002c52cc0f..29414c117da 100644 --- a/integrationTests/mock/transactionCoordinatorMock.go +++ b/integrationTests/mock/transactionCoordinatorMock.go @@ -12,7 +12,7 @@ import ( // TransactionCoordinatorMock - type TransactionCoordinatorMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) RequestMiniBlocksAndTransactionsCalled func(header data.HeaderHandler) RequestBlockTransactionsCalled func(body *block.Body) IsDataPreparedForProcessingCalled func(haveTime func() time.Duration) error @@ -55,9 +55,9 @@ func (tcm *TransactionCoordinatorMock) CreateReceiptsHash() ([]byte, error) { } // ComputeTransactionType - -func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if tcm.ComputeTransactionTypeCalled == nil { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false } return tcm.ComputeTransactionTypeCalled(tx) diff --git a/integrationTests/multiShard/block/executingRewardMiniblocks/executingRewardMiniblocks_test.go b/integrationTests/multiShard/block/executingRewardMiniblocks/executingRewardMiniblocks_test.go index 787efdcab90..03445782c61 100644 --- a/integrationTests/multiShard/block/executingRewardMiniblocks/executingRewardMiniblocks_test.go +++ b/integrationTests/multiShard/block/executingRewardMiniblocks/executingRewardMiniblocks_test.go @@ -279,9 +279,6 @@ func updateNumberTransactionsProposed( transactionsForLeader[addressProposer] += nbTransactions } -func updateRewardsForMetachain(_ map[string]uint32, _ *integrationTests.TestProcessorNode) { -} - func verifyRewardsForMetachain( t *testing.T, mapRewardsForMeta map[string]uint32, diff --git a/integrationTests/multiShard/relayedTx/common.go b/integrationTests/multiShard/relayedTx/common.go index dec175abb73..9ffa22aa079 100644 --- a/integrationTests/multiShard/relayedTx/common.go +++ b/integrationTests/multiShard/relayedTx/common.go @@ -2,28 +2,45 @@ package relayedTx import ( "encoding/hex" - "fmt" "math/big" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/transaction" - + "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/state" + logger "github.com/multiversx/mx-chain-logger-go" ) +var log = logger.GetOrCreate("relayedtests") + // CreateGeneralSetupForRelayTxTest will create the general setup for relayed transactions -func CreateGeneralSetupForRelayTxTest() ([]*integrationTests.TestProcessorNode, []*integrationTests.TestProcessorNode, []*integrationTests.TestWalletAccount, *integrationTests.TestWalletAccount) { +func CreateGeneralSetupForRelayTxTest(baseCostFixEnabled bool) ([]*integrationTests.TestProcessorNode, []*integrationTests.TestProcessorNode, []*integrationTests.TestWalletAccount, *integrationTests.TestWalletAccount) { + initialVal := big.NewInt(10000000000) + epochsConfig := integrationTests.GetDefaultEnableEpochsConfig() + if !baseCostFixEnabled { + epochsConfig.FixRelayedBaseCostEnableEpoch = integrationTests.UnreachableEpoch + epochsConfig.FixRelayedMoveBalanceToNonPayableSCEnableEpoch = integrationTests.UnreachableEpoch + } + nodes, leaders := createAndMintNodes(initialVal, epochsConfig) + + players, relayerAccount := createAndMintPlayers(baseCostFixEnabled, nodes, initialVal) + + return nodes, leaders, players, relayerAccount +} + +func createAndMintNodes(initialVal *big.Int, enableEpochsConfig *config.EnableEpochs) ([]*integrationTests.TestProcessorNode, []*integrationTests.TestProcessorNode) { numOfShards := 2 nodesPerShard := 2 numMetachainNodes := 1 - nodes := integrationTests.CreateNodes( + nodes := integrationTests.CreateNodesWithEnableEpochsConfig( numOfShards, nodesPerShard, numMetachainNodes, + enableEpochsConfig, ) leaders := make([]*integrationTests.TestProcessorNode, numOfShards+1) @@ -34,21 +51,32 @@ func CreateGeneralSetupForRelayTxTest() ([]*integrationTests.TestProcessorNode, integrationTests.DisplayAndStartNodes(nodes) - initialVal := big.NewInt(10000000000) integrationTests.MintAllNodes(nodes, initialVal) + return nodes, leaders +} + +func createAndMintPlayers( + intraShard bool, + nodes []*integrationTests.TestProcessorNode, + initialVal *big.Int, +) ([]*integrationTests.TestWalletAccount, *integrationTests.TestWalletAccount) { + relayerShard := uint32(0) numPlayers := 5 numShards := nodes[0].ShardCoordinator.NumberOfShards() players := make([]*integrationTests.TestWalletAccount, numPlayers) for i := 0; i < numPlayers; i++ { shardId := uint32(i) % numShards + if intraShard { + shardId = relayerShard + } players[i] = integrationTests.CreateTestWalletAccount(nodes[0].ShardCoordinator, shardId) } - relayerAccount := integrationTests.CreateTestWalletAccount(nodes[0].ShardCoordinator, 0) + relayerAccount := integrationTests.CreateTestWalletAccount(nodes[0].ShardCoordinator, relayerShard) integrationTests.MintAllPlayers(nodes, []*integrationTests.TestWalletAccount{relayerAccount}, initialVal) - return nodes, leaders, players, relayerAccount + return players, relayerAccount } // CreateAndSendRelayedAndUserTx will create and send a relayed user transaction @@ -60,7 +88,7 @@ func CreateAndSendRelayedAndUserTx( value *big.Int, gasLimit uint64, txData []byte, -) *transaction.Transaction { +) (*transaction.Transaction, *transaction.Transaction) { txDispatcherNode := getNodeWithinSameShardAsPlayer(nodes, relayer.Address) userTx := createUserTx(player, rcvAddr, value, gasLimit, txData) @@ -68,10 +96,10 @@ func CreateAndSendRelayedAndUserTx( _, err := txDispatcherNode.SendTransaction(relayedTx) if err != nil { - fmt.Println(err.Error()) + log.Error("CreateAndSendRelayedAndUserTx.SendTransaction", "error", err) } - return relayedTx + return relayedTx, userTx } // CreateAndSendRelayedAndUserTxV2 will create and send a relayed user transaction for relayed v2 @@ -83,7 +111,7 @@ func CreateAndSendRelayedAndUserTxV2( value *big.Int, gasLimit uint64, txData []byte, -) *transaction.Transaction { +) (*transaction.Transaction, *transaction.Transaction) { txDispatcherNode := getNodeWithinSameShardAsPlayer(nodes, relayer.Address) userTx := createUserTx(player, rcvAddr, value, 0, txData) @@ -91,10 +119,32 @@ func CreateAndSendRelayedAndUserTxV2( _, err := txDispatcherNode.SendTransaction(relayedTx) if err != nil { - fmt.Println(err.Error()) + log.Error("CreateAndSendRelayedAndUserTxV2.SendTransaction", "error", err) } - return relayedTx + return relayedTx, userTx +} + +// CreateAndSendRelayedAndUserTxV3 will create and send a relayed user transaction v3 +func CreateAndSendRelayedAndUserTxV3( + nodes []*integrationTests.TestProcessorNode, + relayer *integrationTests.TestWalletAccount, + player *integrationTests.TestWalletAccount, + rcvAddr []byte, + value *big.Int, + gasLimit uint64, + txData []byte, +) (*transaction.Transaction, *transaction.Transaction) { + txDispatcherNode := getNodeWithinSameShardAsPlayer(nodes, relayer.Address) + + relayedTx := createRelayedTxV3(txDispatcherNode.EconomicsData, relayer, player, rcvAddr, value, gasLimit, txData) + + _, err := txDispatcherNode.SendTransaction(relayedTx) + if err != nil { + log.Error("CreateAndSendRelayedAndUserTxV3.SendTransaction", "error", err) + } + + return relayedTx, relayedTx } func createUserTx( @@ -118,6 +168,7 @@ func createUserTx( txBuff, _ := tx.GetDataForSigning(integrationTests.TestAddressPubkeyConverter, integrationTests.TestTxSignMarshalizer, integrationTests.TestTxSignHasher) tx.Signature, _ = player.SingleSigner.Sign(player.SkTxSign, txBuff) player.Nonce++ + player.Balance.Sub(player.Balance, value) return tx } @@ -131,7 +182,7 @@ func createRelayedTx( txData := core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshaled) tx := &transaction.Transaction{ Nonce: relayer.Nonce, - Value: big.NewInt(0).Set(userTx.Value), + Value: big.NewInt(0), RcvAddr: userTx.SndAddr, SndAddr: relayer.Address, GasPrice: integrationTests.MinTxGasPrice, @@ -145,9 +196,11 @@ func createRelayedTx( txBuff, _ := tx.GetDataForSigning(integrationTests.TestAddressPubkeyConverter, integrationTests.TestTxSignMarshalizer, integrationTests.TestTxSignHasher) tx.Signature, _ = relayer.SingleSigner.Sign(relayer.SkTxSign, txBuff) relayer.Nonce++ + + relayer.Balance.Sub(relayer.Balance, tx.Value) + txFee := economicsFee.ComputeTxFee(tx) relayer.Balance.Sub(relayer.Balance, txFee) - relayer.Balance.Sub(relayer.Balance, tx.Value) return tx } @@ -174,9 +227,45 @@ func createRelayedTxV2( txBuff, _ := tx.GetDataForSigning(integrationTests.TestAddressPubkeyConverter, integrationTests.TestTxSignMarshalizer, integrationTests.TestTxSignHasher) tx.Signature, _ = relayer.SingleSigner.Sign(relayer.SkTxSign, txBuff) relayer.Nonce++ + + relayer.Balance.Sub(relayer.Balance, tx.Value) + + txFee := economicsFee.ComputeTxFee(tx) + relayer.Balance.Sub(relayer.Balance, txFee) + + return tx +} + +func createRelayedTxV3( + economicsFee process.FeeHandler, + relayer *integrationTests.TestWalletAccount, + player *integrationTests.TestWalletAccount, + rcvAddr []byte, + value *big.Int, + gasLimit uint64, + txData []byte, +) *transaction.Transaction { + tx := &transaction.Transaction{ + Nonce: player.Nonce, + Value: big.NewInt(0).Set(value), + RcvAddr: rcvAddr, + SndAddr: player.Address, + GasPrice: integrationTests.MinTxGasPrice, + GasLimit: gasLimit + integrationTests.MinTxGasLimit, + Data: txData, + ChainID: integrationTests.ChainID, + Version: integrationTests.MinTransactionVersion, + RelayerAddr: relayer.Address, + } + txBuff, _ := tx.GetDataForSigning(integrationTests.TestAddressPubkeyConverter, integrationTests.TestTxSignMarshalizer, integrationTests.TestTxSignHasher) + tx.Signature, _ = player.SingleSigner.Sign(player.SkTxSign, txBuff) + tx.RelayerSignature, _ = relayer.SingleSigner.Sign(relayer.SkTxSign, txBuff) + + player.Nonce++ + player.Balance.Sub(player.Balance, value) + txFee := economicsFee.ComputeTxFee(tx) relayer.Balance.Sub(relayer.Balance, txFee) - relayer.Balance.Sub(relayer.Balance, tx.Value) return tx } @@ -194,7 +283,7 @@ func createAndSendSimpleTransaction( userTx := createUserTx(player, rcvAddr, value, gasLimit, txData) _, err := txDispatcherNode.SendTransaction(userTx) if err != nil { - fmt.Println(err.Error()) + log.Error("createAndSendSimpleTransaction.SendTransaction", "error", err) } } diff --git a/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go b/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go index 405c83d41c4..51660323333 100644 --- a/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go +++ b/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go @@ -6,10 +6,9 @@ import ( "time" "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/stretchr/testify/assert" - "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/multiShard/relayedTx" + "github.com/stretchr/testify/assert" ) func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonceShouldNotIncrementUserAccNonce(t *testing.T) { @@ -17,7 +16,7 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonceShoul t.Skip("this is not a short test") } - nodes, leaders, players, relayer := relayedTx.CreateGeneralSetupForRelayTxTest() + nodes, leaders, players, relayer := relayedTx.CreateGeneralSetupForRelayTxTest(false) defer func() { for _, n := range nodes { n.Close() @@ -33,18 +32,12 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonceShoul receiverAddress1 := []byte("12345678901234567890123456789012") receiverAddress2 := []byte("12345678901234567890123456789011") - totalFees := big.NewInt(0) - relayerInitialValue := big.NewInt(0).Set(relayer.Balance) nrRoundsToTest := int64(5) for i := int64(0); i < nrRoundsToTest; i++ { for _, player := range players { player.Nonce += 1 - relayerTx := relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress1, sendValue, integrationTests.MinTxGasLimit, []byte("")) - totalFee := nodes[0].EconomicsData.ComputeTxFee(relayerTx) - totalFees.Add(totalFees, totalFee) - relayerTx = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress2, sendValue, integrationTests.MinTxGasLimit, []byte("")) - totalFee = nodes[0].EconomicsData.ComputeTxFee(relayerTx) - totalFees.Add(totalFees, totalFee) + _, _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress1, sendValue, integrationTests.MinTxGasLimit, []byte("")) + _, _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress2, sendValue, integrationTests.MinTxGasLimit, []byte("")) } round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, leaders, round, nonce) @@ -72,9 +65,8 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonceShoul assert.Equal(t, uint64(0), account.GetNonce()) } - expectedBalance := big.NewInt(0).Sub(relayerInitialValue, totalFees) relayerAccount := relayedTx.GetUserAccount(nodes, relayer.Address) - assert.True(t, relayerAccount.GetBalance().Cmp(expectedBalance) == 0) + assert.True(t, relayerAccount.GetBalance().Cmp(relayer.Balance) == 0) } func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWithTooMuchGas(t *testing.T) { @@ -82,7 +74,7 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWithTooMuchGas( t.Skip("this is not a short test") } - nodes, idxProposers, players, relayer := relayedTx.CreateGeneralSetupForRelayTxTest() + nodes, leaders, players, relayer := relayedTx.CreateGeneralSetupForRelayTxTest(false) defer func() { for _, n := range nodes { n.Close() @@ -101,13 +93,19 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWithTooMuchGas( additionalGasLimit := uint64(100000) tooMuchGasLimit := integrationTests.MinTxGasLimit + additionalGasLimit nrRoundsToTest := int64(5) + + txsSentEachRound := big.NewInt(2) // 2 relayed txs each round + txsSentPerPlayer := big.NewInt(0).Mul(txsSentEachRound, big.NewInt(nrRoundsToTest)) + initialPlayerFunds := big.NewInt(0).Mul(sendValue, txsSentPerPlayer) + integrationTests.MintAllPlayers(nodes, players, initialPlayerFunds) + for i := int64(0); i < nrRoundsToTest; i++ { for _, player := range players { - _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress1, sendValue, tooMuchGasLimit, []byte("")) - _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress2, sendValue, tooMuchGasLimit, []byte("")) + _, _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress1, sendValue, tooMuchGasLimit, []byte("")) + _, _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress2, sendValue, tooMuchGasLimit, []byte("")) } - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, leaders, round, nonce) integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) time.Sleep(time.Second) @@ -115,7 +113,7 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWithTooMuchGas( roundToPropagateMultiShard := int64(20) for i := int64(0); i <= roundToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, leaders, round, nonce) integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) } @@ -125,8 +123,8 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWithTooMuchGas( finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) finalBalance.Mul(finalBalance, sendValue) - assert.Equal(t, receiver1.GetBalance().Cmp(finalBalance), 0) - assert.Equal(t, receiver2.GetBalance().Cmp(finalBalance), 0) + assert.Equal(t, 0, receiver1.GetBalance().Cmp(finalBalance)) + assert.Equal(t, 0, receiver2.GetBalance().Cmp(finalBalance)) players = append(players, relayer) checkPlayerBalancesWithPenalization(t, nodes, players) @@ -140,7 +138,7 @@ func checkPlayerBalancesWithPenalization( for i := 0; i < len(players); i++ { userAcc := relayedTx.GetUserAccount(nodes, players[i].Address) - assert.Equal(t, userAcc.GetBalance().Cmp(players[i].Balance), 0) + assert.Equal(t, 0, userAcc.GetBalance().Cmp(players[i].Balance)) assert.Equal(t, userAcc.GetNonce(), players[i].Nonce) } } diff --git a/integrationTests/multiShard/relayedTx/relayedTxV2_test.go b/integrationTests/multiShard/relayedTx/relayedTxV2_test.go deleted file mode 100644 index 172b9dfda1c..00000000000 --- a/integrationTests/multiShard/relayedTx/relayedTxV2_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package relayedTx - -import ( - "encoding/hex" - "math/big" - "testing" - "time" - - "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/stretchr/testify/assert" - - "github.com/multiversx/mx-chain-go/integrationTests" - "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" - vmFactory "github.com/multiversx/mx-chain-go/process/factory" -) - -func TestRelayedTransactionV2InMultiShardEnvironmentWithSmartContractTX(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } - - nodes, leaders, players, relayer := CreateGeneralSetupForRelayTxTest() - defer func() { - for _, n := range nodes { - n.Close() - } - }() - - sendValue := big.NewInt(5) - round := uint64(0) - nonce := uint64(0) - round = integrationTests.IncrementAndPrintRound(round) - nonce++ - - receiverAddress1 := []byte("12345678901234567890123456789012") - receiverAddress2 := []byte("12345678901234567890123456789011") - - ownerNode := nodes[0] - initialSupply := "00" + hex.EncodeToString(big.NewInt(100000000000).Bytes()) - scCode := wasm.GetSCCode("../../vm/wasm/testdata/erc20-c-03/wrc20_wasm.wasm") - scAddress, _ := ownerNode.BlockchainHook.NewAddress(ownerNode.OwnAccount.Address, ownerNode.OwnAccount.Nonce, vmFactory.WasmVirtualMachine) - - integrationTests.CreateAndSendTransactionWithGasLimit( - nodes[0], - big.NewInt(0), - 2000000, - make([]byte, 32), - []byte(wasm.CreateDeployTxData(scCode)+"@"+initialSupply), - integrationTests.ChainID, - integrationTests.MinTransactionVersion, - ) - - transferTokenVMGas := uint64(720000) - transferTokenBaseGas := ownerNode.EconomicsData.ComputeGasLimit(&transaction.Transaction{Data: []byte("transferToken@" + hex.EncodeToString(receiverAddress1) + "@00" + hex.EncodeToString(sendValue.Bytes()))}) - transferTokenFullGas := transferTokenBaseGas + transferTokenVMGas - - initialTokenSupply := big.NewInt(1000000000) - initialPlusForGas := uint64(100000) - for _, player := range players { - integrationTests.CreateAndSendTransactionWithGasLimit( - ownerNode, - big.NewInt(0), - transferTokenFullGas+initialPlusForGas, - scAddress, - []byte("transferToken@"+hex.EncodeToString(player.Address)+"@00"+hex.EncodeToString(initialTokenSupply.Bytes())), - integrationTests.ChainID, - integrationTests.MinTransactionVersion, - ) - } - - roundToPropagateMultiShard := int64(20) - for i := int64(0); i <= roundToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, leaders, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } - - nrRoundsToTest := int64(5) - for i := int64(0); i < nrRoundsToTest; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, leaders, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - - for _, player := range players { - _ = CreateAndSendRelayedAndUserTxV2(nodes, relayer, player, scAddress, big.NewInt(0), - transferTokenFullGas, []byte("transferToken@"+hex.EncodeToString(receiverAddress1)+"@00"+hex.EncodeToString(sendValue.Bytes()))) - _ = CreateAndSendRelayedAndUserTxV2(nodes, relayer, player, scAddress, big.NewInt(0), - transferTokenFullGas, []byte("transferToken@"+hex.EncodeToString(receiverAddress2)+"@00"+hex.EncodeToString(sendValue.Bytes()))) - } - - time.Sleep(integrationTests.StepDelay) - } - - for i := int64(0); i <= roundToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, leaders, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } - - time.Sleep(time.Second) - - finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) - finalBalance = big.NewInt(0).Mul(finalBalance, sendValue) - - checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) - checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) - - checkPlayerBalances(t, nodes, players) - - userAcc := GetUserAccount(nodes, relayer.Address) - assert.Equal(t, 1, userAcc.GetBalance().Cmp(relayer.Balance)) -} diff --git a/integrationTests/multiShard/relayedTx/relayedTx_test.go b/integrationTests/multiShard/relayedTx/relayedTx_test.go index 412ae4b1dd9..bba732f7a5b 100644 --- a/integrationTests/multiShard/relayedTx/relayedTx_test.go +++ b/integrationTests/multiShard/relayedTx/relayedTx_test.go @@ -22,332 +22,397 @@ import ( "github.com/multiversx/mx-chain-go/vm" ) +type createAndSendRelayedAndUserTxFuncType = func( + nodes []*integrationTests.TestProcessorNode, + relayer *integrationTests.TestWalletAccount, + player *integrationTests.TestWalletAccount, + rcvAddr []byte, + value *big.Int, + gasLimit uint64, + txData []byte, +) (*transaction.Transaction, *transaction.Transaction) + func TestRelayedTransactionInMultiShardEnvironmentWithNormalTx(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } + t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithNormalTx(CreateAndSendRelayedAndUserTx, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithNormalTx(CreateAndSendRelayedAndUserTxV3, true)) +} - nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest() - defer func() { - for _, n := range nodes { - n.Close() - } - }() +func TestRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(t *testing.T) { + t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(CreateAndSendRelayedAndUserTx, false)) + t.Run("relayed v2", testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(CreateAndSendRelayedAndUserTxV2, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(CreateAndSendRelayedAndUserTxV3, true)) +} - sendValue := big.NewInt(5) - round := uint64(0) - nonce := uint64(0) - round = integrationTests.IncrementAndPrintRound(round) - nonce++ +func TestRelayedTransactionInMultiShardEnvironmentWithESDTTX(t *testing.T) { + t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithESDTTX(CreateAndSendRelayedAndUserTx, false)) + t.Run("relayed v2", testRelayedTransactionInMultiShardEnvironmentWithESDTTX(CreateAndSendRelayedAndUserTxV2, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithESDTTX(CreateAndSendRelayedAndUserTxV3, true)) +} - receiverAddress1 := []byte("12345678901234567890123456789012") - receiverAddress2 := []byte("12345678901234567890123456789011") +func TestRelayedTransactionInMultiShardEnvironmentWithAttestationContract(t *testing.T) { + t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithAttestationContract(CreateAndSendRelayedAndUserTx, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithAttestationContract(CreateAndSendRelayedAndUserTxV3, true)) +} - nrRoundsToTest := int64(5) - for i := int64(0); i < nrRoundsToTest; i++ { - for _, player := range players { - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress1, sendValue, integrationTests.MinTxGasLimit, []byte("")) - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress2, sendValue, integrationTests.MinTxGasLimit, []byte("")) +func testRelayedTransactionInMultiShardEnvironmentWithNormalTx( + createAndSendRelayedAndUserTxFunc createAndSendRelayedAndUserTxFuncType, + baseCostFixEnabled bool, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") } - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest(baseCostFixEnabled) + defer func() { + for _, n := range nodes { + n.Close() + } + }() - time.Sleep(integrationTests.StepDelay) - } + sendValue := big.NewInt(5) + round := uint64(0) + nonce := uint64(0) + round = integrationTests.IncrementAndPrintRound(round) + nonce++ - roundToPropagateMultiShard := int64(20) - for i := int64(0); i <= roundToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } + receiverAddress1 := []byte("12345678901234567890123456789012") + receiverAddress2 := []byte("12345678901234567890123456789011") - time.Sleep(time.Second) - receiver1 := GetUserAccount(nodes, receiverAddress1) - receiver2 := GetUserAccount(nodes, receiverAddress2) + nrRoundsToTest := int64(5) - finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) - finalBalance.Mul(finalBalance, sendValue) - assert.Equal(t, receiver1.GetBalance().Cmp(finalBalance), 0) - assert.Equal(t, receiver2.GetBalance().Cmp(finalBalance), 0) + txsSentEachRound := big.NewInt(2) // 2 relayed txs each round + txsSentPerPlayer := big.NewInt(0).Mul(txsSentEachRound, big.NewInt(nrRoundsToTest)) + initialPlayerFunds := big.NewInt(0).Mul(sendValue, txsSentPerPlayer) + integrationTests.MintAllPlayers(nodes, players, initialPlayerFunds) - players = append(players, relayer) - checkPlayerBalances(t, nodes, players) -} + for i := int64(0); i < nrRoundsToTest; i++ { + for _, player := range players { + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, receiverAddress1, sendValue, integrationTests.MinTxGasLimit, []byte("")) + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, receiverAddress2, sendValue, integrationTests.MinTxGasLimit, []byte("")) + } -func TestRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + + time.Sleep(integrationTests.StepDelay) + } + + roundToPropagateMultiShard := int64(20) + for i := int64(0); i <= roundToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + } + + time.Sleep(time.Second) + receiver1 := GetUserAccount(nodes, receiverAddress1) + receiver2 := GetUserAccount(nodes, receiverAddress2) + + finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) + finalBalance.Mul(finalBalance, sendValue) + assert.Equal(t, receiver1.GetBalance().Cmp(finalBalance), 0) + assert.Equal(t, receiver2.GetBalance().Cmp(finalBalance), 0) + + players = append(players, relayer) + checkPlayerBalances(t, nodes, players) } +} - nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest() - defer func() { - for _, n := range nodes { - n.Close() +func testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX( + createAndSendRelayedAndUserTxFunc createAndSendRelayedAndUserTxFuncType, + baseCostFixEnabled bool, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") } - }() - - sendValue := big.NewInt(5) - round := uint64(0) - nonce := uint64(0) - round = integrationTests.IncrementAndPrintRound(round) - nonce++ - - receiverAddress1 := []byte("12345678901234567890123456789012") - receiverAddress2 := []byte("12345678901234567890123456789011") - - ownerNode := nodes[0] - initialSupply := "00" + hex.EncodeToString(big.NewInt(100000000000).Bytes()) - scCode := wasm.GetSCCode("../../vm/wasm/testdata/erc20-c-03/wrc20_wasm.wasm") - scAddress, _ := ownerNode.BlockchainHook.NewAddress(ownerNode.OwnAccount.Address, ownerNode.OwnAccount.Nonce, vmFactory.WasmVirtualMachine) - - integrationTests.CreateAndSendTransactionWithGasLimit( - nodes[0], - big.NewInt(0), - 200000, - make([]byte, 32), - []byte(wasm.CreateDeployTxData(scCode)+"@"+initialSupply), - integrationTests.ChainID, - integrationTests.MinTransactionVersion, - ) - - transferTokenVMGas := uint64(720000) - transferTokenBaseGas := ownerNode.EconomicsData.ComputeGasLimit(&transaction.Transaction{Data: []byte("transferToken@" + hex.EncodeToString(receiverAddress1) + "@00" + hex.EncodeToString(sendValue.Bytes()))}) - transferTokenFullGas := transferTokenBaseGas + transferTokenVMGas - - initialTokenSupply := big.NewInt(1000000000) - initialPlusForGas := uint64(100000) - for _, player := range players { + + nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest(baseCostFixEnabled) + defer func() { + for _, n := range nodes { + n.Close() + } + }() + + sendValue := big.NewInt(5) + round := uint64(0) + nonce := uint64(0) + round = integrationTests.IncrementAndPrintRound(round) + nonce++ + + receiverAddress1 := []byte("12345678901234567890123456789012") + receiverAddress2 := []byte("12345678901234567890123456789011") + + integrationTests.MintAllPlayers(nodes, players, big.NewInt(1)) + + ownerNode := nodes[0] + initialSupply := "00" + hex.EncodeToString(big.NewInt(100000000000).Bytes()) + scCode := wasm.GetSCCode("../../vm/wasm/testdata/erc20-c-03/wrc20_wasm.wasm") + scAddress, _ := ownerNode.BlockchainHook.NewAddress(ownerNode.OwnAccount.Address, ownerNode.OwnAccount.Nonce, vmFactory.WasmVirtualMachine) + integrationTests.CreateAndSendTransactionWithGasLimit( - ownerNode, + nodes[0], big.NewInt(0), - transferTokenFullGas+initialPlusForGas, - scAddress, - []byte("transferToken@"+hex.EncodeToString(player.Address)+"@00"+hex.EncodeToString(initialTokenSupply.Bytes())), + 200000, + make([]byte, 32), + []byte(wasm.CreateDeployTxData(scCode)+"@"+initialSupply), integrationTests.ChainID, integrationTests.MinTransactionVersion, ) - } - time.Sleep(time.Second) - nrRoundsToTest := int64(5) - for i := int64(0); i < nrRoundsToTest; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + transferTokenVMGas := uint64(720000) + transferTokenBaseGas := ownerNode.EconomicsData.ComputeGasLimit(&transaction.Transaction{Data: []byte("transferToken@" + hex.EncodeToString(receiverAddress1) + "@00" + hex.EncodeToString(sendValue.Bytes()))}) + transferTokenFullGas := transferTokenBaseGas + transferTokenVMGas + initialTokenSupply := big.NewInt(1000000000) + initialPlusForGas := uint64(100000) for _, player := range players { - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, scAddress, big.NewInt(0), - transferTokenFullGas, []byte("transferToken@"+hex.EncodeToString(receiverAddress1)+"@00"+hex.EncodeToString(sendValue.Bytes()))) - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, scAddress, big.NewInt(0), - transferTokenFullGas, []byte("transferToken@"+hex.EncodeToString(receiverAddress2)+"@00"+hex.EncodeToString(sendValue.Bytes()))) + integrationTests.CreateAndSendTransactionWithGasLimit( + ownerNode, + big.NewInt(0), + transferTokenFullGas+initialPlusForGas, + scAddress, + []byte("transferToken@"+hex.EncodeToString(player.Address)+"@00"+hex.EncodeToString(initialTokenSupply.Bytes())), + integrationTests.ChainID, + integrationTests.MinTransactionVersion, + ) } + time.Sleep(time.Second) - time.Sleep(integrationTests.StepDelay) - } - time.Sleep(time.Second) + nrRoundsToTest := int64(5) + for i := int64(0); i < nrRoundsToTest; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - roundToPropagateMultiShard := int64(25) - for i := int64(0); i <= roundToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } + for _, player := range players { + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, scAddress, big.NewInt(0), + transferTokenFullGas, []byte("transferToken@"+hex.EncodeToString(receiverAddress1)+"@00"+hex.EncodeToString(sendValue.Bytes()))) + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, scAddress, big.NewInt(0), + transferTokenFullGas, []byte("transferToken@"+hex.EncodeToString(receiverAddress2)+"@00"+hex.EncodeToString(sendValue.Bytes()))) + } + + time.Sleep(integrationTests.StepDelay) + } + time.Sleep(time.Second) - time.Sleep(time.Second) + roundToPropagateMultiShard := int64(40) + for i := int64(0); i <= roundToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + } - finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) - finalBalance.Mul(finalBalance, sendValue) + time.Sleep(time.Second) - checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) - checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) + finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) + finalBalance.Mul(finalBalance, sendValue) - checkPlayerBalances(t, nodes, players) + checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) + checkSCBalance(t, ownerNode, scAddress, receiverAddress2, finalBalance) - userAcc := GetUserAccount(nodes, relayer.Address) - assert.Equal(t, userAcc.GetBalance().Cmp(relayer.Balance), 1) -} + checkPlayerBalances(t, nodes, players) -func TestRelayedTransactionInMultiShardEnvironmentWithESDTTX(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") + userAcc := GetUserAccount(nodes, relayer.Address) + assert.Equal(t, 1, userAcc.GetBalance().Cmp(relayer.Balance)) } +} - nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest() - defer func() { - for _, n := range nodes { - n.Close() +func testRelayedTransactionInMultiShardEnvironmentWithESDTTX( + createAndSendRelayedAndUserTxFunc createAndSendRelayedAndUserTxFuncType, + baseCostFixEnabled bool, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") } - }() - - sendValue := big.NewInt(5) - round := uint64(0) - nonce := uint64(0) - round = integrationTests.IncrementAndPrintRound(round) - nonce++ - - receiverAddress1 := []byte("12345678901234567890123456789012") - receiverAddress2 := []byte("12345678901234567890123456789011") - - // ------- send token issue - issuePrice := big.NewInt(1000) - initalSupply := big.NewInt(10000000000) - tokenIssuer := nodes[0] - txData := "issue" + - "@" + hex.EncodeToString([]byte("robertWhyNot")) + - "@" + hex.EncodeToString([]byte("RBT")) + - "@" + hex.EncodeToString(initalSupply.Bytes()) + - "@" + hex.EncodeToString([]byte{6}) - integrationTests.CreateAndSendTransaction(tokenIssuer, nodes, issuePrice, vm.ESDTSCAddress, txData, core.MinMetaTxExtraGasCost) - - time.Sleep(time.Second) - nrRoundsToPropagateMultiShard := int64(10) - for i := int64(0); i < nrRoundsToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - time.Sleep(integrationTests.StepDelay) - } - time.Sleep(time.Second) - tokenIdenfitifer := string(integrationTests.GetTokenIdentifier(nodes, []byte("RBT"))) - CheckAddressHasTokens(t, tokenIssuer.OwnAccount.Address, nodes, tokenIdenfitifer, initalSupply) + nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest(baseCostFixEnabled) + defer func() { + for _, n := range nodes { + n.Close() + } + }() + + sendValue := big.NewInt(5) + round := uint64(0) + nonce := uint64(0) + round = integrationTests.IncrementAndPrintRound(round) + nonce++ + + receiverAddress1 := []byte("12345678901234567890123456789012") + receiverAddress2 := []byte("12345678901234567890123456789011") + + // ------- send token issue + issuePrice := big.NewInt(1000) + initalSupply := big.NewInt(10000000000) + tokenIssuer := nodes[0] + txData := "issue" + + "@" + hex.EncodeToString([]byte("robertWhyNot")) + + "@" + hex.EncodeToString([]byte("RBT")) + + "@" + hex.EncodeToString(initalSupply.Bytes()) + + "@" + hex.EncodeToString([]byte{6}) + integrationTests.CreateAndSendTransaction(tokenIssuer, nodes, issuePrice, vm.ESDTSCAddress, txData, core.MinMetaTxExtraGasCost) + + time.Sleep(time.Second) + nrRoundsToPropagateMultiShard := int64(10) + for i := int64(0); i < nrRoundsToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + time.Sleep(integrationTests.StepDelay) + } + time.Sleep(time.Second) - // ------ send tx to players - valueToTopUp := big.NewInt(100000000) - txData = core.BuiltInFunctionESDTTransfer + "@" + hex.EncodeToString([]byte(tokenIdenfitifer)) + "@" + hex.EncodeToString(valueToTopUp.Bytes()) - for _, player := range players { - integrationTests.CreateAndSendTransaction(tokenIssuer, nodes, big.NewInt(0), player.Address, txData, integrationTests.AdditionalGasLimit) - } + tokenIdenfitifer := string(integrationTests.GetTokenIdentifier(nodes, []byte("RBT"))) + CheckAddressHasTokens(t, tokenIssuer.OwnAccount.Address, nodes, tokenIdenfitifer, initalSupply) - time.Sleep(time.Second) - for i := int64(0); i < nrRoundsToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - time.Sleep(integrationTests.StepDelay) - } - time.Sleep(time.Second) - - txData = core.BuiltInFunctionESDTTransfer + "@" + hex.EncodeToString([]byte(tokenIdenfitifer)) + "@" + hex.EncodeToString(sendValue.Bytes()) - transferTokenESDTGas := uint64(1) - transferTokenBaseGas := tokenIssuer.EconomicsData.ComputeGasLimit(&transaction.Transaction{Data: []byte(txData)}) - transferTokenFullGas := transferTokenBaseGas + transferTokenESDTGas + uint64(100) // use more gas to simulate gas refund - nrRoundsToTest := int64(5) - for i := int64(0); i < nrRoundsToTest; i++ { + // ------ send tx to players + valueToTopUp := big.NewInt(100000000) + txData = core.BuiltInFunctionESDTTransfer + "@" + hex.EncodeToString([]byte(tokenIdenfitifer)) + "@" + hex.EncodeToString(valueToTopUp.Bytes()) for _, player := range players { - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress1, big.NewInt(0), transferTokenFullGas, []byte(txData)) - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, receiverAddress2, big.NewInt(0), transferTokenFullGas, []byte(txData)) + integrationTests.CreateAndSendTransaction(tokenIssuer, nodes, big.NewInt(0), player.Address, txData, integrationTests.AdditionalGasLimit) } - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + time.Sleep(time.Second) + for i := int64(0); i < nrRoundsToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + time.Sleep(integrationTests.StepDelay) + } + time.Sleep(time.Second) + + txData = core.BuiltInFunctionESDTTransfer + "@" + hex.EncodeToString([]byte(tokenIdenfitifer)) + "@" + hex.EncodeToString(sendValue.Bytes()) + transferTokenESDTGas := uint64(1) + transferTokenBaseGas := tokenIssuer.EconomicsData.ComputeGasLimit(&transaction.Transaction{Data: []byte(txData)}) + transferTokenFullGas := transferTokenBaseGas + transferTokenESDTGas + uint64(100) // use more gas to simulate gas refund + nrRoundsToTest := int64(5) + for i := int64(0); i < nrRoundsToTest; i++ { + for _, player := range players { + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, receiverAddress1, big.NewInt(0), transferTokenFullGas, []byte(txData)) + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, receiverAddress2, big.NewInt(0), transferTokenFullGas, []byte(txData)) + } + + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + + time.Sleep(integrationTests.StepDelay) + } - time.Sleep(integrationTests.StepDelay) - } + nrRoundsToPropagateMultiShard = int64(20) + for i := int64(0); i <= nrRoundsToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + } - nrRoundsToPropagateMultiShard = int64(20) - for i := int64(0); i <= nrRoundsToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + time.Sleep(time.Second) + finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) + finalBalance.Mul(finalBalance, sendValue) + CheckAddressHasTokens(t, receiverAddress1, nodes, tokenIdenfitifer, finalBalance) + CheckAddressHasTokens(t, receiverAddress2, nodes, tokenIdenfitifer, finalBalance) + + players = append(players, relayer) + checkPlayerBalances(t, nodes, players) } +} - time.Sleep(time.Second) - finalBalance := big.NewInt(0).Mul(big.NewInt(int64(len(players))), big.NewInt(nrRoundsToTest)) - finalBalance.Mul(finalBalance, sendValue) - CheckAddressHasTokens(t, receiverAddress1, nodes, tokenIdenfitifer, finalBalance) - CheckAddressHasTokens(t, receiverAddress2, nodes, tokenIdenfitifer, finalBalance) +func testRelayedTransactionInMultiShardEnvironmentWithAttestationContract( + createAndSendRelayedAndUserTxFunc createAndSendRelayedAndUserTxFuncType, + relayedV3Test bool, +) func(t *testing.T) { + return func(t *testing.T) { - players = append(players, relayer) - checkPlayerBalances(t, nodes, players) -} + if testing.Short() { + t.Skip("this is not a short test") + } -func TestRelayedTransactionInMultiShardEnvironmentWithAttestationContract(t *testing.T) { - if testing.Short() { - t.Skip("this is not a short test") - } + nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest(relayedV3Test) + defer func() { + for _, n := range nodes { + n.Close() + } + }() - nodes, idxProposers, players, relayer := CreateGeneralSetupForRelayTxTest() - defer func() { - for _, n := range nodes { - n.Close() + for _, node := range nodes { + node.EconomicsData.SetMaxGasLimitPerBlock(1500000000, 0) } - }() - for _, node := range nodes { - node.EconomicsData.SetMaxGasLimitPerBlock(1500000000, 0) - } + round := uint64(0) + nonce := uint64(0) + round = integrationTests.IncrementAndPrintRound(round) + nonce++ - round := uint64(0) - nonce := uint64(0) - round = integrationTests.IncrementAndPrintRound(round) - nonce++ - - ownerNode := nodes[0] - scCode := wasm.GetSCCode("attestation.wasm") - scAddress, _ := ownerNode.BlockchainHook.NewAddress(ownerNode.OwnAccount.Address, ownerNode.OwnAccount.Nonce, vmFactory.WasmVirtualMachine) - - registerValue := big.NewInt(100) - integrationTests.CreateAndSendTransactionWithGasLimit( - nodes[0], - big.NewInt(0), - 2000000, - make([]byte, 32), - []byte(wasm.CreateDeployTxData(scCode)+"@"+hex.EncodeToString(registerValue.Bytes())+"@"+hex.EncodeToString(relayer.Address)+"@"+"ababab"), - integrationTests.ChainID, - integrationTests.MinTransactionVersion, - ) - time.Sleep(time.Second) - - registerVMGas := uint64(10000000) - savePublicInfoVMGas := uint64(10000000) - attestVMGas := uint64(10000000) - - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - - uniqueIDs := make([]string, len(players)) - for i, player := range players { - uniqueIDs[i] = core.UniqueIdentifier() - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, scAddress, registerValue, - registerVMGas, []byte("register@"+hex.EncodeToString([]byte(uniqueIDs[i])))) - } - time.Sleep(time.Second) + ownerNode := nodes[0] + scCode := wasm.GetSCCode("attestation.wasm") + scAddress, _ := ownerNode.BlockchainHook.NewAddress(ownerNode.OwnAccount.Address, ownerNode.OwnAccount.Nonce, vmFactory.WasmVirtualMachine) - nrRoundsToPropagateMultiShard := int64(10) - for i := int64(0); i <= nrRoundsToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } + registerValue := big.NewInt(100) + integrationTests.CreateAndSendTransactionWithGasLimit( + nodes[0], + big.NewInt(0), + 2000000, + make([]byte, 32), + []byte(wasm.CreateDeployTxData(scCode)+"@"+hex.EncodeToString(registerValue.Bytes())+"@"+hex.EncodeToString(relayer.Address)+"@"+"ababab"), + integrationTests.ChainID, + integrationTests.MinTransactionVersion, + ) + time.Sleep(time.Second) - cryptoHook := hooks.NewVMCryptoHook() - privateInfos := make([]string, len(players)) - for i := range players { - privateInfos[i] = core.UniqueIdentifier() - publicInfo, _ := cryptoHook.Keccak256([]byte(privateInfos[i])) - createAndSendSimpleTransaction(nodes, relayer, scAddress, big.NewInt(0), savePublicInfoVMGas, - []byte("savePublicInfo@"+hex.EncodeToString([]byte(uniqueIDs[i]))+"@"+hex.EncodeToString(publicInfo))) - } - time.Sleep(time.Second) + registerVMGas := uint64(10000000) + savePublicInfoVMGas := uint64(10000000) + attestVMGas := uint64(10000000) - nrRoundsToPropagate := int64(5) - for i := int64(0); i <= nrRoundsToPropagate; i++ { round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } - for i, player := range players { - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, scAddress, big.NewInt(0), attestVMGas, - []byte("attest@"+hex.EncodeToString([]byte(uniqueIDs[i]))+"@"+hex.EncodeToString([]byte(privateInfos[i])))) - _ = CreateAndSendRelayedAndUserTx(nodes, relayer, player, scAddress, registerValue, - registerVMGas, []byte("register@"+hex.EncodeToString([]byte(uniqueIDs[i])))) - } - time.Sleep(time.Second) + integrationTests.MintAllPlayers(nodes, players, registerValue) - nrRoundsToPropagateMultiShard = int64(20) - for i := int64(0); i <= nrRoundsToPropagateMultiShard; i++ { - round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) - integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) - } + uniqueIDs := make([]string, len(players)) + for i, player := range players { + uniqueIDs[i] = core.UniqueIdentifier() + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, scAddress, registerValue, + registerVMGas, []byte("register@"+hex.EncodeToString([]byte(uniqueIDs[i])))) + } + time.Sleep(time.Second) - for i, player := range players { - checkAttestedPublicKeys(t, ownerNode, scAddress, []byte(uniqueIDs[i]), player.Address) + nrRoundsToPropagateMultiShard := int64(10) + for i := int64(0); i <= nrRoundsToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + } + + cryptoHook := hooks.NewVMCryptoHook() + privateInfos := make([]string, len(players)) + for i := range players { + privateInfos[i] = core.UniqueIdentifier() + publicInfo, _ := cryptoHook.Keccak256([]byte(privateInfos[i])) + createAndSendSimpleTransaction(nodes, relayer, scAddress, big.NewInt(0), savePublicInfoVMGas, + []byte("savePublicInfo@"+hex.EncodeToString([]byte(uniqueIDs[i]))+"@"+hex.EncodeToString(publicInfo))) + } + time.Sleep(time.Second) + + nrRoundsToPropagate := int64(5) + for i := int64(0); i <= nrRoundsToPropagate; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + } + + integrationTests.MintAllPlayers(nodes, players, registerValue) + + for i, player := range players { + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, scAddress, big.NewInt(0), attestVMGas, + []byte("attest@"+hex.EncodeToString([]byte(uniqueIDs[i]))+"@"+hex.EncodeToString([]byte(privateInfos[i])))) + _, _ = createAndSendRelayedAndUserTxFunc(nodes, relayer, player, scAddress, registerValue, + registerVMGas, []byte("register@"+hex.EncodeToString([]byte(uniqueIDs[i])))) + } + time.Sleep(time.Second) + + nrRoundsToPropagateMultiShard = int64(20) + for i := int64(0); i <= nrRoundsToPropagateMultiShard; i++ { + round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) + integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) + } + + for i, player := range players { + checkAttestedPublicKeys(t, ownerNode, scAddress, []byte(uniqueIDs[i]), player.Address) + } } } @@ -378,7 +443,7 @@ func checkSCBalance(t *testing.T, node *integrationTests.TestProcessorNode, scAd }) assert.Nil(t, err) actualBalance := big.NewInt(0).SetBytes(vmOutput.ReturnData[0]) - assert.Equal(t, balance, actualBalance) + assert.Equal(t, balance.String(), actualBalance.String()) } func checkPlayerBalances( @@ -387,7 +452,7 @@ func checkPlayerBalances( players []*integrationTests.TestWalletAccount) { for _, player := range players { userAcc := GetUserAccount(nodes, player.Address) - assert.Equal(t, userAcc.GetBalance().Cmp(player.Balance), 0) + assert.Equal(t, 0, userAcc.GetBalance().Cmp(player.Balance)) assert.Equal(t, userAcc.GetNonce(), player.Nonce) } } diff --git a/integrationTests/multiShard/smartContract/dns/dns_test.go b/integrationTests/multiShard/smartContract/dns/dns_test.go index 98dc1a1d674..40f7117e469 100644 --- a/integrationTests/multiShard/smartContract/dns/dns_test.go +++ b/integrationTests/multiShard/smartContract/dns/dns_test.go @@ -203,7 +203,7 @@ func sendRegisterUserNameAsRelayedTx( for i, player := range players { userName := generateNewUserName() scAddress := selectDNSAddressFromUserName(sortedDNSAddresses, userName) - _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, []byte(scAddress), dnsRegisterValue, + _, _ = relayedTx.CreateAndSendRelayedAndUserTx(nodes, relayer, player, []byte(scAddress), dnsRegisterValue, gasLimit, []byte("register@"+hex.EncodeToString([]byte(userName)))) userNames[i] = userName } diff --git a/integrationTests/multiShard/smartContract/scCallingSC_test.go b/integrationTests/multiShard/smartContract/scCallingSC_test.go index 74307489b9c..9f46f6e8f03 100644 --- a/integrationTests/multiShard/smartContract/scCallingSC_test.go +++ b/integrationTests/multiShard/smartContract/scCallingSC_test.go @@ -960,16 +960,6 @@ func TestSCNonPayableIntraShardErrorShouldProcessBlock(t *testing.T) { } } -func getNodeIndex(nodeList []*integrationTests.TestProcessorNode, node *integrationTests.TestProcessorNode) (int, error) { - for i := range nodeList { - if node == nodeList[i] { - return i, nil - } - } - - return 0, errors.New("no such node in list") -} - func putDeploySCToDataPool( fileName string, pubkey []byte, diff --git a/integrationTests/realcomponents/processorRunner.go b/integrationTests/realcomponents/processorRunner.go index 982a3fbe68a..20a33dcffc8 100644 --- a/integrationTests/realcomponents/processorRunner.go +++ b/integrationTests/realcomponents/processorRunner.go @@ -72,6 +72,9 @@ func NewProcessorRunner(tb testing.TB, config config.Configs) *ProcessorRunner { } func (pr *ProcessorRunner) createComponents(tb testing.TB) { + var err error + require.Nil(tb, err) + pr.createCoreComponents(tb) pr.createCryptoComponents(tb) pr.createStatusCoreComponents(tb) diff --git a/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go b/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go index 6685b5b1433..ddcd2d9ba34 100644 --- a/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go +++ b/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go @@ -84,12 +84,11 @@ func TestShardShouldNotProposeAndExecuteTwoBlocksInSameRound(t *testing.T) { } // TestShardShouldProposeBlockContainingInvalidTransactions tests the following scenario: -// 1. generate 3 move balance transactions: one that can be executed, one that can not be executed but the account has -// the balance for the fee and one that is completely invalid (no balance left for it) +// 1. generate 3 move balance transactions: one that can be executed, one to be processed as invalid, and one that isn't executable (no balance left for fee). // 2. proposer will have those 3 transactions in its pools and will propose a block // 3. another node will be able to sync the proposed block (and request - receive) the 2 transactions that // will end up in the block (one valid and one invalid) -// 4. the non-executable transaction will be removed from the proposer's pool +// 4. the non-executable transaction will not be immediately removed from the proposer's pool. See MX-16200. func TestShardShouldProposeBlockContainingInvalidTransactions(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") @@ -197,7 +196,18 @@ func testStateOnNodes(t *testing.T, nodes []*integrationTests.TestProcessorNode, testTxIsInMiniblock(t, proposer, hashes[txValidIdx], block.TxBlock) testTxIsInMiniblock(t, proposer, hashes[txInvalidIdx], block.InvalidBlock) testTxIsInNotInBody(t, proposer, hashes[txDeletedIdx]) - testTxHashNotPresentInPool(t, proposer, hashes[txDeletedIdx]) + + // Removed from mempool. + _, ok := proposer.DataPool.Transactions().SearchFirstData(hashes[txValidIdx]) + assert.False(t, ok) + + // Removed from mempool. + _, ok = proposer.DataPool.Transactions().SearchFirstData(hashes[txInvalidIdx]) + assert.False(t, ok) + + // Not removed from mempool (see MX-16200). + _, ok = proposer.DataPool.Transactions().SearchFirstData(hashes[txDeletedIdx]) + assert.True(t, ok) } func testSameBlockHeight(t *testing.T, nodes []*integrationTests.TestProcessorNode, idxProposer int, expectedHeight uint64) { @@ -210,11 +220,6 @@ func testSameBlockHeight(t *testing.T, nodes []*integrationTests.TestProcessorNo } } -func testTxHashNotPresentInPool(t *testing.T, proposer *integrationTests.TestProcessorNode, hash []byte) { - txCache := proposer.DataPool.Transactions() - _, ok := txCache.SearchFirstData(hash) - assert.False(t, ok) -} func testTxIsInMiniblock(t *testing.T, proposer *integrationTests.TestProcessorNode, hash []byte, bt block.Type) { hdrHandler := proposer.BlockChain.GetCurrentBlockHeader() diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index d6d9a7572a7..41e2180880d 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -1713,6 +1713,7 @@ func (tpn *TestProcessorNode) initInnerProcessors(gasMap map[string]map[string]u txTypeHandler, _ := coordinator.NewTxTypeHandler(argsTxTypeHandler) tpn.GasHandler, _ = preprocess.NewGasComputation(tpn.EconomicsData, txTypeHandler, tpn.EnableEpochsHandler) badBlocksHandler, _ := tpn.InterimProcContainer.Get(dataBlock.InvalidBlock) + guardianChecker := &guardianMocks.GuardedAccountHandlerStub{} argsNewScProcessor := scrCommon.ArgsNewSmartContractProcessor{ VmContainer: tpn.VMContainer, @@ -1758,7 +1759,7 @@ func (tpn *TestProcessorNode) initInnerProcessors(gasMap map[string]map[string]u ScrForwarder: tpn.ScrForwarder, EnableRoundsHandler: tpn.EnableRoundsHandler, EnableEpochsHandler: tpn.EnableEpochsHandler, - GuardianChecker: &guardianMocks.GuardedAccountHandlerStub{}, + GuardianChecker: guardianChecker, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, TxLogsProcessor: tpn.TransactionLogProcessor, } @@ -2640,22 +2641,29 @@ func (tpn *TestProcessorNode) SendTransaction(tx *dataTransaction.Transaction) ( if len(tx.GuardianAddr) == TestAddressPubkeyConverter.Len() { guardianAddress = TestAddressPubkeyConverter.SilentEncode(tx.GuardianAddr, log) } + + relayerAddress := "" + if len(tx.RelayerAddr) == TestAddressPubkeyConverter.Len() { + relayerAddress = TestAddressPubkeyConverter.SilentEncode(tx.RelayerAddr, log) + } createTxArgs := &external.ArgsCreateTransaction{ - Nonce: tx.Nonce, - Value: tx.Value.String(), - Receiver: encodedRcvAddr, - ReceiverUsername: nil, - Sender: encodedSndAddr, - SenderUsername: nil, - GasPrice: tx.GasPrice, - GasLimit: tx.GasLimit, - DataField: tx.Data, - SignatureHex: hex.EncodeToString(tx.Signature), - ChainID: string(tx.ChainID), - Version: tx.Version, - Options: tx.Options, - Guardian: guardianAddress, - GuardianSigHex: hex.EncodeToString(tx.GuardianSignature), + Nonce: tx.Nonce, + Value: tx.Value.String(), + Receiver: encodedRcvAddr, + ReceiverUsername: nil, + Sender: encodedSndAddr, + SenderUsername: nil, + GasPrice: tx.GasPrice, + GasLimit: tx.GasLimit, + DataField: tx.Data, + SignatureHex: hex.EncodeToString(tx.Signature), + ChainID: string(tx.ChainID), + Version: tx.Version, + Options: tx.Options, + Guardian: guardianAddress, + GuardianSigHex: hex.EncodeToString(tx.GuardianSignature), + Relayer: relayerAddress, + RelayerSignatureHex: hex.EncodeToString(tx.RelayerSignature), } tx, txHash, err := tpn.Node.CreateTransaction(createTxArgs) if err != nil { @@ -2898,11 +2906,12 @@ func (tpn *TestProcessorNode) WhiteListBody(nodes []*TestProcessorNode, bodyHand } } -// CommitBlock commits the block and body +// CommitBlock commits the block and body. +// This isn't entirely correct, since there's not state rollback if the commit fails. func (tpn *TestProcessorNode) CommitBlock(body data.BodyHandler, header data.HeaderHandler) { err := tpn.BlockProcessor.CommitBlock(header, body) if err != nil { - log.Error("CommitBlock", "error", err) + log.Error("TestProcessorNode.CommitBlock", "error", err.Error()) } } @@ -3348,6 +3357,8 @@ func CreateEnableEpochsConfig() config.EnableEpochs { MiniBlockPartialExecutionEnableEpoch: UnreachableEpoch, RefactorPeersMiniBlocksEnableEpoch: UnreachableEpoch, SCProcessorV2EnableEpoch: UnreachableEpoch, + FixRelayedBaseCostEnableEpoch: UnreachableEpoch, + FixRelayedMoveBalanceToNonPayableSCEnableEpoch: UnreachableEpoch, EquivalentMessagesEnableEpoch: UnreachableEpoch, FixedOrderInConsensusEnableEpoch: UnreachableEpoch, } diff --git a/integrationTests/testProcessorNodeWithMultisigner.go b/integrationTests/testProcessorNodeWithMultisigner.go index 7c20b09f349..7603865cc38 100644 --- a/integrationTests/testProcessorNodeWithMultisigner.go +++ b/integrationTests/testProcessorNodeWithMultisigner.go @@ -33,6 +33,7 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/cryptoMocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" + "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/genesisMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" "github.com/multiversx/mx-chain-go/testscommon/nodeTypeProviderMock" @@ -474,6 +475,7 @@ func CreateNodesWithNodesCoordinatorAndHeaderSigVerifier( FallbackHeaderValidator: &testscommon.FallBackHeaderValidatorStub{}, EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), HeadersPool: &mock.HeadersCacherStub{}, + StorageService: &genericMocks.ChainStorerMock{}, } headerSig, _ := headerCheck.NewHeaderSigVerifier(&args) @@ -619,6 +621,7 @@ func CreateNodesWithNodesCoordinatorKeygenAndSingleSigner( FallbackHeaderValidator: &testscommon.FallBackHeaderValidatorStub{}, EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), HeadersPool: &mock.HeadersCacherStub{}, + StorageService: &genericMocks.ChainStorerMock{}, } headerSig, _ := headerCheck.NewHeaderSigVerifier(&args) diff --git a/integrationTests/testProcessorNodeWithTestWebServer.go b/integrationTests/testProcessorNodeWithTestWebServer.go index b7d05e76f4c..27a499c4ddc 100644 --- a/integrationTests/testProcessorNodeWithTestWebServer.go +++ b/integrationTests/testProcessorNodeWithTestWebServer.go @@ -29,6 +29,7 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/cache" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/genesisMocks" + "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" "github.com/multiversx/mx-chain-go/testscommon/state" "github.com/multiversx/mx-chain-go/vm/systemSmartContracts/defaults" ) @@ -237,6 +238,8 @@ func createFacadeComponents(tpn *TestProcessorNode) nodeFacade.ApiResolver { TxTypeHandler: txTypeHandler, LogsFacade: logsFacade, DataFieldParser: dataFieldParser, + TxMarshaller: &marshallerMock.MarshalizerMock{}, + EnableEpochsHandler: tpn.EnableEpochsHandler, } apiTransactionHandler, err := transactionAPI.NewAPITransactionProcessor(argsApiTransactionProc) log.LogIfError(err) diff --git a/integrationTests/vm/testInitializer.go b/integrationTests/vm/testInitializer.go index 4ffd57197ca..c6e33ddc21a 100644 --- a/integrationTests/vm/testInitializer.go +++ b/integrationTests/vm/testInitializer.go @@ -317,7 +317,7 @@ func CreateAccount(accnts state.AccountsAdapter, pubKey []byte, nonce uint64, ba return hashCreated, nil } -func createEconomicsData(enableEpochsConfig config.EnableEpochs) (process.EconomicsDataHandler, error) { +func createEconomicsData(enableEpochsConfig config.EnableEpochs, gasPriceModifier float64) (process.EconomicsDataHandler, error) { maxGasLimitPerBlock := strconv.FormatUint(math.MaxUint64, 10) minGasPrice := strconv.FormatUint(1, 10) minGasLimit := strconv.FormatUint(1, 10) @@ -363,7 +363,7 @@ func createEconomicsData(enableEpochsConfig config.EnableEpochs) (process.Econom }, MinGasPrice: minGasPrice, GasPerDataByte: "1", - GasPriceModifier: 1.0, + GasPriceModifier: gasPriceModifier, MaxGasPriceSetGuardian: "2000000000", }, }, @@ -437,7 +437,7 @@ func CreateTxProcessorWithOneSCExecutorMockVM( } txTypeHandler, _ := coordinator.NewTxTypeHandler(argsTxTypeHandler) - economicsData, err := createEconomicsData(enableEpochsConfig) + economicsData, err := createEconomicsData(enableEpochsConfig, 1) if err != nil { return nil, err } @@ -623,6 +623,7 @@ func CreateVMAndBlockchainHookAndDataPool( blockChainHook, _ := vmFactory.BlockChainHookImpl().(*hooks.BlockChainHookImpl) _ = builtInFuncFactory.SetPayableHandler(blockChainHook) + _ = builtInFuncFactory.SetBlockchainHook(blockChainHook) return vmContainer, blockChainHook, datapool } @@ -686,7 +687,7 @@ func CreateVMAndBlockchainHookMeta( MissingTrieNodesNotifier: &testscommon.MissingTrieNodesNotifierStub{}, } - economicsData, err := createEconomicsData(config.EnableEpochs{}) + economicsData, err := createEconomicsData(config.EnableEpochs{}, 1) if err != nil { log.LogIfError(err) } @@ -826,6 +827,7 @@ func CreateTxProcessorWithOneSCExecutorWithVMs( guardianChecker process.GuardianChecker, roundNotifierInstance process.RoundNotifier, chainHandler data.ChainHandler, + gasPriceModifier float64, ) (*ResultsCreateTxProcessor, error) { if check.IfNil(poolsHolder) { poolsHolder = dataRetrieverMock.NewPoolsHolderMock() @@ -848,7 +850,7 @@ func CreateTxProcessorWithOneSCExecutorWithVMs( gasSchedule := make(map[string]map[string]uint64) defaults.FillGasMapInternal(gasSchedule, 1) - economicsData, err := createEconomicsData(enableEpochsConfig) + economicsData, err := createEconomicsData(enableEpochsConfig, gasPriceModifier) if err != nil { return nil, err } @@ -1139,6 +1141,7 @@ func CreatePreparedTxProcessorAndAccountsWithVMsWithRoundsConfig( guardedAccountHandler, roundNotifierInstance, chainHandler, + 1, ) if err != nil { return nil, err @@ -1172,36 +1175,48 @@ func createMockGasScheduleNotifierWithCustomGasSchedule(updateGasSchedule func(g } // CreatePreparedTxProcessorWithVMs - -func CreatePreparedTxProcessorWithVMs(enableEpochs config.EnableEpochs) (*VMTestContext, error) { - return CreatePreparedTxProcessorWithVMsAndCustomGasSchedule(enableEpochs, func(gasMap wasmConfig.GasScheduleMap) {}) +func CreatePreparedTxProcessorWithVMs(enableEpochs config.EnableEpochs, gasPriceModifier float64) (*VMTestContext, error) { + return CreatePreparedTxProcessorWithVMsAndCustomGasSchedule(enableEpochs, func(gasMap wasmConfig.GasScheduleMap) {}, gasPriceModifier) } // CreatePreparedTxProcessorWithVMsAndCustomGasSchedule - func CreatePreparedTxProcessorWithVMsAndCustomGasSchedule( enableEpochs config.EnableEpochs, - updateGasSchedule func(gasMap wasmConfig.GasScheduleMap)) (*VMTestContext, error) { + updateGasSchedule func(gasMap wasmConfig.GasScheduleMap), + gasPriceModifier float64) (*VMTestContext, error) { return CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGasAndRoundConfig( enableEpochs, mock.NewMultiShardsCoordinatorMock(2), integrationtests.CreateMemUnit(), createMockGasScheduleNotifierWithCustomGasSchedule(updateGasSchedule), testscommon.GetDefaultRoundsConfig(), + gasPriceModifier, ) } // CreatePreparedTxProcessorWithVMsWithShardCoordinator - -func CreatePreparedTxProcessorWithVMsWithShardCoordinator(enableEpochsConfig config.EnableEpochs, shardCoordinator sharding.Coordinator) (*VMTestContext, error) { - return CreatePreparedTxProcessorWithVMsWithShardCoordinatorAndRoundConfig(enableEpochsConfig, testscommon.GetDefaultRoundsConfig(), shardCoordinator) +func CreatePreparedTxProcessorWithVMsWithShardCoordinator( + enableEpochsConfig config.EnableEpochs, + shardCoordinator sharding.Coordinator, + gasPriceModifier float64, +) (*VMTestContext, error) { + return CreatePreparedTxProcessorWithVMsWithShardCoordinatorAndRoundConfig(enableEpochsConfig, testscommon.GetDefaultRoundsConfig(), shardCoordinator, gasPriceModifier) } // CreatePreparedTxProcessorWithVMsWithShardCoordinatorAndRoundConfig - -func CreatePreparedTxProcessorWithVMsWithShardCoordinatorAndRoundConfig(enableEpochsConfig config.EnableEpochs, roundsConfig config.RoundConfig, shardCoordinator sharding.Coordinator) (*VMTestContext, error) { +func CreatePreparedTxProcessorWithVMsWithShardCoordinatorAndRoundConfig( + enableEpochsConfig config.EnableEpochs, + roundsConfig config.RoundConfig, + shardCoordinator sharding.Coordinator, + gasPriceModifier float64, +) (*VMTestContext, error) { return CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGasAndRoundConfig( enableEpochsConfig, shardCoordinator, integrationtests.CreateMemUnit(), CreateMockGasScheduleNotifier(), roundsConfig, + gasPriceModifier, ) } @@ -1211,6 +1226,7 @@ func CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas( shardCoordinator sharding.Coordinator, db storage.Storer, gasScheduleNotifier core.GasScheduleNotifier, + gasPriceModifier float64, ) (*VMTestContext, error) { vmConfig := createDefaultVMConfig() return CreatePreparedTxProcessorWithVMConfigWithShardCoordinatorDBAndGasAndRoundConfig( @@ -1220,6 +1236,7 @@ func CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas( gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), vmConfig, + gasPriceModifier, ) } @@ -1230,6 +1247,7 @@ func CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGasAndRoundConfig( db storage.Storer, gasScheduleNotifier core.GasScheduleNotifier, roundsConfig config.RoundConfig, + gasPriceModifier float64, ) (*VMTestContext, error) { vmConfig := createDefaultVMConfig() return CreatePreparedTxProcessorWithVMConfigWithShardCoordinatorDBAndGasAndRoundConfig( @@ -1239,6 +1257,7 @@ func CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGasAndRoundConfig( gasScheduleNotifier, roundsConfig, vmConfig, + gasPriceModifier, ) } @@ -1250,6 +1269,7 @@ func CreatePreparedTxProcessorWithVMConfigWithShardCoordinatorDBAndGasAndRoundCo gasScheduleNotifier core.GasScheduleNotifier, roundsConfig config.RoundConfig, vmConfig *config.VirtualMachineConfig, + gasPriceModifier float64, ) (*VMTestContext, error) { feeAccumulator := postprocess.NewFeeAccumulator() epochNotifierInstance := forking.NewGenericEpochNotifier() @@ -1291,6 +1311,7 @@ func CreatePreparedTxProcessorWithVMConfigWithShardCoordinatorDBAndGasAndRoundCo guardedAccountHandler, roundNotifierInstance, chainHandler, + gasPriceModifier, ) if err != nil { return nil, err @@ -1387,6 +1408,7 @@ func CreateTxProcessorArwenVMWithGasScheduleAndRoundConfig( guardedAccountHandler, roundNotifierInstance, chainHandler, + 1, ) if err != nil { return nil, err @@ -1469,6 +1491,7 @@ func CreateTxProcessorArwenWithVMConfigAndRoundConfig( guardedAccountHandler, roundNotifierInstance, chainHandler, + 1, ) if err != nil { return nil, err @@ -1836,13 +1859,13 @@ func GetNodeIndex(nodeList []*integrationTests.TestProcessorNode, node *integrat } // CreatePreparedTxProcessorWithVMsMultiShard - -func CreatePreparedTxProcessorWithVMsMultiShard(selfShardID uint32, enableEpochsConfig config.EnableEpochs) (*VMTestContext, error) { - return CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(selfShardID, enableEpochsConfig, testscommon.GetDefaultRoundsConfig()) +func CreatePreparedTxProcessorWithVMsMultiShard(selfShardID uint32, enableEpochsConfig config.EnableEpochs, gasPriceModifier float64) (*VMTestContext, error) { + return CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(selfShardID, enableEpochsConfig, testscommon.GetDefaultRoundsConfig(), gasPriceModifier) } // CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig - -func CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(selfShardID uint32, enableEpochsConfig config.EnableEpochs, roundsConfig config.RoundConfig) (*VMTestContext, error) { - return CreatePreparedTxProcessorWithVMsMultiShardRoundVMConfig(selfShardID, enableEpochsConfig, roundsConfig, createDefaultVMConfig()) +func CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(selfShardID uint32, enableEpochsConfig config.EnableEpochs, roundsConfig config.RoundConfig, gasPriceModifier float64) (*VMTestContext, error) { + return CreatePreparedTxProcessorWithVMsMultiShardRoundVMConfig(selfShardID, enableEpochsConfig, roundsConfig, createDefaultVMConfig(), gasPriceModifier) } // CreatePreparedTxProcessorWithVMsMultiShardRoundVMConfig - @@ -1851,6 +1874,7 @@ func CreatePreparedTxProcessorWithVMsMultiShardRoundVMConfig( enableEpochsConfig config.EnableEpochs, roundsConfig config.RoundConfig, vmConfig *config.VirtualMachineConfig, + gasPriceModifier float64, ) (*VMTestContext, error) { shardCoordinator, _ := sharding.NewMultiShardCoordinator(3, selfShardID) @@ -1900,6 +1924,7 @@ func CreatePreparedTxProcessorWithVMsMultiShardRoundVMConfig( guardedAccountHandler, roundNotifierInstance, chainHandler, + gasPriceModifier, ) if err != nil { return nil, err diff --git a/integrationTests/vm/txsFee/apiTransactionEvaluator_test.go b/integrationTests/vm/txsFee/apiTransactionEvaluator_test.go index 56551737de5..4c66bf28f52 100644 --- a/integrationTests/vm/txsFee/apiTransactionEvaluator_test.go +++ b/integrationTests/vm/txsFee/apiTransactionEvaluator_test.go @@ -30,7 +30,7 @@ func TestSCCallCostTransactionCost(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -55,7 +55,7 @@ func TestScDeployTransactionCost(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -75,7 +75,7 @@ func TestAsyncCallsTransactionCost(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -109,7 +109,7 @@ func TestBuiltInFunctionTransactionCost(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs( config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -132,7 +132,7 @@ func TestESDTTransfer(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -142,7 +142,7 @@ func TestESDTTransfer(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) tx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100), 0, 0) res, err := testContext.TxCostHandler.ComputeTransactionGasLimit(tx) @@ -157,7 +157,7 @@ func TestAsyncESDTTransfer(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -170,7 +170,7 @@ func TestAsyncESDTTransfer(t *testing.T) { esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) // deploy 2 contracts ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) diff --git a/integrationTests/vm/txsFee/asyncCall_multi_test.go b/integrationTests/vm/txsFee/asyncCall_multi_test.go index 24cf1f14750..ef14b5556a3 100644 --- a/integrationTests/vm/txsFee/asyncCall_multi_test.go +++ b/integrationTests/vm/txsFee/asyncCall_multi_test.go @@ -5,6 +5,7 @@ import ( "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/scheduled" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" @@ -24,7 +25,7 @@ func TestAsyncCallLegacy(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -71,7 +72,7 @@ func TestAsyncCallMulti(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -122,7 +123,7 @@ func TestAsyncCallTransferAndExecute(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -183,7 +184,7 @@ func TestAsyncCallTransferESDTAndExecute_Success(t *testing.T) { } func transferESDTAndExecute(t *testing.T, numberOfCallsFromParent int, numberOfBackTransfers int) { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -276,7 +277,7 @@ func deployForwarderAndTestContract( forwarderSCAddress := utils.DoDeploySecond(t, testContext, pathToForwarder, ownerAccount, gasPrice, deployGasLimit, nil, big.NewInt(0)) - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, forwarderSCAddress, egldBalance, esdtToken, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, forwarderSCAddress, egldBalance, esdtToken, 0, esdtBalance, uint32(core.Fungible)) utils.CleanAccumulatedIntermediateTransactions(t, testContext) @@ -297,15 +298,15 @@ func TestAsyncCallMulti_CrossShard(t *testing.T) { t.Skip("this is not a short test") } - testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextFirstContract.Close() - testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSecondContract.Close() - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSender.Close() @@ -387,15 +388,15 @@ func TestAsyncCallTransferAndExecute_CrossShard(t *testing.T) { t.Skip("this is not a short test") } - childShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + childShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer childShard.Close() - forwarderShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + forwarderShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer forwarderShard.Close() - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSender.Close() @@ -479,15 +480,15 @@ func TestAsyncCallTransferESDTAndExecute_CrossShard_Success(t *testing.T) { } func transferESDTAndExecuteCrossShard(t *testing.T, numberOfCallsFromParent int, numberOfBackTransfers int) { - vaultShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + vaultShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer vaultShard.Close() - forwarderShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + forwarderShard, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer forwarderShard.Close() - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSender.Close() @@ -514,7 +515,7 @@ func transferESDTAndExecuteCrossShard(t *testing.T, numberOfCallsFromParent int, pathToContract = "testdata/forwarderQueue/forwarder-queue-promises.wasm" forwarderSCAddress := utils.DoDeploySecond(t, forwarderShard, pathToContract, forwarderOwnerAccount, gasPrice, gasLimit, nil, big.NewInt(0)) - utils.CreateAccountWithESDTBalance(t, forwarderShard.Accounts, forwarderSCAddress, egldBalance, esdtToken, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, forwarderShard.Accounts, forwarderSCAddress, egldBalance, esdtToken, 0, esdtBalance, uint32(core.Fungible)) utils.CheckESDTNFTBalance(t, forwarderShard, forwarderSCAddress, esdtToken, 0, esdtBalance) diff --git a/integrationTests/vm/txsFee/asyncCall_test.go b/integrationTests/vm/txsFee/asyncCall_test.go index 19a966e2fa8..88057f564a7 100644 --- a/integrationTests/vm/txsFee/asyncCall_test.go +++ b/integrationTests/vm/txsFee/asyncCall_test.go @@ -33,7 +33,7 @@ func TestAsyncCallShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -94,7 +94,7 @@ func TestMinterContractWithAsyncCalls(t *testing.T) { gasMap[common.MaxPerTransaction]["MaxBuiltInCallsPerTx"] = 199 gasMap[common.MaxPerTransaction]["MaxNumberOfTransfersPerTx"] = 100000 gasMap[common.MaxPerTransaction]["MaxNumberOfTrieReadsPerTx"] = 100000 - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -202,6 +202,7 @@ func testAsyncCallsOnInitFunctionOnUpgrade( gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), vm.CreateVMConfigWithVersion("v1.4"), + 1, ) require.Nil(t, err) testContextShardMeta, err := vm.CreatePreparedTxProcessorWithVMConfigWithShardCoordinatorDBAndGasAndRoundConfig( @@ -211,6 +212,7 @@ func testAsyncCallsOnInitFunctionOnUpgrade( gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), vm.CreateVMConfigWithVersion("v1.4"), + 1, ) require.Nil(t, err) @@ -340,6 +342,7 @@ func testAsyncCallsOnInitFunctionOnDeploy(t *testing.T, gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), vm.CreateVMConfigWithVersion("v1.4"), + 1, ) require.Nil(t, err) testContextShardMeta, err := vm.CreatePreparedTxProcessorWithVMConfigWithShardCoordinatorDBAndGasAndRoundConfig( @@ -349,6 +352,7 @@ func testAsyncCallsOnInitFunctionOnDeploy(t *testing.T, gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), vm.CreateVMConfigWithVersion("v1.4"), + 1, ) require.Nil(t, err) diff --git a/integrationTests/vm/txsFee/asyncESDT_test.go b/integrationTests/vm/txsFee/asyncESDT_test.go index 4476a79511d..32869f51ae9 100644 --- a/integrationTests/vm/txsFee/asyncESDT_test.go +++ b/integrationTests/vm/txsFee/asyncESDT_test.go @@ -27,7 +27,7 @@ func TestAsyncESDTCallShouldWork(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -40,7 +40,7 @@ func TestAsyncESDTCallShouldWork(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance, uint32(core.Fungible)) // deploy 2 contracts ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -83,7 +83,7 @@ func TestAsyncESDTCallSecondScRefusesPayment(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -96,7 +96,7 @@ func TestAsyncESDTCallSecondScRefusesPayment(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance, uint32(core.Fungible)) // deploy 2 contracts ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -140,7 +140,7 @@ func TestAsyncESDTCallsOutOfGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -153,7 +153,7 @@ func TestAsyncESDTCallsOutOfGas(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance, uint32(core.Fungible)) // deploy 2 contracts ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -198,7 +198,7 @@ func TestAsyncMultiTransferOnCallback(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -208,7 +208,7 @@ func TestAsyncMultiTransferOnCallback(t *testing.T) { sftBalance := big.NewInt(1000) halfBalance := big.NewInt(500) - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance, uint32(core.SemiFungible)) utils.CheckESDTNFTBalance(t, testContext, ownerAddr, sftTokenID, sftNonce, sftBalance) ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -295,7 +295,7 @@ func TestAsyncMultiTransferOnCallAndOnCallback(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -305,7 +305,7 @@ func TestAsyncMultiTransferOnCallAndOnCallback(t *testing.T) { sftBalance := big.NewInt(1000) halfBalance := big.NewInt(500) - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance, uint32(core.SemiFungible)) utils.CheckESDTNFTBalance(t, testContext, ownerAddr, sftTokenID, sftNonce, sftBalance) ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -399,7 +399,7 @@ func TestSendNFTToContractWith0Function(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -408,7 +408,7 @@ func TestSendNFTToContractWith0Function(t *testing.T) { sftNonce := uint64(1) sftBalance := big.NewInt(1000) - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance, uint32(core.SemiFungible)) utils.CheckESDTNFTBalance(t, testContext, ownerAddr, sftTokenID, sftNonce, sftBalance) ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -452,7 +452,7 @@ func TestSendNFTToContractWith0FunctionNonPayable(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -461,7 +461,7 @@ func TestSendNFTToContractWith0FunctionNonPayable(t *testing.T) { sftNonce := uint64(1) sftBalance := big.NewInt(1000) - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, ownerAddr, big.NewInt(1000000000), sftTokenID, sftNonce, sftBalance, uint32(core.SemiFungible)) utils.CheckESDTNFTBalance(t, testContext, ownerAddr, sftTokenID, sftNonce, sftBalance) ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) @@ -506,7 +506,7 @@ func TestAsyncESDTCallForThirdContractShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -523,7 +523,7 @@ func TestAsyncESDTCallForThirdContractShouldWork(t *testing.T) { localEsdtBalance := big.NewInt(100000000) esdtTransferValue := big.NewInt(5000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, localEgldBalance, token, 0, localEsdtBalance, uint32(core.Fungible)) // deploy contract ownerAccount, _ := testContext.Accounts.LoadAccount(ownerAddr) diff --git a/integrationTests/vm/txsFee/backwardsCompatibility_test.go b/integrationTests/vm/txsFee/backwardsCompatibility_test.go index 2b160d342cd..424594c6754 100644 --- a/integrationTests/vm/txsFee/backwardsCompatibility_test.go +++ b/integrationTests/vm/txsFee/backwardsCompatibility_test.go @@ -26,7 +26,7 @@ func TestMoveBalanceSelfShouldWorkAndConsumeTxFeeWhenAllFlagsAreDisabled(t *test SCDeployEnableEpoch: 100, MetaProtectionEnableEpoch: 100, RelayedTransactionsEnableEpoch: 100, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -71,7 +71,7 @@ func TestMoveBalanceAllFlagsDisabledLessBalanceThanGasLimitMulGasPrice(t *testin SCDeployEnableEpoch: integrationTests.UnreachableEpoch, MetaProtectionEnableEpoch: integrationTests.UnreachableEpoch, RelayedTransactionsEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -99,7 +99,7 @@ func TestMoveBalanceSelfShouldWorkAndConsumeTxFeeWhenSomeFlagsAreDisabled(t *tes SCDeployEnableEpoch: 100, MetaProtectionEnableEpoch: 100, RelayedTransactionsV2EnableEpoch: 100, - }) + }, 1) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/builtInFunctions_test.go b/integrationTests/vm/txsFee/builtInFunctions_test.go index 4ac02c62661..0c7c1f7cdf3 100644 --- a/integrationTests/vm/txsFee/builtInFunctions_test.go +++ b/integrationTests/vm/txsFee/builtInFunctions_test.go @@ -32,11 +32,11 @@ func TestBuildInFunctionChangeOwnerCallShouldWorkV1(t *testing.T) { config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, SCProcessorV2EnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) utils.CleanAccumulatedIntermediateTransactions(t, testContext) @@ -73,11 +73,11 @@ func TestBuildInFunctionChangeOwnerCallShouldWork(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs( config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) utils.CleanAccumulatedIntermediateTransactions(t, testContext) @@ -111,11 +111,11 @@ func TestBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas(t *testing.T) t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, initialOwner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, initialOwner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) @@ -152,11 +152,11 @@ func TestBuildInFunctionChangeOwnerInvalidAddressShouldConsumeGas(t *testing.T) t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) @@ -190,11 +190,11 @@ func TestBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldNotConsumeGas(t t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) newOwner := []byte("12345678901234567890123456789112") @@ -230,11 +230,11 @@ func TestBuildInFunctionChangeOwnerOutOfGasShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) @@ -275,7 +275,7 @@ func TestBuildInFunctionSaveKeyValue_WrongDestination(t *testing.T) { config.EnableEpochs{ CleanUpInformativeSCRsEnableEpoch: integrationTests.UnreachableEpoch, SCProcessorV2EnableEpoch: integrationTests.UnreachableEpoch, - }, shardCoord) + }, shardCoord, 1) require.Nil(t, err) defer testContext.Close() @@ -313,7 +313,7 @@ func TestBuildInFunctionSaveKeyValue_NotEnoughGasFor3rdSave(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinator( config.EnableEpochs{ BackwardCompSaveKeyValueEnableEpoch: 5, - }, shardCoord) + }, shardCoord, 1) require.Nil(t, err) defer testContext.Close() @@ -356,6 +356,7 @@ func TestBuildInFunctionSaveKeyValue_NotEnoughGasForTheSameKeyValue(t *testing.T gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), vm.CreateVMConfigWithVersion("v1.5"), + 1, ) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/common.go b/integrationTests/vm/txsFee/common.go index 9f6574aca1d..774af8202d2 100644 --- a/integrationTests/vm/txsFee/common.go +++ b/integrationTests/vm/txsFee/common.go @@ -15,7 +15,11 @@ import ( "github.com/stretchr/testify/require" ) -const gasPrice = uint64(10) +const ( + gasPrice = uint64(10) + minGasLimit = uint64(1) + gasPriceModifier = float64(0.1) +) // MetaData defines test meta data struct type MetaData struct { diff --git a/integrationTests/vm/txsFee/dns_test.go b/integrationTests/vm/txsFee/dns_test.go index 0ff3914d7a0..c8787d99db5 100644 --- a/integrationTests/vm/txsFee/dns_test.go +++ b/integrationTests/vm/txsFee/dns_test.go @@ -31,7 +31,7 @@ func TestDeployDNSContract_TestRegisterAndResolveAndSendTxWithSndAndRcvUserName( testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: 10, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -131,6 +131,7 @@ func TestDeployDNSContract_TestGasWhenSaveUsernameFailsCrossShardBackwardsCompat enableEpochs, testscommon.GetDefaultRoundsConfig(), vmConfig, + 1, ) require.Nil(t, err) defer testContextForDNSContract.Close() @@ -140,6 +141,7 @@ func TestDeployDNSContract_TestGasWhenSaveUsernameFailsCrossShardBackwardsCompat enableEpochs, testscommon.GetDefaultRoundsConfig(), vmConfig, + 1, ) require.Nil(t, err) defer testContextForRelayerAndUser.Close() @@ -200,11 +202,13 @@ func TestDeployDNSContract_TestGasWhenSaveUsernameAfterDNSv2IsActivated(t *testi t.Skip("this is not a short test") } - testContextForDNSContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextForDNSContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: integrationTests.UnreachableEpoch, + }, 1) require.Nil(t, err) defer testContextForDNSContract.Close() - testContextForRelayerAndUser, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) + testContextForRelayerAndUser, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextForRelayerAndUser.Close() scAddress, _ := utils.DoDeployDNS(t, testContextForDNSContract, "../../multiShard/smartContract/dns/dns.wasm") diff --git a/integrationTests/vm/txsFee/dynamicGasCost_test.go b/integrationTests/vm/txsFee/dynamicGasCost_test.go index e1fca367f3f..08edae2af13 100644 --- a/integrationTests/vm/txsFee/dynamicGasCost_test.go +++ b/integrationTests/vm/txsFee/dynamicGasCost_test.go @@ -29,7 +29,7 @@ func TestDynamicGasCostForDataTrieStorageLoad(t *testing.T) { shardCoordinator, _ := sharding.NewMultiShardCoordinator(3, 1) gasScheduleNotifier := vm.CreateMockGasScheduleNotifier() - testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas(enableEpochs, shardCoordinator, integrationTests.CreateMemUnit(), gasScheduleNotifier) + testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas(enableEpochs, shardCoordinator, integrationTests.CreateMemUnit(), gasScheduleNotifier, 1) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/esdtLocalBurn_test.go b/integrationTests/vm/txsFee/esdtLocalBurn_test.go index 29c4fc26320..77edb13d0e2 100644 --- a/integrationTests/vm/txsFee/esdtLocalBurn_test.go +++ b/integrationTests/vm/txsFee/esdtLocalBurn_test.go @@ -18,7 +18,7 @@ func TestESDTLocalBurnShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -52,7 +52,7 @@ func TestESDTLocalBurnMoreThanTotalBalanceShouldErr(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -86,7 +86,7 @@ func TestESDTLocalBurnNotAllowedShouldErr(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -95,7 +95,7 @@ func TestESDTLocalBurnNotAllowedShouldErr(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(40) tx := utils.CreateESDTLocalBurnTx(0, sndAddr, sndAddr, token, big.NewInt(100), gasPrice, gasLimit) diff --git a/integrationTests/vm/txsFee/esdtLocalMint_test.go b/integrationTests/vm/txsFee/esdtLocalMint_test.go index f2104f4c341..62d7bf5decf 100644 --- a/integrationTests/vm/txsFee/esdtLocalMint_test.go +++ b/integrationTests/vm/txsFee/esdtLocalMint_test.go @@ -18,7 +18,7 @@ func TestESDTLocalMintShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -52,7 +52,7 @@ func TestESDTLocalMintNotAllowedShouldErr(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -61,7 +61,7 @@ func TestESDTLocalMintNotAllowedShouldErr(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(40) tx := utils.CreateESDTLocalMintTx(0, sndAddr, sndAddr, token, big.NewInt(100), gasPrice, gasLimit) diff --git a/integrationTests/vm/txsFee/esdtMetaDataRecreate_test.go b/integrationTests/vm/txsFee/esdtMetaDataRecreate_test.go index afb0166de58..e2dc58caf8f 100644 --- a/integrationTests/vm/txsFee/esdtMetaDataRecreate_test.go +++ b/integrationTests/vm/txsFee/esdtMetaDataRecreate_test.go @@ -7,10 +7,12 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + dataBlock "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" ) @@ -32,9 +34,10 @@ func runEsdtMetaDataRecreateTest(t *testing.T, tokenType string) { baseEsdtKeyPrefix := core.ProtectedKeyPrefix + core.ESDTKeyIdentifier key := append([]byte(baseEsdtKeyPrefix), token...) - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() + testContext.BlockchainHook.(process.BlockChainHookHandler).SetCurrentHeader(&dataBlock.Header{Round: 7}) createAccWithBalance(t, testContext.Accounts, sndAddr, big.NewInt(100000000)) createAccWithBalance(t, testContext.Accounts, core.ESDTSCAddress, big.NewInt(100000000)) @@ -61,7 +64,11 @@ func runEsdtMetaDataRecreateTest(t *testing.T, tokenType string) { _, err = testContext.Accounts.Commit() require.Nil(t, err) - checkMetaData(t, testContext, core.SystemAccountAddress, key, defaultMetaData) + if tokenType == core.DynamicNFTESDT { + checkMetaData(t, testContext, sndAddr, key, defaultMetaData) + } else { + checkMetaData(t, testContext, core.SystemAccountAddress, key, defaultMetaData) + } } func esdtMetaDataRecreateTx( diff --git a/integrationTests/vm/txsFee/esdtMetaDataUpdate_test.go b/integrationTests/vm/txsFee/esdtMetaDataUpdate_test.go index ea5ec910c97..d8fc6c7bb19 100644 --- a/integrationTests/vm/txsFee/esdtMetaDataUpdate_test.go +++ b/integrationTests/vm/txsFee/esdtMetaDataUpdate_test.go @@ -7,10 +7,12 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + dataBlock "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" ) @@ -32,9 +34,10 @@ func runEsdtMetaDataUpdateTest(t *testing.T, tokenType string) { baseEsdtKeyPrefix := core.ProtectedKeyPrefix + core.ESDTKeyIdentifier key := append([]byte(baseEsdtKeyPrefix), token...) - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() + testContext.BlockchainHook.(process.BlockChainHookHandler).SetCurrentHeader(&dataBlock.Header{Round: 7}) createAccWithBalance(t, testContext.Accounts, sndAddr, big.NewInt(100000000)) createAccWithBalance(t, testContext.Accounts, core.ESDTSCAddress, big.NewInt(100000000)) @@ -64,7 +67,11 @@ func runEsdtMetaDataUpdateTest(t *testing.T, tokenType string) { _, err = testContext.Accounts.Commit() require.Nil(t, err) - checkMetaData(t, testContext, core.SystemAccountAddress, key, defaultMetaData) + if tokenType == core.DynamicNFTESDT { + checkMetaData(t, testContext, sndAddr, key, defaultMetaData) + } else { + checkMetaData(t, testContext, core.SystemAccountAddress, key, defaultMetaData) + } } func esdtMetaDataUpdateTx( diff --git a/integrationTests/vm/txsFee/esdtModifyCreator_test.go b/integrationTests/vm/txsFee/esdtModifyCreator_test.go index 1aa80ffd5c3..d12eb5186af 100644 --- a/integrationTests/vm/txsFee/esdtModifyCreator_test.go +++ b/integrationTests/vm/txsFee/esdtModifyCreator_test.go @@ -7,10 +7,13 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + dataBlock "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-core-go/data/esdt" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" ) @@ -36,9 +39,10 @@ func runEsdtModifyCreatorTest(t *testing.T, tokenType string) { baseEsdtKeyPrefix := core.ProtectedKeyPrefix + core.ESDTKeyIdentifier key := append([]byte(baseEsdtKeyPrefix), token...) - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() + testContext.BlockchainHook.(process.BlockChainHookHandler).SetCurrentHeader(&dataBlock.Header{Round: 7}) createAccWithBalance(t, testContext.Accounts, newCreator, big.NewInt(100000000)) createAccWithBalance(t, testContext.Accounts, creatorAddr, big.NewInt(100000000)) @@ -66,7 +70,12 @@ func runEsdtModifyCreatorTest(t *testing.T, tokenType string) { _, err = testContext.Accounts.Commit() require.Nil(t, err) - retrievedMetaData := getMetaDataFromAcc(t, testContext, core.SystemAccountAddress, key) + retrievedMetaData := &esdt.MetaData{} + if tokenType == core.DynamicNFTESDT { + retrievedMetaData = getMetaDataFromAcc(t, testContext, newCreator, key) + } else { + retrievedMetaData = getMetaDataFromAcc(t, testContext, core.SystemAccountAddress, key) + } require.Equal(t, newCreator, retrievedMetaData.Creator) } diff --git a/integrationTests/vm/txsFee/esdtModifyRoyalties_test.go b/integrationTests/vm/txsFee/esdtModifyRoyalties_test.go index fd4b9c84880..151f1a62866 100644 --- a/integrationTests/vm/txsFee/esdtModifyRoyalties_test.go +++ b/integrationTests/vm/txsFee/esdtModifyRoyalties_test.go @@ -7,10 +7,13 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + dataBlock "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-core-go/data/esdt" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" ) @@ -31,9 +34,10 @@ func runEsdtModifyRoyaltiesTest(t *testing.T, tokenType string) { baseEsdtKeyPrefix := core.ProtectedKeyPrefix + core.ESDTKeyIdentifier key := append([]byte(baseEsdtKeyPrefix), token...) - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() + testContext.BlockchainHook.(process.BlockChainHookHandler).SetCurrentHeader(&dataBlock.Header{Round: 7}) createAccWithBalance(t, testContext.Accounts, creatorAddr, big.NewInt(100000000)) createAccWithBalance(t, testContext.Accounts, core.ESDTSCAddress, big.NewInt(100000000)) @@ -60,7 +64,12 @@ func runEsdtModifyRoyaltiesTest(t *testing.T, tokenType string) { _, err = testContext.Accounts.Commit() require.Nil(t, err) - retrievedMetaData := getMetaDataFromAcc(t, testContext, core.SystemAccountAddress, key) + retrievedMetaData := &esdt.MetaData{} + if tokenType == core.DynamicNFTESDT { + retrievedMetaData = getMetaDataFromAcc(t, testContext, creatorAddr, key) + } else { + retrievedMetaData = getMetaDataFromAcc(t, testContext, core.SystemAccountAddress, key) + } require.Equal(t, uint32(big.NewInt(20).Uint64()), retrievedMetaData.Royalties) } diff --git a/integrationTests/vm/txsFee/esdtSetNewURIs_test.go b/integrationTests/vm/txsFee/esdtSetNewURIs_test.go index 2354f4b9625..7c3500cd641 100644 --- a/integrationTests/vm/txsFee/esdtSetNewURIs_test.go +++ b/integrationTests/vm/txsFee/esdtSetNewURIs_test.go @@ -7,10 +7,13 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + dataBlock "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-core-go/data/esdt" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" + "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" ) @@ -32,9 +35,10 @@ func runEsdtSetNewURIsTest(t *testing.T, tokenType string) { baseEsdtKeyPrefix := core.ProtectedKeyPrefix + core.ESDTKeyIdentifier key := append([]byte(baseEsdtKeyPrefix), token...) - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() + testContext.BlockchainHook.(process.BlockChainHookHandler).SetCurrentHeader(&dataBlock.Header{Round: 7}) createAccWithBalance(t, testContext.Accounts, sndAddr, big.NewInt(100000000)) createAccWithBalance(t, testContext.Accounts, core.ESDTSCAddress, big.NewInt(100000000)) @@ -61,7 +65,12 @@ func runEsdtSetNewURIsTest(t *testing.T, tokenType string) { _, err = testContext.Accounts.Commit() require.Nil(t, err) - retrievedMetaData := getMetaDataFromAcc(t, testContext, core.SystemAccountAddress, key) + retrievedMetaData := &esdt.MetaData{} + if tokenType == core.DynamicNFTESDT { + retrievedMetaData = getMetaDataFromAcc(t, testContext, sndAddr, key) + } else { + retrievedMetaData = getMetaDataFromAcc(t, testContext, core.SystemAccountAddress, key) + } require.Equal(t, [][]byte{[]byte("newUri1"), []byte("newUri2")}, retrievedMetaData.URIs) } diff --git a/integrationTests/vm/txsFee/esdt_test.go b/integrationTests/vm/txsFee/esdt_test.go index 07871a87750..54cf8b38b71 100644 --- a/integrationTests/vm/txsFee/esdt_test.go +++ b/integrationTests/vm/txsFee/esdt_test.go @@ -22,7 +22,7 @@ func TestESDTTransferShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -32,7 +32,7 @@ func TestESDTTransferShouldWork(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(40) tx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100), gasPrice, gasLimit) @@ -62,7 +62,7 @@ func TestESDTTransferShouldWorkToMuchGasShouldConsumeAllGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -72,7 +72,7 @@ func TestESDTTransferShouldWorkToMuchGasShouldConsumeAllGas(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(1000) tx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100), gasPrice, gasLimit) @@ -102,7 +102,7 @@ func TestESDTTransferInvalidESDTValueShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -112,7 +112,7 @@ func TestESDTTransferInvalidESDTValueShouldConsumeGas(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(1000) tx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100000000+1), gasPrice, gasLimit) @@ -143,7 +143,7 @@ func TestESDTTransferCallBackOnErrorShouldNotGenerateSCRsFurther(t *testing.T) { } shardC, _ := sharding.NewMultiShardCoordinator(2, 0) - testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinator(config.EnableEpochs{}, shardC) + testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinator(config.EnableEpochs{}, shardC, 1) require.Nil(t, err) defer testContext.Close() @@ -156,7 +156,7 @@ func TestESDTTransferCallBackOnErrorShouldNotGenerateSCRsFurther(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) hexEncodedToken := hex.EncodeToString(token) esdtValueEncoded := hex.EncodeToString(big.NewInt(100).Bytes()) diff --git a/integrationTests/vm/txsFee/guardAccount_test.go b/integrationTests/vm/txsFee/guardAccount_test.go index 6ccde4df164..52a64322bb1 100644 --- a/integrationTests/vm/txsFee/guardAccount_test.go +++ b/integrationTests/vm/txsFee/guardAccount_test.go @@ -97,11 +97,13 @@ func prepareTestContextForGuardedAccounts(tb testing.TB) *vm.VMTestContext { GovernanceEnableEpoch: unreachableEpoch, SetSenderInEeiOutputTransferEnableEpoch: unreachableEpoch, RefactorPeersMiniBlocksEnableEpoch: unreachableEpoch, + FixRelayedBaseCostEnableEpoch: unreachableEpoch, }, testscommon.NewMultiShardsCoordinatorMock(2), db, gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), + 1, ) require.Nil(tb, err) @@ -977,7 +979,7 @@ func TestGuardAccounts_RelayedTransactionV1(t *testing.T) { alice, david, gasPrice, - transferGas+guardianSigVerificationGas, + 1+guardianSigVerificationGas, make([]byte, 0)) userTx.GuardianAddr = bob @@ -985,7 +987,7 @@ func TestGuardAccounts_RelayedTransactionV1(t *testing.T) { userTx.Version = txWithOptionVersion rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + transferGas + guardianSigVerificationGas + uint64(len(rtxData)) + rTxGasLimit := minGasLimit + guardianSigVerificationGas + minGasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(getNonce(testContext, charlie), big.NewInt(0), charlie, alice, gasPrice, rTxGasLimit, rtxData) returnCode, err = testContext.TxProcessor.ProcessTransaction(rtx) require.Nil(t, err) @@ -1016,13 +1018,13 @@ func TestGuardAccounts_RelayedTransactionV1(t *testing.T) { alice, david, gasPrice, - transferGas+guardianSigVerificationGas, + minGasLimit, make([]byte, 0)) userTx.Version = txWithOptionVersion rtxData = integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit = 1 + transferGas + guardianSigVerificationGas + uint64(len(rtxData)) + rTxGasLimit = minGasLimit + minGasLimit + uint64(len(rtxData)) rtx = vm.CreateTransaction(getNonce(testContext, charlie), big.NewInt(0), charlie, alice, gasPrice, rTxGasLimit, rtxData) returnCode, err = testContext.TxProcessor.ProcessTransaction(rtx) require.Nil(t, err) @@ -1095,14 +1097,14 @@ func TestGuardAccounts_RelayedTransactionV2(t *testing.T) { testContext.CleanIntermediateTransactions(t) // step 3 - charlie sends a relayed transaction v1 on the behalf of alice - // 3.1 cosigned transaction should work + // 3.1 cosigned transaction should not work userTx := vm.CreateTransaction( getNonce(testContext, alice), transferValue, alice, david, gasPrice, - transferGas+guardianSigVerificationGas, + 1+guardianSigVerificationGas, make([]byte, 0)) userTx.GuardianAddr = bob @@ -1110,7 +1112,7 @@ func TestGuardAccounts_RelayedTransactionV2(t *testing.T) { userTx.Version = txWithOptionVersion rtxData := integrationTests.PrepareRelayedTxDataV2(userTx) - rTxGasLimit := 1 + transferGas + guardianSigVerificationGas + uint64(len(rtxData)) + rTxGasLimit := minGasLimit + guardianSigVerificationGas + minGasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(getNonce(testContext, charlie), big.NewInt(0), charlie, alice, gasPrice, rTxGasLimit, rtxData) returnCode, err = testContext.TxProcessor.ProcessTransaction(rtx) require.Nil(t, err) @@ -1129,7 +1131,8 @@ func TestGuardAccounts_RelayedTransactionV2(t *testing.T) { assert.Equal(t, aliceCurrentBalance, getBalance(testContext, alice)) bobExpectedBalance := big.NewInt(0).Set(initialMint) assert.Equal(t, bobExpectedBalance, getBalance(testContext, bob)) - charlieExpectedBalance := big.NewInt(0).Sub(initialMint, big.NewInt(int64(rTxGasLimit*gasPrice))) + charlieConsumed := minGasLimit + guardianSigVerificationGas + minGasLimit + uint64(len(rtxData)) + charlieExpectedBalance := big.NewInt(0).Sub(initialMint, big.NewInt(int64(charlieConsumed*gasPrice))) assert.Equal(t, charlieExpectedBalance, getBalance(testContext, charlie)) assert.Equal(t, initialMint, getBalance(testContext, david)) @@ -1143,13 +1146,13 @@ func TestGuardAccounts_RelayedTransactionV2(t *testing.T) { alice, david, gasPrice, - transferGas+guardianSigVerificationGas, + minGasLimit, make([]byte, 0)) userTx.Version = txWithOptionVersion rtxData = integrationTests.PrepareRelayedTxDataV2(userTx) - rTxGasLimit = 1 + transferGas + guardianSigVerificationGas + uint64(len(rtxData)) + rTxGasLimit = minGasLimit + minGasLimit + uint64(len(rtxData)) rtx = vm.CreateTransaction(getNonce(testContext, charlie), big.NewInt(0), charlie, alice, gasPrice, rTxGasLimit, rtxData) returnCode, err = testContext.TxProcessor.ProcessTransaction(rtx) require.Nil(t, err) diff --git a/integrationTests/vm/txsFee/migrateDataTrie_test.go b/integrationTests/vm/txsFee/migrateDataTrie_test.go index 02eecc0e1c3..d089be8fc14 100644 --- a/integrationTests/vm/txsFee/migrateDataTrie_test.go +++ b/integrationTests/vm/txsFee/migrateDataTrie_test.go @@ -45,7 +45,7 @@ func TestMigrateDataTrieBuiltInFunc(t *testing.T) { t.Run("deterministic trie", func(t *testing.T) { t.Parallel() - testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas(enableEpochs, shardCoordinator, integrationTests.CreateMemUnit(), gasScheduleNotifier) + testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas(enableEpochs, shardCoordinator, integrationTests.CreateMemUnit(), gasScheduleNotifier, 1) require.Nil(t, err) defer testContext.Close() @@ -123,7 +123,7 @@ func TestMigrateDataTrieBuiltInFunc(t *testing.T) { t.Run("random trie - all leaves are migrated in multiple transactions", func(t *testing.T) { t.Parallel() - testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas(enableEpochs, shardCoordinator, integrationTests.CreateMemUnit(), gasScheduleNotifier) + testContext, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas(enableEpochs, shardCoordinator, integrationTests.CreateMemUnit(), gasScheduleNotifier, 1) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/moveBalance_test.go b/integrationTests/vm/txsFee/moveBalance_test.go index 28907f5a2c6..8e847dba20b 100644 --- a/integrationTests/vm/txsFee/moveBalance_test.go +++ b/integrationTests/vm/txsFee/moveBalance_test.go @@ -22,7 +22,7 @@ func TestMoveBalanceSelfShouldWorkAndConsumeTxFee(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -61,7 +61,7 @@ func TestMoveBalanceAllFlagsEnabledLessBalanceThanGasLimitMulGasPrice(t *testing t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -82,7 +82,7 @@ func TestMoveBalanceShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -126,7 +126,7 @@ func TestMoveBalanceInvalidHasGasButNoValueShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -159,7 +159,7 @@ func TestMoveBalanceHigherNonceShouldNotConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -193,7 +193,7 @@ func TestMoveBalanceMoreGasThanGasLimitPerMiniBlockForSafeCrossShard(t *testing. t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -228,7 +228,7 @@ func TestMoveBalanceInvalidUserNames(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/multiESDTTransfer_test.go b/integrationTests/vm/txsFee/multiESDTTransfer_test.go index c85a1a2bc1b..b9a4474cf34 100644 --- a/integrationTests/vm/txsFee/multiESDTTransfer_test.go +++ b/integrationTests/vm/txsFee/multiESDTTransfer_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests/vm" @@ -19,7 +20,7 @@ func TestMultiESDTTransferShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -29,9 +30,9 @@ func TestMultiESDTTransferShouldWork(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) secondToken := []byte("second") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), secondToken, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), secondToken, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(4000) tx := utils.CreateMultiTransferTX(0, sndAddr, rcvAddr, gasPrice, gasLimit, &utils.TransferESDTData{ @@ -80,7 +81,7 @@ func TestMultiESDTTransferFailsBecauseOfMaxLimit(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMsAndCustomGasSchedule(config.EnableEpochs{}, func(gasMap wasmConfig.GasScheduleMap) { gasMap[common.MaxPerTransaction]["MaxNumberOfTransfersPerTx"] = 1 - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -90,9 +91,9 @@ func TestMultiESDTTransferFailsBecauseOfMaxLimit(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) secondToken := []byte("second") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), secondToken, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), secondToken, 0, esdtBalance, uint32(core.Fungible)) gasLimit := uint64(4000) tx := utils.CreateMultiTransferTX(0, sndAddr, rcvAddr, gasPrice, gasLimit, &utils.TransferESDTData{ diff --git a/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go b/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go index 28130046e11..573370bab26 100644 --- a/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go +++ b/integrationTests/vm/txsFee/multiShard/asyncCallWithChangeOwner_test.go @@ -25,7 +25,7 @@ func TestDoChangeOwnerCrossShardFromAContract(t *testing.T) { ChangeOwnerAddressCrossShardThroughSCEnableEpoch: 0, } - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer testContextSource.Close() @@ -42,7 +42,7 @@ func TestDoChangeOwnerCrossShardFromAContract(t *testing.T) { require.Equal(t, uint32(0), testContextSource.ShardCoordinator.ComputeId(firstContract)) require.Equal(t, uint32(0), testContextSource.ShardCoordinator.ComputeId(firstOwner)) - testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer testContextSecondContract.Close() diff --git a/integrationTests/vm/txsFee/multiShard/asyncCall_test.go b/integrationTests/vm/txsFee/multiShard/asyncCall_test.go index e6e7fe5ce6e..c02aed11578 100644 --- a/integrationTests/vm/txsFee/multiShard/asyncCall_test.go +++ b/integrationTests/vm/txsFee/multiShard/asyncCall_test.go @@ -23,15 +23,15 @@ func TestAsyncCallShouldWork(t *testing.T) { DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, } - testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer testContextFirstContract.Close() - testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer testContextSecondContract.Close() - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs, 1) require.Nil(t, err) defer testContextSender.Close() @@ -131,15 +131,15 @@ func TestAsyncCallDisabled(t *testing.T) { activationRound.Round = "0" roundsConfig.RoundActivations["DisableAsyncCallV1"] = activationRound - testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(0, enableEpochs, roundsConfig) + testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(0, enableEpochs, roundsConfig, 1) require.Nil(t, err) defer testContextFirstContract.Close() - testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(1, enableEpochs, roundsConfig) + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(1, enableEpochs, roundsConfig, 1) require.Nil(t, err) defer testContextSecondContract.Close() - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(2, enableEpochs, roundsConfig) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShardAndRoundConfig(2, enableEpochs, roundsConfig, 1) require.Nil(t, err) defer testContextSender.Close() diff --git a/integrationTests/vm/txsFee/multiShard/asyncESDT_test.go b/integrationTests/vm/txsFee/multiShard/asyncESDT_test.go index 21a894662a7..5ea878f2b26 100644 --- a/integrationTests/vm/txsFee/multiShard/asyncESDT_test.go +++ b/integrationTests/vm/txsFee/multiShard/asyncESDT_test.go @@ -5,6 +5,7 @@ import ( "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" @@ -22,15 +23,15 @@ func TestAsyncESDTTransferWithSCCallShouldWork(t *testing.T) { DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, } - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer testContextSender.Close() - testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer testContextFirstContract.Close() - testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs) + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs, 1) require.Nil(t, err) defer testContextSecondContract.Close() @@ -46,7 +47,7 @@ func TestAsyncESDTTransferWithSCCallShouldWork(t *testing.T) { token := []byte("miiutoken") egldBalance := big.NewInt(10000000) esdtBalance := big.NewInt(10000000) - utils.CreateAccountWithESDTBalance(t, testContextSender.Accounts, senderAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContextSender.Accounts, senderAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) // create accounts for owners _, _ = vm.CreateAccount(testContextFirstContract.Accounts, firstContractOwner, 0, egldBalance) @@ -138,15 +139,15 @@ func TestAsyncESDTTransferWithSCCallSecondContractAnotherToken(t *testing.T) { DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, } - testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + testContextSender, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer testContextSender.Close() - testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + testContextFirstContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer testContextFirstContract.Close() - testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs) + testContextSecondContract, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs, 1) require.Nil(t, err) defer testContextSecondContract.Close() @@ -162,7 +163,7 @@ func TestAsyncESDTTransferWithSCCallSecondContractAnotherToken(t *testing.T) { token := []byte("miiutoken") egldBalance := big.NewInt(10000000) esdtBalance := big.NewInt(10000000) - utils.CreateAccountWithESDTBalance(t, testContextSender.Accounts, senderAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContextSender.Accounts, senderAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) // create accounts for owners _, _ = vm.CreateAccount(testContextFirstContract.Accounts, firstContractOwner, 0, egldBalance) diff --git a/integrationTests/vm/txsFee/multiShard/builtInFunctions_test.go b/integrationTests/vm/txsFee/multiShard/builtInFunctions_test.go index fd0232072c2..b8aff559fbc 100644 --- a/integrationTests/vm/txsFee/multiShard/builtInFunctions_test.go +++ b/integrationTests/vm/txsFee/multiShard/builtInFunctions_test.go @@ -41,7 +41,8 @@ func TestBuiltInFunctionExecuteOnSourceAndDestinationShouldWork(t *testing.T) { config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, + 1) require.Nil(t, err) defer testContextSource.Close() @@ -50,7 +51,8 @@ func TestBuiltInFunctionExecuteOnSourceAndDestinationShouldWork(t *testing.T) { config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, + 1) require.Nil(t, err) defer testContextDst.Close() diff --git a/integrationTests/vm/txsFee/multiShard/esdtLiquidity_test.go b/integrationTests/vm/txsFee/multiShard/esdtLiquidity_test.go index 036c17d9cef..3e863713571 100644 --- a/integrationTests/vm/txsFee/multiShard/esdtLiquidity_test.go +++ b/integrationTests/vm/txsFee/multiShard/esdtLiquidity_test.go @@ -25,17 +25,17 @@ func TestSystemAccountLiquidityAfterCrossShardTransferAndBurn(t *testing.T) { tokenID := []byte("MYNFT") sh0Addr := []byte("12345678901234567890123456789010") sh1Addr := []byte("12345678901234567890123456789011") - sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer sh0Context.Close() - sh1Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + sh1Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer sh1Context.Close() _, _ = vm.CreateAccount(sh1Context.Accounts, sh1Addr, 0, big.NewInt(1000000000)) // create the nft and ensure that it exists on the account's trie and the liquidity is set on the system account - utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(100000000), tokenID, 1, big.NewInt(1)) + utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(100000000), tokenID, 1, big.NewInt(1), uint32(core.NonFungible)) utils.CheckESDTNFTBalance(t, sh0Context, sh0Addr, tokenID, 1, big.NewInt(1)) utils.CheckESDTNFTBalance(t, sh0Context, core.SystemAccountAddress, tokenID, 1, big.NewInt(1)) @@ -77,16 +77,16 @@ func TestSystemAccountLiquidityAfterNFTWipe(t *testing.T) { tokenID := []byte("MYNFT-0a0a0a") sh0Addr := bytes.Repeat([]byte{1}, 31) sh0Addr = append(sh0Addr, 0) - sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer sh0Context.Close() - metaContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, config.EnableEpochs{}) + metaContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, config.EnableEpochs{}, 1) require.Nil(t, err) defer metaContext.Close() // create the nft and ensure that it exists on the account's trie and the liquidity is set on the system account - utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(10000000000000), tokenID, 1, big.NewInt(1)) + utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(10000000000000), tokenID, 1, big.NewInt(1), uint32(core.NonFungible)) utils.CheckESDTNFTBalance(t, sh0Context, sh0Addr, tokenID, 1, big.NewInt(1)) utils.CheckESDTNFTBalance(t, sh0Context, core.SystemAccountAddress, tokenID, 1, big.NewInt(1)) @@ -127,16 +127,16 @@ func TestSystemAccountLiquidityAfterSFTWipe(t *testing.T) { tokenID := []byte("MYSFT-0a0a0a") sh0Addr := bytes.Repeat([]byte{1}, 31) sh0Addr = append(sh0Addr, 0) - sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer sh0Context.Close() - metaContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, config.EnableEpochs{}) + metaContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, config.EnableEpochs{}, 1) require.Nil(t, err) defer metaContext.Close() // create the nft and ensure that it exists on the account's trie and the liquidity is set on the system account - utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(10000000000000), tokenID, 1, big.NewInt(10)) + utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(10000000000000), tokenID, 1, big.NewInt(10), uint32(core.SemiFungible)) utils.CheckESDTNFTBalance(t, sh0Context, sh0Addr, tokenID, 1, big.NewInt(10)) utils.CheckESDTNFTBalance(t, sh0Context, core.SystemAccountAddress, tokenID, 1, big.NewInt(10)) diff --git a/integrationTests/vm/txsFee/multiShard/esdt_test.go b/integrationTests/vm/txsFee/multiShard/esdt_test.go index 8f978daee1c..2e37f2c9948 100644 --- a/integrationTests/vm/txsFee/multiShard/esdt_test.go +++ b/integrationTests/vm/txsFee/multiShard/esdt_test.go @@ -20,7 +20,7 @@ func TestESDTTransferShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -33,7 +33,7 @@ func TestESDTTransferShouldWork(t *testing.T) { egldBalance := big.NewInt(100000000) esdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, egldBalance, token, 0, esdtBalance, uint32(core.Fungible)) gasPrice := uint64(10) gasLimit := uint64(40) @@ -61,11 +61,11 @@ func TestMultiESDTNFTTransferViaRelayedV2(t *testing.T) { relayerSh0 := []byte("12345678901234567890123456789110") relayerSh1 := []byte("12345678901234567890123456789111") - sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer sh0Context.Close() - sh1Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + sh1Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer sh1Context.Close() _, _ = vm.CreateAccount(sh1Context.Accounts, sh1Addr, 0, big.NewInt(10000000000)) @@ -73,8 +73,8 @@ func TestMultiESDTNFTTransferViaRelayedV2(t *testing.T) { _, _ = vm.CreateAccount(sh1Context.Accounts, relayerSh1, 0, big.NewInt(1000000000)) // create the nfts, add the liquidity to the system accounts and check for balances - utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(100000000), tokenID1, 1, big.NewInt(1)) - utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(100000000), tokenID2, 1, big.NewInt(1)) + utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(100000000), tokenID1, 1, big.NewInt(1), uint32(core.NonFungible)) + utils.CreateAccountWithESDTBalance(t, sh0Context.Accounts, sh0Addr, big.NewInt(100000000), tokenID2, 1, big.NewInt(1), uint32(core.NonFungible)) sh0Accnt, _ := sh0Context.Accounts.LoadAccount(sh0Addr) sh1Accnt, _ := sh1Context.Accounts.LoadAccount(sh1Addr) diff --git a/integrationTests/vm/txsFee/multiShard/moveBalance_test.go b/integrationTests/vm/txsFee/multiShard/moveBalance_test.go index 8c5f6bd6015..dcf42bce5b9 100644 --- a/integrationTests/vm/txsFee/multiShard/moveBalance_test.go +++ b/integrationTests/vm/txsFee/multiShard/moveBalance_test.go @@ -18,7 +18,7 @@ func TestMoveBalanceShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -57,7 +57,7 @@ func TestMoveBalanceContractAddressDataFieldNilShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -99,7 +99,7 @@ func TestMoveBalanceContractAddressDataFieldNotNilShouldConsumeGas(t *testing.T) t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -141,11 +141,11 @@ func TestMoveBalanceExecuteOneSourceAndDestinationShard(t *testing.T) { t.Skip("this is not a short test") } - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSource.Close() - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextDst.Close() diff --git a/integrationTests/vm/txsFee/multiShard/nftTransferUpdate_test.go b/integrationTests/vm/txsFee/multiShard/nftTransferUpdate_test.go index 1fdd2f6f78f..4a15002f5c0 100644 --- a/integrationTests/vm/txsFee/multiShard/nftTransferUpdate_test.go +++ b/integrationTests/vm/txsFee/multiShard/nftTransferUpdate_test.go @@ -40,11 +40,11 @@ func TestNFTTransferAndUpdateOnOldTypeToken(t *testing.T) { initialAttribute := []byte("initial attribute") newAttribute := []byte("new attribute") - sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + sh0Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer sh0Context.Close() - sh1Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + sh1Context, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer sh1Context.Close() diff --git a/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go b/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go index e987d4dbc74..49a0e256483 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go @@ -23,7 +23,8 @@ func TestRelayedBuiltInFunctionExecuteOnRelayerAndDstShardShouldWork(t *testing. 2, config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, + 1) require.Nil(t, err) defer testContextRelayer.Close() @@ -31,7 +32,8 @@ func TestRelayedBuiltInFunctionExecuteOnRelayerAndDstShardShouldWork(t *testing. 1, config.EnableEpochs{ PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, + 1) require.Nil(t, err) defer testContextInner.Close() diff --git a/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go b/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go index aa206c591b4..2d2013fd0e8 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go @@ -13,52 +13,68 @@ import ( "github.com/stretchr/testify/require" ) +const ( + minGasLimit = uint64(1) + gasPriceModifier = float64(0.1) +) + func TestRelayedMoveBalanceRelayerShard0InnerTxSenderAndReceiverShard1ShouldWork(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedMoveBalanceRelayerShard0InnerTxSenderAndReceiverShard1ShouldWork(integrationTests.UnreachableEpoch)) +} + +func testRelayedMoveBalanceRelayerShard0InnerTxSenderAndReceiverShard1ShouldWork(relayedFixActivationEpoch uint32) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789030") - shardID := testContext.ShardCoordinator.ComputeId(relayerAddr) - require.Equal(t, uint32(0), shardID) + relayerAddr := []byte("12345678901234567890123456789030") + shardID := testContext.ShardCoordinator.ComputeId(relayerAddr) + require.Equal(t, uint32(0), shardID) - sndAddr := []byte("12345678901234567890123456789011") - shardID = testContext.ShardCoordinator.ComputeId(sndAddr) - require.Equal(t, uint32(1), shardID) + sndAddr := []byte("12345678901234567890123456789011") + shardID = testContext.ShardCoordinator.ComputeId(sndAddr) + require.Equal(t, uint32(1), shardID) - rcvAddr := []byte("12345678901234567890123456789021") - shardID = testContext.ShardCoordinator.ComputeId(rcvAddr) - require.Equal(t, uint32(1), shardID) + rcvAddr := []byte("12345678901234567890123456789021") + shardID = testContext.ShardCoordinator.ComputeId(rcvAddr) + require.Equal(t, uint32(1), shardID) - gasPrice := uint64(10) - gasLimit := uint64(100) + gasPrice := uint64(10) + gasLimit := uint64(100) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, []byte("aaaa")) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(100)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, []byte("aaaa")) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := gasLimit + minGasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - // check balance inner tx sender - utils.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - // check balance inner tx receiver - utils.TestAccount(t, testContext.Accounts, rcvAddr, 0, big.NewInt(100)) + // check balance inner tx sender + utils.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(1000), accumulatedFees) + // check balance inner tx receiver + utils.TestAccount(t, testContext.Accounts, rcvAddr, 0, big.NewInt(100)) + + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, big.NewInt(100), accumulatedFees) + } } func TestRelayedMoveBalanceRelayerAndInnerTxSenderShard0ReceiverShard1(t *testing.T) { @@ -66,48 +82,56 @@ func TestRelayedMoveBalanceRelayerAndInnerTxSenderShard0ReceiverShard1(t *testin t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedMoveBalanceRelayerAndInnerTxSenderShard0ReceiverShard1(integrationTests.UnreachableEpoch)) +} + +func testRelayedMoveBalanceRelayerAndInnerTxSenderShard0ReceiverShard1(relayedFixActivationEpoch uint32) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789030") - shardID := testContext.ShardCoordinator.ComputeId(relayerAddr) - require.Equal(t, uint32(0), shardID) + relayerAddr := []byte("12345678901234567890123456789030") + shardID := testContext.ShardCoordinator.ComputeId(relayerAddr) + require.Equal(t, uint32(0), shardID) - sndAddr := []byte("12345678901234567890123456789011") - shardID = testContext.ShardCoordinator.ComputeId(sndAddr) - require.Equal(t, uint32(1), shardID) + sndAddr := []byte("12345678901234567890123456789011") + shardID = testContext.ShardCoordinator.ComputeId(sndAddr) + require.Equal(t, uint32(1), shardID) - scAddress := "00000000000000000000dbb53e4b23392b0d6f36cce32deb2d623e9625ab3132" - scAddrBytes, _ := hex.DecodeString(scAddress) - scAddrBytes[31] = 1 - shardID = testContext.ShardCoordinator.ComputeId(scAddrBytes) - require.Equal(t, uint32(1), shardID) + scAddress := "00000000000000000000dbb53e4b23392b0d6f36cce32deb2d623e9625ab3132" + scAddrBytes, _ := hex.DecodeString(scAddress) + scAddrBytes[31] = 1 + shardID = testContext.ShardCoordinator.ComputeId(scAddrBytes) + require.Equal(t, uint32(1), shardID) - gasPrice := uint64(10) - gasLimit := uint64(100) + gasPrice := uint64(10) + gasLimit := uint64(100) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, nil) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, nil) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := gasLimit + minGasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Nil(t, err) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - // check inner tx receiver - account, err := testContext.Accounts.GetExistingAccount(scAddrBytes) - require.Nil(t, account) - require.NotNil(t, err) + // check inner tx receiver + account, err := testContext.Accounts.GetExistingAccount(scAddrBytes) + require.Nil(t, account) + require.NotNil(t, err) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(1000), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, big.NewInt(100), accumulatedFees) + } } func TestRelayedMoveBalanceExecuteOnSourceAndDestination(t *testing.T) { @@ -115,67 +139,79 @@ func TestRelayedMoveBalanceExecuteOnSourceAndDestination(t *testing.T) { t.Skip("this is not a short test") } - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextSource.Close() - - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextDst.Close() - - relayerAddr := []byte("12345678901234567890123456789030") - shardID := testContextSource.ShardCoordinator.ComputeId(relayerAddr) - require.Equal(t, uint32(0), shardID) - - sndAddr := []byte("12345678901234567890123456789011") - shardID = testContextSource.ShardCoordinator.ComputeId(sndAddr) - require.Equal(t, uint32(1), shardID) - - scAddress := "00000000000000000000dbb53e4b23392b0d6f36cce32deb2d623e9625ab3132" - scAddrBytes, _ := hex.DecodeString(scAddress) - scAddrBytes[31] = 1 - shardID = testContextSource.ShardCoordinator.ComputeId(scAddrBytes) - require.Equal(t, uint32(1), shardID) - - gasPrice := uint64(10) - gasLimit := uint64(100) - - _, _ = vm.CreateAccount(testContextSource.Accounts, relayerAddr, 0, big.NewInt(100000)) - - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, nil) - - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - - // execute on source shard - retCode, err := testContextSource.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) - - // check relayed balance - utils.TestAccount(t, testContextSource.Accounts, relayerAddr, 1, big.NewInt(97270)) - - // check accumulated fees - accumulatedFees := testContextSource.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(1630), accumulatedFees) - - // execute on destination shard - retCode, err = testContextDst.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Nil(t, err) - - _, err = testContextDst.Accounts.Commit() - require.Nil(t, err) - - // check inner tx receiver - account, err := testContextDst.Accounts.GetExistingAccount(scAddrBytes) - require.Nil(t, account) - require.NotNil(t, err) + t.Run("before relayed base cost fix", testRelayedMoveBalanceExecuteOnSourceAndDestination(integrationTests.UnreachableEpoch)) +} - // check accumulated fees - accumulatedFees = testContextDst.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(1000), accumulatedFees) +func testRelayedMoveBalanceExecuteOnSourceAndDestination(relayedFixActivationEpoch uint32) func(t *testing.T) { + return func(t *testing.T) { + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextSource.Close() + + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextDst.Close() + + relayerAddr := []byte("12345678901234567890123456789030") + shardID := testContextSource.ShardCoordinator.ComputeId(relayerAddr) + require.Equal(t, uint32(0), shardID) + + sndAddr := []byte("12345678901234567890123456789011") + shardID = testContextSource.ShardCoordinator.ComputeId(sndAddr) + require.Equal(t, uint32(1), shardID) + + scAddress := "00000000000000000000dbb53e4b23392b0d6f36cce32deb2d623e9625ab3132" + scAddrBytes, _ := hex.DecodeString(scAddress) + scAddrBytes[31] = 1 + shardID = testContextSource.ShardCoordinator.ComputeId(scAddrBytes) + require.Equal(t, uint32(1), shardID) + + gasPrice := uint64(10) + gasLimit := uint64(100) + + _, _ = vm.CreateAccount(testContextSource.Accounts, relayerAddr, 0, big.NewInt(100000)) + _, _ = vm.CreateAccount(testContextSource.Accounts, sndAddr, 0, big.NewInt(100)) + + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, nil) + + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + + // execute on source shard + retCode, err := testContextSource.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) + + // check relayed balance + // 100000 - rTxFee(163)*gasPrice(10) - txFeeInner(1000*gasPriceModifier(0.1)) = 98270 + utils.TestAccount(t, testContextSource.Accounts, relayerAddr, 1, big.NewInt(98270)) + + // check accumulated fees + accumulatedFees := testContextSource.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, big.NewInt(1630), accumulatedFees) + + // execute on destination shard + retCode, err = testContextDst.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Nil(t, err) + + _, err = testContextDst.Accounts.Commit() + require.Nil(t, err) + + // check inner tx receiver + account, err := testContextDst.Accounts.GetExistingAccount(scAddrBytes) + require.Nil(t, account) + require.NotNil(t, err) + + // check accumulated fees + accumulatedFees = testContextDst.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, big.NewInt(100), accumulatedFees) + } } func TestRelayedMoveBalanceExecuteOnSourceAndDestinationRelayerAndInnerTxSenderShard0InnerTxReceiverShard1ShouldWork(t *testing.T) { @@ -183,63 +219,83 @@ func TestRelayedMoveBalanceExecuteOnSourceAndDestinationRelayerAndInnerTxSenderS t.Skip("this is not a short test") } - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextSource.Close() - - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextDst.Close() - - relayerAddr := []byte("12345678901234567890123456789030") - shardID := testContextSource.ShardCoordinator.ComputeId(relayerAddr) - require.Equal(t, uint32(0), shardID) - - sndAddr := []byte("12345678901234567890123456789010") - shardID = testContextSource.ShardCoordinator.ComputeId(sndAddr) - require.Equal(t, uint32(0), shardID) - - rcvAddr := []byte("12345678901234567890123456789011") - shardID = testContextSource.ShardCoordinator.ComputeId(rcvAddr) - require.Equal(t, uint32(1), shardID) - - gasPrice := uint64(10) - gasLimit := uint64(100) - - _, _ = vm.CreateAccount(testContextSource.Accounts, relayerAddr, 0, big.NewInt(100000)) - - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) - - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - - // execute on source shard - retCode, err := testContextSource.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) - - // check relayed balance - utils.TestAccount(t, testContextSource.Accounts, relayerAddr, 1, big.NewInt(97270)) - // check inner tx sender - utils.TestAccount(t, testContextSource.Accounts, sndAddr, 1, big.NewInt(0)) - - // check accumulated fees - accumulatedFees := testContextSource.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(2630), accumulatedFees) - - // get scr for destination shard - txs := testContextSource.GetIntermediateTransactions(t) - scr := txs[0] - - utils.ProcessSCRResult(t, testContextDst, scr, vmcommon.Ok, nil) - - // check balance receiver - utils.TestAccount(t, testContextDst.Accounts, rcvAddr, 0, big.NewInt(100)) + t.Run("before relayed base cost fix", testRelayedMoveBalanceExecuteOnSourceAndDestinationRelayerAndInnerTxSenderShard0InnerTxReceiverShard1ShouldWork(integrationTests.UnreachableEpoch)) + t.Run("after relayed base cost fix", testRelayedMoveBalanceExecuteOnSourceAndDestinationRelayerAndInnerTxSenderShard0InnerTxReceiverShard1ShouldWork(0)) +} - // check accumulated fess - accumulatedFees = testContextDst.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(0), accumulatedFees) +func testRelayedMoveBalanceExecuteOnSourceAndDestinationRelayerAndInnerTxSenderShard0InnerTxReceiverShard1ShouldWork(relayedFixActivationEpoch uint32) func(t *testing.T) { + return func(t *testing.T) { + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextSource.Close() + + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextDst.Close() + + relayerAddr := []byte("12345678901234567890123456789030") + shardID := testContextSource.ShardCoordinator.ComputeId(relayerAddr) + require.Equal(t, uint32(0), shardID) + + sndAddr := []byte("12345678901234567890123456789010") + shardID = testContextSource.ShardCoordinator.ComputeId(sndAddr) + require.Equal(t, uint32(0), shardID) + + rcvAddr := []byte("12345678901234567890123456789011") + shardID = testContextSource.ShardCoordinator.ComputeId(rcvAddr) + require.Equal(t, uint32(1), shardID) + + gasPrice := uint64(10) + gasLimit := uint64(100) + + _, _ = vm.CreateAccount(testContextSource.Accounts, relayerAddr, 0, big.NewInt(100000)) + _, _ = vm.CreateAccount(testContextSource.Accounts, sndAddr, 0, big.NewInt(100)) + + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) + + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := gasLimit + minGasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + + // execute on source shard + retCode, err := testContextSource.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) + + // check relayed balance + // before base cost fix: 100000 - rTxFee(163)*gasPrice(10) - innerTxFee(1000*gasPriceModifier(0.1)) = 98270 + // after base cost fix: 100000 - rTxFee(163)*gasPrice(10) - innerTxFee(10) = 98360 + expectedRelayerBalance := big.NewInt(98270) + expectedAccumulatedFees := big.NewInt(1730) + if relayedFixActivationEpoch != integrationTests.UnreachableEpoch { + expectedRelayerBalance = big.NewInt(98360) + expectedAccumulatedFees = big.NewInt(1640) + } + utils.TestAccount(t, testContextSource.Accounts, relayerAddr, 1, expectedRelayerBalance) + // check inner tx sender + utils.TestAccount(t, testContextSource.Accounts, sndAddr, 1, big.NewInt(0)) + + // check accumulated fees + accumulatedFees := testContextSource.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) + + // get scr for destination shard + txs := testContextSource.GetIntermediateTransactions(t) + scr := txs[0] + + utils.ProcessSCRResult(t, testContextDst, scr, vmcommon.Ok, nil) + + // check balance receiver + utils.TestAccount(t, testContextDst.Accounts, rcvAddr, 0, big.NewInt(100)) + + // check accumulated fess + accumulatedFees = testContextDst.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, big.NewInt(0), accumulatedFees) + } } func TestRelayedMoveBalanceRelayerAndInnerTxReceiverShard0SenderShard1(t *testing.T) { @@ -247,75 +303,87 @@ func TestRelayedMoveBalanceRelayerAndInnerTxReceiverShard0SenderShard1(t *testin t.Skip("this is not a short test") } - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextSource.Close() + t.Run("before relayed base cost fix", testRelayedMoveBalanceRelayerAndInnerTxReceiverShard0SenderShard1(integrationTests.UnreachableEpoch)) +} + +func testRelayedMoveBalanceRelayerAndInnerTxReceiverShard0SenderShard1(relayedFixActivationEpoch uint32) func(t *testing.T) { + return func(t *testing.T) { + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextSource.Close() - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextDst.Close() + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextDst.Close() - relayerAddr := []byte("12345678901234567890123456789030") - shardID := testContextSource.ShardCoordinator.ComputeId(relayerAddr) - require.Equal(t, uint32(0), shardID) + relayerAddr := []byte("12345678901234567890123456789030") + shardID := testContextSource.ShardCoordinator.ComputeId(relayerAddr) + require.Equal(t, uint32(0), shardID) - sndAddr := []byte("12345678901234567890123456789011") - shardID = testContextSource.ShardCoordinator.ComputeId(sndAddr) - require.Equal(t, uint32(1), shardID) + sndAddr := []byte("12345678901234567890123456789011") + shardID = testContextSource.ShardCoordinator.ComputeId(sndAddr) + require.Equal(t, uint32(1), shardID) - rcvAddr := []byte("12345678901234567890123456789010") - shardID = testContextSource.ShardCoordinator.ComputeId(rcvAddr) - require.Equal(t, uint32(0), shardID) + rcvAddr := []byte("12345678901234567890123456789010") + shardID = testContextSource.ShardCoordinator.ComputeId(rcvAddr) + require.Equal(t, uint32(0), shardID) - gasPrice := uint64(10) - gasLimit := uint64(100) + gasPrice := uint64(10) + gasLimit := uint64(100) - _, _ = vm.CreateAccount(testContextSource.Accounts, relayerAddr, 0, big.NewInt(100000)) + _, _ = vm.CreateAccount(testContextSource.Accounts, relayerAddr, 0, big.NewInt(100000)) + _, _ = vm.CreateAccount(testContextDst.Accounts, sndAddr, 0, big.NewInt(100)) - innerTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) + innerTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) - rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - // execute on relayer shard - retCode, err := testContextSource.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + // execute on relayer shard + retCode, err := testContextSource.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - // check relayed balance - utils.TestAccount(t, testContextSource.Accounts, relayerAddr, 1, big.NewInt(97270)) + // check relayed balance + // 100000 - rTxFee(163)*gasPrice(10) - innerTxFee(1000*gasPriceModifier(0.1)) = 98270 + utils.TestAccount(t, testContextSource.Accounts, relayerAddr, 1, big.NewInt(98270)) - // check inner Tx receiver - innerTxSenderAccount, err := testContextSource.Accounts.GetExistingAccount(sndAddr) - require.Nil(t, innerTxSenderAccount) - require.NotNil(t, err) + // check inner Tx receiver + innerTxSenderAccount, err := testContextSource.Accounts.GetExistingAccount(sndAddr) + require.Nil(t, innerTxSenderAccount) + require.NotNil(t, err) - // check accumulated fees - accumulatedFees := testContextSource.TxFeeHandler.GetAccumulatedFees() - expectedAccFees := big.NewInt(1630) - require.Equal(t, expectedAccFees, accumulatedFees) + // check accumulated fees + accumulatedFees := testContextSource.TxFeeHandler.GetAccumulatedFees() + expectedAccFees := big.NewInt(1630) + require.Equal(t, expectedAccFees, accumulatedFees) - // execute on destination shard - retCode, err = testContextDst.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + // execute on destination shard + retCode, err = testContextDst.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - utils.TestAccount(t, testContextDst.Accounts, sndAddr, 1, big.NewInt(0)) + utils.TestAccount(t, testContextDst.Accounts, sndAddr, 1, big.NewInt(0)) - // check accumulated fees - accumulatedFees = testContextDst.TxFeeHandler.GetAccumulatedFees() - expectedAccFees = big.NewInt(1000) - require.Equal(t, expectedAccFees, accumulatedFees) + // check accumulated fees + accumulatedFees = testContextDst.TxFeeHandler.GetAccumulatedFees() + expectedAccFees = big.NewInt(100) + require.Equal(t, expectedAccFees, accumulatedFees) - txs := testContextDst.GetIntermediateTransactions(t) - scr := txs[0] + txs := testContextDst.GetIntermediateTransactions(t) + scr := txs[0] - // execute generated SCR from shard1 on shard 0 - utils.ProcessSCRResult(t, testContextSource, scr, vmcommon.Ok, nil) + // execute generated SCR from shard1 on shard 0 + utils.ProcessSCRResult(t, testContextSource, scr, vmcommon.Ok, nil) - // check receiver balance - utils.TestAccount(t, testContextSource.Accounts, rcvAddr, 0, big.NewInt(100)) + // check receiver balance + utils.TestAccount(t, testContextSource.Accounts, rcvAddr, 0, big.NewInt(100)) + } } func TestMoveBalanceRelayerShard0InnerTxSenderShard1InnerTxReceiverShard2ShouldWork(t *testing.T) { @@ -323,77 +391,91 @@ func TestMoveBalanceRelayerShard0InnerTxSenderShard1InnerTxReceiverShard2ShouldW t.Skip("this is not a short test") } - testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextRelayer.Close() - - testContextInnerSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextInnerSource.Close() - - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) - require.Nil(t, err) - defer testContextDst.Close() - - relayerAddr := []byte("12345678901234567890123456789030") - shardID := testContextRelayer.ShardCoordinator.ComputeId(relayerAddr) - require.Equal(t, uint32(0), shardID) - - sndAddr := []byte("12345678901234567890123456789011") - shardID = testContextRelayer.ShardCoordinator.ComputeId(sndAddr) - require.Equal(t, uint32(1), shardID) - - rcvAddr := []byte("12345678901234567890123456789012") - shardID = testContextRelayer.ShardCoordinator.ComputeId(rcvAddr) - require.Equal(t, uint32(2), shardID) - - gasPrice := uint64(10) - gasLimit := uint64(100) - - _, _ = vm.CreateAccount(testContextRelayer.Accounts, relayerAddr, 0, big.NewInt(100000)) - - innerTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) - - rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - - // execute on relayer shard - retCode, err := testContextRelayer.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) - - // check relayed balance - utils.TestAccount(t, testContextRelayer.Accounts, relayerAddr, 1, big.NewInt(97270)) - - // check inner Tx receiver - innerTxSenderAccount, err := testContextRelayer.Accounts.GetExistingAccount(sndAddr) - require.Nil(t, innerTxSenderAccount) - require.NotNil(t, err) - - // check accumulated fees - accumulatedFees := testContextRelayer.TxFeeHandler.GetAccumulatedFees() - expectedAccFees := big.NewInt(1630) - require.Equal(t, expectedAccFees, accumulatedFees) - - // execute on inner tx sender shard - retCode, err = testContextInnerSource.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) - - utils.TestAccount(t, testContextInnerSource.Accounts, sndAddr, 1, big.NewInt(0)) - - // check accumulated fees - accumulatedFees = testContextInnerSource.TxFeeHandler.GetAccumulatedFees() - expectedAccFees = big.NewInt(1000) - require.Equal(t, expectedAccFees, accumulatedFees) - - // execute on inner tx receiver shard - txs := testContextInnerSource.GetIntermediateTransactions(t) - scr := txs[0] - - utils.ProcessSCRResult(t, testContextDst, scr, vmcommon.Ok, nil) + t.Run("before relayed base cost fix", testMoveBalanceRelayerShard0InnerTxSenderShard1InnerTxReceiverShard2ShouldWork(integrationTests.UnreachableEpoch)) +} - // check receiver balance - utils.TestAccount(t, testContextDst.Accounts, rcvAddr, 0, big.NewInt(100)) +func testMoveBalanceRelayerShard0InnerTxSenderShard1InnerTxReceiverShard2ShouldWork(relayedFixActivationEpoch uint32) func(t *testing.T) { + return func(t *testing.T) { + testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextRelayer.Close() + + testContextInnerSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextInnerSource.Close() + + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContextDst.Close() + + relayerAddr := []byte("12345678901234567890123456789030") + shardID := testContextRelayer.ShardCoordinator.ComputeId(relayerAddr) + require.Equal(t, uint32(0), shardID) + + sndAddr := []byte("12345678901234567890123456789011") + shardID = testContextRelayer.ShardCoordinator.ComputeId(sndAddr) + require.Equal(t, uint32(1), shardID) + + rcvAddr := []byte("12345678901234567890123456789012") + shardID = testContextRelayer.ShardCoordinator.ComputeId(rcvAddr) + require.Equal(t, uint32(2), shardID) + + gasPrice := uint64(10) + gasLimit := uint64(100) + + _, _ = vm.CreateAccount(testContextRelayer.Accounts, relayerAddr, 0, big.NewInt(100000)) + _, _ = vm.CreateAccount(testContextInnerSource.Accounts, sndAddr, 0, big.NewInt(100)) + + innerTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) + + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + + // execute on relayer shard + retCode, err := testContextRelayer.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) + + // check relayed balance + // 100000 - rTxFee(164)*gasPrice(10) - innerTxFee(1000*gasPriceModifier(0.1)) = 98270 + utils.TestAccount(t, testContextRelayer.Accounts, relayerAddr, 1, big.NewInt(98270)) + + // check inner Tx receiver + innerTxSenderAccount, err := testContextRelayer.Accounts.GetExistingAccount(sndAddr) + require.Nil(t, innerTxSenderAccount) + require.NotNil(t, err) + + // check accumulated fees + accumulatedFees := testContextRelayer.TxFeeHandler.GetAccumulatedFees() + expectedAccFees := big.NewInt(1630) + require.Equal(t, expectedAccFees, accumulatedFees) + + // execute on inner tx sender shard + retCode, err = testContextInnerSource.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) + + utils.TestAccount(t, testContextInnerSource.Accounts, sndAddr, 1, big.NewInt(0)) + + // check accumulated fees + accumulatedFees = testContextInnerSource.TxFeeHandler.GetAccumulatedFees() + expectedAccFees = big.NewInt(100) + require.Equal(t, expectedAccFees, accumulatedFees) + + // execute on inner tx receiver shard + txs := testContextInnerSource.GetIntermediateTransactions(t) + scr := txs[0] + + utils.ProcessSCRResult(t, testContextDst, scr, vmcommon.Ok, nil) + + // check receiver balance + utils.TestAccount(t, testContextDst.Accounts, rcvAddr, 0, big.NewInt(100)) + } } diff --git a/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go b/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go index 7700c55b0f4..de22bb57d60 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go @@ -18,11 +18,11 @@ func TestRelayedSCDeployShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) + testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextRelayer.Close() - testContextInner, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextInner, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextInner.Close() diff --git a/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go b/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go index bbab4208aa2..736783b11ae 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go @@ -31,15 +31,15 @@ func TestRelayedTxScCallMultiShardShouldWork(t *testing.T) { DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, } - testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs) + testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, enableEpochs, 1) require.Nil(t, err) defer testContextRelayer.Close() - testContextInnerSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + testContextInnerSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer testContextInnerSource.Close() - testContextInnerDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + testContextInnerDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer testContextInnerDst.Close() @@ -140,15 +140,15 @@ func TestRelayedTxScCallMultiShardFailOnInnerTxDst(t *testing.T) { t.Skip("this is not a short test") } - testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}) + testContextRelayer, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(2, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextRelayer.Close() - testContextInnerSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContextInnerSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextInnerSource.Close() - testContextInnerDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextInnerDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextInnerDst.Close() diff --git a/integrationTests/vm/txsFee/multiShard/scCallWithValueTransfer_test.go b/integrationTests/vm/txsFee/multiShard/scCallWithValueTransfer_test.go index 8f66a649a3b..c2a7356d1f3 100644 --- a/integrationTests/vm/txsFee/multiShard/scCallWithValueTransfer_test.go +++ b/integrationTests/vm/txsFee/multiShard/scCallWithValueTransfer_test.go @@ -30,14 +30,14 @@ func TestDeployContractAndTransferValueSCProcessorV2(t *testing.T) { } func testDeployContractAndTransferValue(t *testing.T, scProcessorV2EnabledEpoch uint32) { - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSource.Close() configEnabledEpochs := config.EnableEpochs{} configEnabledEpochs.SCProcessorV2EnableEpoch = scProcessorV2EnabledEpoch - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, configEnabledEpochs) + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, configEnabledEpochs, 1) require.Nil(t, err) defer testContextDst.Close() diff --git a/integrationTests/vm/txsFee/multiShard/scCalls_test.go b/integrationTests/vm/txsFee/multiShard/scCalls_test.go index 34aa049c7c4..9eb2d85fbe0 100644 --- a/integrationTests/vm/txsFee/multiShard/scCalls_test.go +++ b/integrationTests/vm/txsFee/multiShard/scCalls_test.go @@ -18,11 +18,11 @@ func TestScCallExecuteOnSourceAndDstShardShouldWork(t *testing.T) { enableEpochs := config.EnableEpochs{} - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs) + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, enableEpochs, 1) require.Nil(t, err) defer testContextSource.Close() - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs) + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, enableEpochs, 1) require.Nil(t, err) defer testContextDst.Close() @@ -98,11 +98,11 @@ func TestScCallExecuteOnSourceAndDstShardInvalidOnDst(t *testing.T) { t.Skip("this is not a short test") } - testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContextSource, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextSource.Close() - testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextDst, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextDst.Close() diff --git a/integrationTests/vm/txsFee/relayedAsyncCall_test.go b/integrationTests/vm/txsFee/relayedAsyncCall_test.go index d98a440b648..9b4e243ec6a 100644 --- a/integrationTests/vm/txsFee/relayedAsyncCall_test.go +++ b/integrationTests/vm/txsFee/relayedAsyncCall_test.go @@ -42,7 +42,7 @@ func TestRelayedAsyncCallShouldWork(t *testing.T) { } func testRelayedAsyncCallShouldWork(t *testing.T, enableEpochs config.EnableEpochs, senderAddr []byte) *vm.VMTestContext { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs, 1) require.Nil(t, err) localEgldBalance := big.NewInt(100000000) diff --git a/integrationTests/vm/txsFee/relayedAsyncESDT_test.go b/integrationTests/vm/txsFee/relayedAsyncESDT_test.go index 204f8e4b885..9958dc2e6b2 100644 --- a/integrationTests/vm/txsFee/relayedAsyncESDT_test.go +++ b/integrationTests/vm/txsFee/relayedAsyncESDT_test.go @@ -5,6 +5,7 @@ import ( "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" @@ -20,7 +21,7 @@ func TestRelayedAsyncESDTCallShouldWork(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -34,7 +35,7 @@ func TestRelayedAsyncESDTCallShouldWork(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance, uint32(core.Fungible)) _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, localEgldBalance) // deploy 2 contracts @@ -82,7 +83,7 @@ func TestRelayedAsyncESDTCall_InvalidCallFirstContract(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -96,7 +97,7 @@ func TestRelayedAsyncESDTCall_InvalidCallFirstContract(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance, uint32(core.Fungible)) _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, localEgldBalance) // deploy 2 contracts @@ -144,7 +145,7 @@ func TestRelayedAsyncESDTCall_InvalidOutOfGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -158,7 +159,7 @@ func TestRelayedAsyncESDTCall_InvalidOutOfGas(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance, uint32(core.Fungible)) _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, localEgldBalance) // deploy 2 contracts diff --git a/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go b/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go index 93396ce5deb..7688f147729 100644 --- a/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go +++ b/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go @@ -20,51 +20,62 @@ func TestRelayedBuildInFunctionChangeOwnerCallShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs( - config.EnableEpochs{ - PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, - }) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedBuildInFunctionChangeOwnerCallShouldWork(integrationTests.UnreachableEpoch, big.NewInt(25610), big.NewInt(4390))) + t.Run("after relayed base cost fix", testRelayedBuildInFunctionChangeOwnerCallShouldWork(0, big.NewInt(24854), big.NewInt(5146))) +} - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") - testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) - utils.CleanAccumulatedIntermediateTransactions(t, testContext) +func testRelayedBuildInFunctionChangeOwnerCallShouldWork( + relayedFixActivationEpoch uint32, + expectedBalanceRelayer *big.Int, + expectedAccumulatedFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs( + config.EnableEpochs{ + PenalizedTooMuchGasEnableEpoch: integrationTests.UnreachableEpoch, + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - newOwner := []byte("12345678901234567890123456789112") - gasLimit := uint64(1000) + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9991691, 8309, 39) + testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) + utils.CleanAccumulatedIntermediateTransactions(t, testContext) - txData := []byte(core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(newOwner)) - innerTx := vm.CreateTransaction(1, big.NewInt(0), owner, scAddress, gasPrice, gasLimit, txData) + relayerAddr := []byte("12345678901234567890123456789033") + newOwner := []byte("12345678901234567890123456789112") + gasLimit := uint64(1000) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + txData := []byte(core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(newOwner)) + innerTx := vm.CreateTransaction(1, big.NewInt(0), owner, scAddress, gasPrice, gasLimit, txData) - rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - utils.CheckOwnerAddr(t, testContext, scAddress, newOwner) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceRelayer := big.NewInt(16610) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) + utils.CheckOwnerAddr(t, testContext, scAddress, newOwner) - expectedBalance := big.NewInt(9988100) - vm.TestAccount(t, testContext.Accounts, owner, 2, expectedBalance) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(13390), accumulatedFees) + expectedBalance := big.NewInt(9991691) + vm.TestAccount(t, testContext.Accounts, owner, 2, expectedBalance) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(915), developerFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) + + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, big.NewInt(91), developerFees) + } } func TestRelayedBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas(t *testing.T) { @@ -72,49 +83,61 @@ func TestRelayedBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas(t *test t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(25610), big.NewInt(4390))) + t.Run("after relayed base cost fix", testRelayedBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas(0, big.NewInt(25610), big.NewInt(4390))) +} - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") - testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) - utils.CleanAccumulatedIntermediateTransactions(t, testContext) +func testRelayedBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedBalanceRelayer *big.Int, + expectedAccumulatedFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789113") - newOwner := []byte("12345678901234567890123456789112") - gasLimit := uint64(1000) + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9991691, 8309, 39) + testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) + utils.CleanAccumulatedIntermediateTransactions(t, testContext) - txData := []byte(core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(newOwner)) - innerTx := vm.CreateTransaction(1, big.NewInt(0), sndAddr, scAddress, gasPrice, gasLimit, txData) + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789113") + newOwner := []byte("12345678901234567890123456789112") + gasLimit := uint64(1000) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + txData := []byte(core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(newOwner)) + innerTx := vm.CreateTransaction(1, big.NewInt(0), sndAddr, scAddress, gasPrice, gasLimit, txData) - rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Equal(t, process.ErrFailedTransaction, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Equal(t, process.ErrFailedTransaction, err) - utils.CheckOwnerAddr(t, testContext, scAddress, owner) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceRelayer := big.NewInt(16610) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) + utils.CheckOwnerAddr(t, testContext, scAddress, owner) - expectedBalance := big.NewInt(9988100) - vm.TestAccount(t, testContext.Accounts, owner, 1, expectedBalance) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(13390), accumulatedFees) + expectedBalance := big.NewInt(9991691) + vm.TestAccount(t, testContext.Accounts, owner, 1, expectedBalance) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(0), developerFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) + + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, big.NewInt(0), developerFees) + } } func TestRelayedBuildInFunctionChangeOwnerInvalidAddressShouldConsumeGas(t *testing.T) { @@ -122,11 +145,11 @@ func TestRelayedBuildInFunctionChangeOwnerInvalidAddressShouldConsumeGas(t *test t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) utils.CleanAccumulatedIntermediateTransactions(t, testContext) @@ -140,7 +163,7 @@ func TestRelayedBuildInFunctionChangeOwnerInvalidAddressShouldConsumeGas(t *test _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) @@ -173,13 +196,15 @@ func TestRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeG t.Run("nonce fix is disabled, should increase the sender's nonce", func(t *testing.T) { testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeGas(t, config.EnableEpochs{ - RelayedNonceFixEnableEpoch: 1000, + RelayedNonceFixEnableEpoch: 1000, + FixRelayedBaseCostEnableEpoch: 1000, }) }) t.Run("nonce fix is enabled, should still increase the sender's nonce", func(t *testing.T) { testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeGas(t, config.EnableEpochs{ - RelayedNonceFixEnableEpoch: 0, + RelayedNonceFixEnableEpoch: 0, + FixRelayedBaseCostEnableEpoch: 1000, }) }) } @@ -188,11 +213,11 @@ func testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeG t *testing.T, enableEpochs config.EnableEpochs, ) { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) utils.CleanAccumulatedIntermediateTransactions(t, testContext) @@ -206,7 +231,7 @@ func testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeG _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) @@ -236,11 +261,11 @@ func TestRelayedBuildInFunctionChangeOwnerCallOutOfGasShouldConsumeGas(t *testin t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, owner := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) testContext.TxFeeHandler.CreateBlockStarted(getZeroGasAndFees()) utils.CleanAccumulatedIntermediateTransactions(t, testContext) @@ -248,13 +273,13 @@ func TestRelayedBuildInFunctionChangeOwnerCallOutOfGasShouldConsumeGas(t *testin newOwner := []byte("12345678901234567890123456789112") txData := []byte(core.BuiltInFunctionChangeOwnerAddress + "@" + hex.EncodeToString(newOwner)) - gasLimit := uint64(len(txData) + 1) + gasLimit := uint64(len(txData)) + minGasLimit innerTx := vm.CreateTransaction(1, big.NewInt(0), owner, scAddress, gasPrice, gasLimit, txData) _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) diff --git a/integrationTests/vm/txsFee/relayedDns_test.go b/integrationTests/vm/txsFee/relayedDns_test.go index 54c70be0ee8..389941886e7 100644 --- a/integrationTests/vm/txsFee/relayedDns_test.go +++ b/integrationTests/vm/txsFee/relayedDns_test.go @@ -18,7 +18,7 @@ func TestRelayedTxDnsTransaction_ShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/relayedESDT_test.go b/integrationTests/vm/txsFee/relayedESDT_test.go index c9837fb7075..ffe9597f943 100644 --- a/integrationTests/vm/txsFee/relayedESDT_test.go +++ b/integrationTests/vm/txsFee/relayedESDT_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" @@ -17,95 +18,121 @@ func TestRelayedESDTTransferShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedESDTTransferShouldWork(integrationTests.UnreachableEpoch, big.NewInt(9997614), big.NewInt(2386))) + t.Run("after relayed base cost fix", testRelayedESDTTransferShouldWork(0, big.NewInt(9997299), big.NewInt(2701))) +} + +func testRelayedESDTTransferShouldWork( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789012") - rcvAddr := []byte("12345678901234567890123456789022") + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") + rcvAddr := []byte("12345678901234567890123456789022") - relayerBalance := big.NewInt(10000000) - localEsdtBalance := big.NewInt(100000000) - token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, relayerBalance) + relayerBalance := big.NewInt(10000000) + localEsdtBalance := big.NewInt(100000000) + token := []byte("miiutoken") + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance, uint32(core.Fungible)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, relayerBalance) - gasLimit := uint64(40) - innerTx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100), gasPrice, gasLimit) + gasLimit := uint64(40) + innerTx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100), gasPrice, gasLimit) - rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceSnd := big.NewInt(99999900) - utils.CheckESDTBalance(t, testContext, sndAddr, token, expectedBalanceSnd) + expectedBalanceSnd := big.NewInt(99999900) + utils.CheckESDTBalance(t, testContext, sndAddr, token, expectedBalanceSnd) - expectedReceiverBalance := big.NewInt(100) - utils.CheckESDTBalance(t, testContext, rcvAddr, token, expectedReceiverBalance) + expectedReceiverBalance := big.NewInt(100) + utils.CheckESDTBalance(t, testContext, rcvAddr, token, expectedReceiverBalance) - expectedEGLDBalance := big.NewInt(0) - utils.TestAccount(t, testContext.Accounts, sndAddr, 1, expectedEGLDBalance) + expectedEGLDBalance := big.NewInt(0) + utils.TestAccount(t, testContext.Accounts, sndAddr, 1, expectedEGLDBalance) - utils.TestAccount(t, testContext.Accounts, relayerAddr, 1, big.NewInt(9997290)) + utils.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(2710), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) + } } -func TestTestRelayedESTTransferNotEnoughESTValueShouldConsumeGas(t *testing.T) { +func TestRelayedESTTransferNotEnoughESTValueShouldConsumeGas(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedESTTransferNotEnoughESTValueShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(9997488), big.NewInt(2512))) + t.Run("after relayed base cost fix", testRelayedESTTransferNotEnoughESTValueShouldConsumeGas(0, big.NewInt(9997119), big.NewInt(2881))) +} + +func testRelayedESTTransferNotEnoughESTValueShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789012") - rcvAddr := []byte("12345678901234567890123456789022") + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") + rcvAddr := []byte("12345678901234567890123456789022") - relayerBalance := big.NewInt(10000000) - localEsdtBalance := big.NewInt(100000000) - token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, relayerBalance) + relayerBalance := big.NewInt(10000000) + localEsdtBalance := big.NewInt(100000000) + token := []byte("miiutoken") + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, big.NewInt(0), token, 0, localEsdtBalance, uint32(core.Fungible)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, relayerBalance) - gasLimit := uint64(40) - innerTx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100000001), gasPrice, gasLimit) + gasLimit := uint64(42) + innerTx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100000001), gasPrice, gasLimit) - rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.ExecutionFailed, retCode) + require.Nil(t, err) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceSnd := big.NewInt(100000000) - utils.CheckESDTBalance(t, testContext, sndAddr, token, expectedBalanceSnd) + expectedBalanceSnd := big.NewInt(100000000) + utils.CheckESDTBalance(t, testContext, sndAddr, token, expectedBalanceSnd) - expectedReceiverBalance := big.NewInt(0) - utils.CheckESDTBalance(t, testContext, rcvAddr, token, expectedReceiverBalance) + expectedReceiverBalance := big.NewInt(0) + utils.CheckESDTBalance(t, testContext, rcvAddr, token, expectedReceiverBalance) - expectedEGLDBalance := big.NewInt(0) - utils.TestAccount(t, testContext.Accounts, sndAddr, 1, expectedEGLDBalance) + expectedEGLDBalance := big.NewInt(0) + utils.TestAccount(t, testContext.Accounts, sndAddr, 1, expectedEGLDBalance) - utils.TestAccount(t, testContext.Accounts, relayerAddr, 1, big.NewInt(9997130)) + utils.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(2870), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) + } } diff --git a/integrationTests/vm/txsFee/relayedMoveBalance_test.go b/integrationTests/vm/txsFee/relayedMoveBalance_test.go index accdffbfb4e..1a81602ff82 100644 --- a/integrationTests/vm/txsFee/relayedMoveBalance_test.go +++ b/integrationTests/vm/txsFee/relayedMoveBalance_test.go @@ -23,7 +23,9 @@ func TestRelayedMoveBalanceShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: integrationTests.UnreachableEpoch, + }, 1) require.Nil(t, err) defer testContext.Close() @@ -32,7 +34,7 @@ func TestRelayedMoveBalanceShouldWork(t *testing.T) { rcvAddr := []byte("12345678901234567890123456789022") senderNonce := uint64(0) - senderBalance := big.NewInt(0) + senderBalance := big.NewInt(100) gasLimit := uint64(100) _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) @@ -42,8 +44,8 @@ func TestRelayedMoveBalanceShouldWork(t *testing.T) { userTx := vm.CreateTransaction(senderNonce, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, []byte("aaaa")) rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rTxGasLimit := gasLimit + minGasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) require.Equal(t, vmcommon.Ok, retCode) @@ -53,8 +55,8 @@ func TestRelayedMoveBalanceShouldWork(t *testing.T) { require.Nil(t, err) // check relayer balance - // 3000 - value(100) - gasLimit(275)*gasPrice(10) = 2850 - expectedBalanceRelayer := big.NewInt(150) + // 3000 - rTxFee(175)*gasPrice(10) + txFeeInner(1000) = 2750 + expectedBalanceRelayer := big.NewInt(250) vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) // check balance inner tx sender @@ -73,7 +75,7 @@ func TestRelayedMoveBalanceInvalidGasLimitShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -88,7 +90,7 @@ func TestRelayedMoveBalanceInvalidGasLimitShouldConsumeGas(t *testing.T) { rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 2 + userTx.GasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) _, err = testContext.TxProcessor.ProcessTransaction(rtx) require.Equal(t, process.ErrFailedTransaction, err) @@ -109,7 +111,9 @@ func TestRelayedMoveBalanceInvalidUserTxShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: integrationTests.UnreachableEpoch, + }, 1) require.Nil(t, err) defer testContext.Close() @@ -117,14 +121,14 @@ func TestRelayedMoveBalanceInvalidUserTxShouldConsumeGas(t *testing.T) { sndAddr := []byte("12345678901234567890123456789012") rcvAddr := []byte("12345678901234567890123456789022") - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(100)) userTx := vm.CreateTransaction(1, big.NewInt(100), sndAddr, rcvAddr, 1, 100, []byte("aaaa")) _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + userTx.GasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) + rTxGasLimit := minGasLimit + userTx.GasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) retcode, _ := testContext.TxProcessor.ProcessTransaction(rtx) require.Equal(t, vmcommon.UserError, retcode) @@ -132,6 +136,7 @@ func TestRelayedMoveBalanceInvalidUserTxShouldConsumeGas(t *testing.T) { _, err = testContext.Accounts.Commit() require.Nil(t, err) + // 3000 - rTxFee(179)*gasPrice(1) - innerTxFee(100) = 2721 expectedBalanceRelayer := big.NewInt(2721) vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) @@ -146,8 +151,9 @@ func TestRelayedMoveBalanceInvalidUserTxValueShouldConsumeGas(t *testing.T) { } testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ - RelayedNonceFixEnableEpoch: 1, - }) + RelayedNonceFixEnableEpoch: 1, + FixRelayedBaseCostEnableEpoch: integrationTests.UnreachableEpoch, + }, 1) require.Nil(t, err) defer testContext.Close() @@ -155,14 +161,14 @@ func TestRelayedMoveBalanceInvalidUserTxValueShouldConsumeGas(t *testing.T) { sndAddr := []byte("12345678901234567890123456789012") rcvAddr := []byte("12345678901234567890123456789022") - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(100)) userTx := vm.CreateTransaction(0, big.NewInt(150), sndAddr, rcvAddr, 1, 100, []byte("aaaa")) _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + userTx.GasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, big.NewInt(100), relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) + rTxGasLimit := minGasLimit + userTx.GasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) require.Equal(t, vmcommon.UserError, retCode) @@ -170,6 +176,7 @@ func TestRelayedMoveBalanceInvalidUserTxValueShouldConsumeGas(t *testing.T) { _, err = testContext.Accounts.Commit() require.Nil(t, err) + // 3000 - rTxFee(175)*gasPrice(1) - innerTxFee(100) = 2750 expectedBalanceRelayer := big.NewInt(2725) vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) @@ -185,7 +192,7 @@ func TestRelayedMoveBalanceHigherNonce(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ RelayedNonceFixEnableEpoch: 1, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -241,7 +248,7 @@ func TestRelayedMoveBalanceLowerNonce(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ RelayedNonceFixEnableEpoch: 1, - }) + }, 1) require.Nil(t, err) defer testContext.Close() @@ -305,6 +312,7 @@ func TestRelayedMoveBalanceHigherNonceWithActivatedFixCrossShard(t *testing.T) { shardCoordinator0, integrationtests.CreateMemUnit(), vm.CreateMockGasScheduleNotifier(), + 1, ) require.Nil(t, err) @@ -314,6 +322,7 @@ func TestRelayedMoveBalanceHigherNonceWithActivatedFixCrossShard(t *testing.T) { shardCoordinator1, integrationtests.CreateMemUnit(), vm.CreateMockGasScheduleNotifier(), + 1, ) require.Nil(t, err) defer testContext0.Close() @@ -357,7 +366,7 @@ func executeRelayedTransaction( ) { testContext.TxsLogsProcessor.Clean() relayerAccount := getAccount(tb, testContext, relayerAddress) - gasLimit := 1 + userTx.GasLimit + uint64(len(userTxPrepared)) + gasLimit := minGasLimit + userTx.GasLimit + uint64(len(userTxPrepared)) relayedTx := vm.CreateTransaction(relayerAccount.GetNonce(), value, relayerAddress, senderAddress, 1, gasLimit, userTxPrepared) retCode, _ := testContext.TxProcessor.ProcessTransaction(relayedTx) diff --git a/integrationTests/vm/txsFee/relayedScCalls_test.go b/integrationTests/vm/txsFee/relayedScCalls_test.go index 7441d55541f..20ee29b02e5 100644 --- a/integrationTests/vm/txsFee/relayedScCalls_test.go +++ b/integrationTests/vm/txsFee/relayedScCalls_test.go @@ -19,47 +19,59 @@ func TestRelayedScCallShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ - DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed base cost fix", testRelayedScCallShouldWork(integrationTests.UnreachableEpoch, big.NewInt(29982306), big.NewInt(25903), big.NewInt(1608))) + t.Run("after relayed base cost fix", testRelayedScCallShouldWork(0, big.NewInt(29982216), big.NewInt(25993), big.NewInt(1608))) +} - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") - utils.CleanAccumulatedIntermediateTransactions(t, testContext) +func testRelayedScCallShouldWork( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, + expectedDevFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789112") - gasLimit := uint64(100000) + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9991691, 8309, 39) + utils.CleanAccumulatedIntermediateTransactions(t, testContext) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000000)) + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789112") + gasLimit := uint64(100000) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000000)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - ret := vm.GetIntValueFromSC(nil, testContext.Accounts, scAddress, "get") - require.Equal(t, big.NewInt(2), ret) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalance := big.NewInt(29840970) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) + ret := vm.GetIntValueFromSC(nil, testContext.Accounts, scAddress, "get") + require.Equal(t, big.NewInt(2), ret) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(170830), accumulatedFees) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(16093), developerFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) + + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, expectedDevFees, developerFees) + } } func TestRelayedScCallContractNotFoundShouldConsumeGas(t *testing.T) { @@ -67,42 +79,54 @@ func TestRelayedScCallContractNotFoundShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScCallContractNotFoundShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(27130), big.NewInt(2870))) + t.Run("after relayed fix", testRelayedScCallContractNotFoundShouldConsumeGas(0, big.NewInt(27040), big.NewInt(2960))) +} - scAddress := "00000000000000000500dbb53e4b23392b0d6f36cce32deb2d623e9625ab3132" - scAddrBytes, _ := hex.DecodeString(scAddress) +func testRelayedScCallContractNotFoundShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789112") - gasLimit := uint64(1000) + scAddress := "00000000000000000500dbb53e4b23392b0d6f36cce32deb2d623e9625ab3132" + scAddrBytes, _ := hex.DecodeString(scAddress) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789112") + gasLimit := uint64(1000) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, []byte("increment")) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, []byte("increment")) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Nil(t, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Nil(t, err) + + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalance := big.NewInt(18130) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(11870), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(0), developerFees) + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, big.NewInt(0), developerFees) + } } func TestRelayedScCallInvalidMethodShouldConsumeGas(t *testing.T) { @@ -110,42 +134,54 @@ func TestRelayedScCallInvalidMethodShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScCallInvalidMethodShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(26924), big.NewInt(11385))) + t.Run("after relayed fix", testRelayedScCallInvalidMethodShouldConsumeGas(0, big.NewInt(26924), big.NewInt(11385))) +} - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") - utils.CleanAccumulatedIntermediateTransactions(t, testContext) +func testRelayedScCallInvalidMethodShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + RelayedNonceFixEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789112") - gasLimit := uint64(1000) + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9991691, 8309, 39) + utils.CleanAccumulatedIntermediateTransactions(t, testContext) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789112") + gasLimit := uint64(1000) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("invalidMethod")) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("invalidMethod")) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Nil(t, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Nil(t, err) - expectedBalance := big.NewInt(18050) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(23850), accumulatedFees) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(399), developerFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) + + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, big.NewInt(39), developerFees) + } } func TestRelayedScCallInsufficientGasLimitShouldConsumeGas(t *testing.T) { @@ -153,41 +189,54 @@ func TestRelayedScCallInsufficientGasLimitShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScCallInsufficientGasLimitShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(28140), big.NewInt(10169))) + t.Run("after relayed fix", testRelayedScCallInsufficientGasLimitShouldConsumeGas(0, big.NewInt(28050), big.NewInt(10259))) +} - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") - utils.CleanAccumulatedIntermediateTransactions(t, testContext) +func testRelayedScCallInsufficientGasLimitShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedBalance *big.Int, + expectedAccumulatedFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789112") - gasLimit := uint64(5) + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9991691, 8309, 39) + utils.CleanAccumulatedIntermediateTransactions(t, testContext) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789112") + data := "increment" + gasLimit := minGasLimit + uint64(len(data)) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte(data)) - retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalance := big.NewInt(28100) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(13800), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(399), developerFees) + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, big.NewInt(39), developerFees) + } } func TestRelayedScCallOutOfGasShouldConsumeGas(t *testing.T) { @@ -195,42 +244,54 @@ func TestRelayedScCallOutOfGasShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScCallOutOfGasShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(28040), big.NewInt(10269))) + t.Run("after relayed fix", testRelayedScCallOutOfGasShouldConsumeGas(0, big.NewInt(28040), big.NewInt(10269))) +} - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") - utils.CleanAccumulatedIntermediateTransactions(t, testContext) +func testRelayedScCallOutOfGasShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + RelayedNonceFixEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789112") - gasLimit := uint64(20) + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9991691, 8309, 39) + utils.CleanAccumulatedIntermediateTransactions(t, testContext) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789112") + gasLimit := uint64(20) - userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) - require.Nil(t, err) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Nil(t, err) + + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalance := big.NewInt(27950) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(13950), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) - developerFees := testContext.TxFeeHandler.GetDeveloperFees() - require.Equal(t, big.NewInt(399), developerFees) + developerFees := testContext.TxFeeHandler.GetDeveloperFees() + require.Equal(t, big.NewInt(39), developerFees) + } } func TestRelayedDeployInvalidContractShouldIncrementNonceOnSender(t *testing.T) { @@ -281,7 +342,7 @@ func testRelayedDeployInvalidContractShouldIncrementNonceOnSender( senderAddr []byte, senderNonce uint64, ) *vm.VMTestContext { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs, 1) require.Nil(t, err) relayerAddr := []byte("12345678901234567890123456789033") @@ -294,7 +355,7 @@ func testRelayedDeployInvalidContractShouldIncrementNonceOnSender( userTx := vm.CreateTransaction(senderNonce, big.NewInt(100), senderAddr, emptyAddress, gasPrice, gasLimit, nil) rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rTxGasLimit := minGasLimit + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, senderAddr, gasPrice, rTxGasLimit, rtxData) retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) diff --git a/integrationTests/vm/txsFee/relayedScDeploy_test.go b/integrationTests/vm/txsFee/relayedScDeploy_test.go index 15d6d677b44..21bd43df7e2 100644 --- a/integrationTests/vm/txsFee/relayedScDeploy_test.go +++ b/integrationTests/vm/txsFee/relayedScDeploy_test.go @@ -17,43 +17,55 @@ func TestRelayedScDeployShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScDeployShouldWork(integrationTests.UnreachableEpoch, big.NewInt(20170), big.NewInt(29830))) + t.Run("after relayed fix", testRelayedScDeployShouldWork(0, big.NewInt(8389), big.NewInt(41611))) +} + +func testRelayedScDeployShouldWork( + relayedFixActivationEpoch uint32, + expectedRelayerBalance *big.Int, + expectedAccFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789012") + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") - senderNonce := uint64(0) - senderBalance := big.NewInt(0) - gasLimit := uint64(1962) + senderNonce := uint64(0) + senderBalance := big.NewInt(0) + gasLimit := uint64(2000) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) - scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") - userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) + scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") + userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.Ok, retCode) - require.Nil(t, err) + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.Ok, retCode) + require.Nil(t, err) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceRelayer := big.NewInt(2530) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedRelayerBalance) - // check balance inner tx sender - vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) + // check balance inner tx sender + vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(47470), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccFees, accumulatedFees) + } } func TestRelayedScDeployInvalidCodeShouldConsumeGas(t *testing.T) { @@ -61,44 +73,56 @@ func TestRelayedScDeployInvalidCodeShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScDeployInvalidCodeShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(20716), big.NewInt(29284))) + t.Run("after relayed fix", testRelayedScDeployInvalidCodeShouldConsumeGas(0, big.NewInt(8890), big.NewInt(41110))) +} + +func testRelayedScDeployInvalidCodeShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedBalance *big.Int, + expectedAccumulatedFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789012") + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") - senderNonce := uint64(0) - senderBalance := big.NewInt(0) - gasLimit := uint64(500) + senderNonce := uint64(0) + senderBalance := big.NewInt(0) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) - scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") - scCodeBytes := []byte(wasm.CreateDeployTxData(scCode)) - scCodeBytes = append(scCodeBytes, []byte("aaaaa")...) - userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, scCodeBytes) + scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") + scCodeBytes := []byte(wasm.CreateDeployTxData(scCode)) + scCodeBytes = append(scCodeBytes, []byte("aaaaa")...) + gasLimit := minGasLimit + uint64(len(scCodeBytes)) + userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, scCodeBytes) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) + retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceRelayer := big.NewInt(17030) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) - // check balance inner tx sender - vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) + // check balance inner tx sender + vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(32970), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) + } } func TestRelayedScDeployInsufficientGasLimitShouldConsumeGas(t *testing.T) { @@ -106,42 +130,55 @@ func TestRelayedScDeployInsufficientGasLimitShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScDeployInsufficientGasLimitShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(20821), big.NewInt(29179))) + t.Run("after relayed fix", testRelayedScDeployInsufficientGasLimitShouldConsumeGas(0, big.NewInt(9040), big.NewInt(40960))) +} + +func testRelayedScDeployInsufficientGasLimitShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedBalance *big.Int, + expectedAccumulatedFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789012") + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") - senderNonce := uint64(0) - senderBalance := big.NewInt(0) - gasLimit := uint64(500) + senderNonce := uint64(0) + senderBalance := big.NewInt(0) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) - scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") - userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) + scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") + data := wasm.CreateDeployTxData(scCode) + gasLimit := minGasLimit + uint64(len(data)) + userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(data)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, retCode) + retCode, _ := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceRelayer := big.NewInt(17130) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) - // check balance inner tx sender - vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) + // check balance inner tx sender + vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(32870), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) + } } func TestRelayedScDeployOutOfGasShouldConsumeGas(t *testing.T) { @@ -149,41 +186,54 @@ func TestRelayedScDeployOutOfGasShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) - require.Nil(t, err) - defer testContext.Close() + t.Run("before relayed fix", testRelayedScDeployOutOfGasShouldConsumeGas(integrationTests.UnreachableEpoch, big.NewInt(20821), big.NewInt(29179))) + t.Run("after relayed fix", testRelayedScDeployOutOfGasShouldConsumeGas(0, big.NewInt(9040), big.NewInt(40960))) +} + +func testRelayedScDeployOutOfGasShouldConsumeGas( + relayedFixActivationEpoch uint32, + expectedBalance *big.Int, + expectedAccumulatedFees *big.Int, +) func(t *testing.T) { + return func(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + FixRelayedBaseCostEnableEpoch: relayedFixActivationEpoch, + }, gasPriceModifier) + require.Nil(t, err) + defer testContext.Close() - relayerAddr := []byte("12345678901234567890123456789033") - sndAddr := []byte("12345678901234567890123456789012") + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") - senderNonce := uint64(0) - senderBalance := big.NewInt(0) - gasLimit := uint64(570) + senderNonce := uint64(0) + senderBalance := big.NewInt(0) - _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) - _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, senderBalance) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(50000)) - scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") - userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) + scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") + data := wasm.CreateDeployTxData(scCode) + gasLimit := minGasLimit + uint64(len(data)) + userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(data)) - rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) - rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) - rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) - code, err := testContext.TxProcessor.ProcessTransaction(rtx) - require.Equal(t, vmcommon.UserError, code) - require.Nil(t, err) + code, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, code) + require.Nil(t, err) - _, err = testContext.Accounts.Commit() - require.Nil(t, err) + _, err = testContext.Accounts.Commit() + require.Nil(t, err) - expectedBalanceRelayer := big.NewInt(16430) - vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) + vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalance) - // check balance inner tx sender - vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) + // check balance inner tx sender + vm.TestAccount(t, testContext.Accounts, sndAddr, 1, big.NewInt(0)) - // check accumulated fees - accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() - require.Equal(t, big.NewInt(33570), accumulatedFees) + // check accumulated fees + accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() + require.Equal(t, expectedAccumulatedFees, accumulatedFees) + } } diff --git a/integrationTests/vm/txsFee/scCalls_test.go b/integrationTests/vm/txsFee/scCalls_test.go index 0c2262a9362..c98d0960b66 100644 --- a/integrationTests/vm/txsFee/scCalls_test.go +++ b/integrationTests/vm/txsFee/scCalls_test.go @@ -66,6 +66,7 @@ func prepareTestContextForEpoch836(tb testing.TB) (*vm.VMTestContext, []byte) { db, gasScheduleNotifier, testscommon.GetDefaultRoundsConfig(), + 1, ) require.Nil(tb, err) @@ -92,11 +93,11 @@ func TestScCallShouldWork(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) sndAddr := []byte("12345678901234567890123456789112") @@ -138,7 +139,7 @@ func TestScCallContractNotFoundShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -171,11 +172,11 @@ func TestScCallInvalidMethodToCallShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) sndAddr := []byte("12345678901234567890123456789112") @@ -208,11 +209,11 @@ func TestScCallInsufficientGasLimitShouldNotConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) sndAddr := []byte("12345678901234567890123456789112") senderBalance := big.NewInt(100000) @@ -246,11 +247,11 @@ func TestScCallOutOfGasShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) sndAddr := []byte("12345678901234567890123456789112") @@ -285,13 +286,13 @@ func TestScCallAndGasChangeShouldWork(t *testing.T) { testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ DynamicGasCostForDataTrieStorageLoadEnableEpoch: integrationTests.UnreachableEpoch, - }) + }, 1) require.Nil(t, err) defer testContext.Close() mockGasSchedule := testContext.GasSchedule.(*mock.GasScheduleNotifierMock) - scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm") + scAddress, _ := utils.DoDeploy(t, testContext, "../wasm/testdata/counter/output/counter.wasm", 9988100, 11900, 399) utils.CleanAccumulatedIntermediateTransactions(t, testContext) sndAddr := []byte("12345678901234567890123456789112") @@ -332,7 +333,7 @@ func TestESDTScCallAndGasChangeShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -351,7 +352,7 @@ func TestESDTScCallAndGasChangeShouldWork(t *testing.T) { localEsdtBalance := big.NewInt(100000000) token := []byte("miiutoken") - utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, senderBalance, token, 0, localEsdtBalance) + utils.CreateAccountWithESDTBalance(t, testContext.Accounts, sndAddr, senderBalance, token, 0, localEsdtBalance, uint32(core.Fungible)) txData := txDataBuilder.NewBuilder() valueToSendToSc := int64(1000) @@ -421,7 +422,7 @@ func prepareTestContextForEpoch460(tb testing.TB) (*vm.VMTestContext, []byte) { RefactorPeersMiniBlocksEnableEpoch: unreachableEpoch, RuntimeMemStoreLimitEnableEpoch: unreachableEpoch, MaxBlockchainHookCountersEnableEpoch: unreachableEpoch, - }) + }, 1) require.Nil(tb, err) senderBalance := big.NewInt(1000000000000000000) diff --git a/integrationTests/vm/txsFee/scDeploy_test.go b/integrationTests/vm/txsFee/scDeploy_test.go index 8410bcf4917..ea646e6db73 100644 --- a/integrationTests/vm/txsFee/scDeploy_test.go +++ b/integrationTests/vm/txsFee/scDeploy_test.go @@ -17,7 +17,7 @@ func TestScDeployShouldWork(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -52,7 +52,7 @@ func TestScDeployInvalidContractCodeShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -88,7 +88,7 @@ func TestScDeployInsufficientGasLimitShouldNotConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() @@ -123,7 +123,7 @@ func TestScDeployOutOfGasShouldConsumeGas(t *testing.T) { t.Skip("this is not a short test") } - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) defer testContext.Close() diff --git a/integrationTests/vm/txsFee/utils/utils.go b/integrationTests/vm/txsFee/utils/utils.go index 3eea35a4833..bc7abfbeaf0 100644 --- a/integrationTests/vm/txsFee/utils/utils.go +++ b/integrationTests/vm/txsFee/utils/utils.go @@ -37,8 +37,11 @@ func DoDeploy( t *testing.T, testContext *vm.VMTestContext, pathToContract string, + expectedBalance, + expectedAccFees, + expectedDevFees int64, ) (scAddr []byte, owner []byte) { - return doDeployInternal(t, testContext, pathToContract, 9988100, 11900, 399) + return doDeployInternal(t, testContext, pathToContract, expectedBalance, expectedAccFees, expectedDevFees) } // DoDeployOldCounter - diff --git a/integrationTests/vm/txsFee/utils/utilsESDT.go b/integrationTests/vm/txsFee/utils/utilsESDT.go index 68fe255a1ba..99428ecfcf7 100644 --- a/integrationTests/vm/txsFee/utils/utilsESDT.go +++ b/integrationTests/vm/txsFee/utils/utilsESDT.go @@ -27,6 +27,7 @@ func CreateAccountWithESDTBalance( tokenIdentifier []byte, esdtNonce uint64, esdtValue *big.Int, + esdtType uint32, ) { account, err := accnts.LoadAccount(pubKey) require.Nil(t, err) @@ -39,6 +40,7 @@ func CreateAccountWithESDTBalance( require.Nil(t, err) esdtData := &esdt.ESDigitalToken{ + Type: esdtType, Value: esdtValue, Properties: []byte{}, } @@ -92,6 +94,7 @@ func CreateAccountWithNFT( require.Nil(t, err) esdtData := &esdt.ESDigitalToken{ + Type: uint32(core.NonFungible), Value: big.NewInt(1), Properties: []byte{}, TokenMetaData: &esdt.MetaData{ @@ -152,7 +155,7 @@ func CreateAccountWithESDTBalanceAndRoles( esdtValue *big.Int, roles [][]byte, ) { - CreateAccountWithESDTBalance(t, accnts, pubKey, egldValue, tokenIdentifier, esdtNonce, esdtValue) + CreateAccountWithESDTBalance(t, accnts, pubKey, egldValue, tokenIdentifier, esdtNonce, esdtValue, uint32(core.Fungible)) SetESDTRoles(t, accnts, pubKey, tokenIdentifier, roles) } diff --git a/integrationTests/vm/txsFee/validatorSC_test.go b/integrationTests/vm/txsFee/validatorSC_test.go index 6de545c5c93..c54025a90b1 100644 --- a/integrationTests/vm/txsFee/validatorSC_test.go +++ b/integrationTests/vm/txsFee/validatorSC_test.go @@ -54,7 +54,7 @@ func TestValidatorsSC_DoStakePutInQueueUnStakeAndUnBondShouldRefund(t *testing.T t.Skip("this is not a short test") } - testContextMeta, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, config.EnableEpochs{}) + testContextMeta, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextMeta.Close() @@ -120,6 +120,7 @@ func TestValidatorsSC_DoStakePutInQueueUnStakeAndUnBondTokensShouldRefund(t *tes StakingV4Step1EnableEpoch: stakingV4Step1EnableEpoch, StakingV4Step2EnableEpoch: stakingV4Step2EnableEpoch, }, + 1, ) require.Nil(t, err) @@ -170,7 +171,7 @@ func TestValidatorsSC_DoStakeWithTopUpValueTryToUnStakeTokensAndUnBondTokens(t * } func testValidatorsSCDoStakeWithTopUpValueTryToUnStakeTokensAndUnBondTokens(t *testing.T, enableEpochs config.EnableEpochs) { - testContextMeta, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, enableEpochs) + testContextMeta, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(core.MetachainShardId, enableEpochs, 1) require.Nil(t, err) defer testContextMeta.Close() @@ -207,6 +208,7 @@ func TestValidatorsSC_ToStakePutInQueueUnStakeAndUnBondShouldRefundUnBondTokens( StakingV4Step1EnableEpoch: stakingV4Step1EnableEpoch, StakingV4Step2EnableEpoch: stakingV4Step2EnableEpoch, }, + 1, ) require.Nil(t, err) @@ -263,6 +265,7 @@ func TestValidatorsSC_ToStakePutInQueueUnStakeNodesAndUnBondNodesShouldRefund(t StakingV4Step1EnableEpoch: stakingV4Step1EnableEpoch, StakingV4Step2EnableEpoch: stakingV4Step2EnableEpoch, }, + 1, ) require.Nil(t, err) diff --git a/integrationTests/vm/wasm/wasmvm/mockcontracts/simpleChildSC.go b/integrationTests/vm/wasm/wasmvm/mockcontracts/simpleChildSC.go deleted file mode 100644 index 7e1bb9dbaf5..00000000000 --- a/integrationTests/vm/wasm/wasmvm/mockcontracts/simpleChildSC.go +++ /dev/null @@ -1,50 +0,0 @@ -package mockcontracts - -import ( - "errors" - - mock "github.com/multiversx/mx-chain-vm-go/mock/context" - test "github.com/multiversx/mx-chain-vm-go/testcommon" - "github.com/multiversx/mx-chain-vm-go/vmhost" -) - -// SimpleCallChildMock is an exposed mock contract method -func SimpleCallChildMock(instanceMock *mock.InstanceMock, config interface{}) { - instanceMock.AddMockMethod("simpleChildFunction", func() *mock.InstanceMock { - testConfig := config.(*test.TestConfig) - host := instanceMock.Host - instance := mock.GetMockInstance(host) - - arguments := host.Runtime().Arguments() - if len(arguments) != 1 { - host.Runtime().SignalUserError("wrong num of arguments") - return instance - } - - host.Metering().UseGas(testConfig.GasUsedByChild) - - behavior := byte(0) - if len(arguments[0]) != 0 { - behavior = arguments[0][0] - } - err := handleChildBehaviorArgument(host, behavior) - if err != nil { - return instance - } - - _, _ = host.Storage().SetStorage(test.ChildKey, test.ChildData) - host.Output().Finish(test.ChildFinish) - - return instance - }) -} - -func handleChildBehaviorArgument(host vmhost.VMHost, behavior byte) error { - if behavior == 1 { - host.Runtime().SignalUserError("child error") - return errors.New("behavior / child error") - } - - host.Output().Finish([]byte{behavior}) - return nil -} diff --git a/integrationTests/vm/wasm/wasmvm/mockcontracts/simpleParentSC.go b/integrationTests/vm/wasm/wasmvm/mockcontracts/simpleParentSC.go deleted file mode 100644 index 63f4fcc5d4e..00000000000 --- a/integrationTests/vm/wasm/wasmvm/mockcontracts/simpleParentSC.go +++ /dev/null @@ -1,43 +0,0 @@ -package mockcontracts - -import ( - "math/big" - - mock "github.com/multiversx/mx-chain-vm-go/mock/context" - test "github.com/multiversx/mx-chain-vm-go/testcommon" - "github.com/multiversx/mx-chain-vm-go/vmhost" - "github.com/multiversx/mx-chain-vm-go/vmhost/vmhooks" - "github.com/stretchr/testify/require" -) - -var failBehavior = []byte{1} - -// PerformOnDestCallFailParentMock is an exposed mock contract method -func PerformOnDestCallFailParentMock(instanceMock *mock.InstanceMock, config interface{}) { - instanceMock.AddMockMethod("performOnDestCallFail", func() *mock.InstanceMock { - testConfig := config.(*test.TestConfig) - host := instanceMock.Host - instance := mock.GetMockInstance(host) - t := instance.T - - err := host.Metering().UseGasBounded(testConfig.GasUsedByParent) - if err != nil { - host.Runtime().SetRuntimeBreakpointValue(vmhost.BreakpointOutOfGas) - return instance - } - - _, _ = host.Storage().SetStorage(test.ParentKeyA, test.ParentDataA) - host.Output().Finish(test.ParentFinishA) - - retVal := vmhooks.ExecuteOnDestContextWithTypedArgs( - host, - int64(testConfig.GasProvidedToChild), - big.NewInt(0), - []byte("simpleChildFunction"), - testConfig.ChildAddress, - [][]byte{failBehavior}) - require.Equal(t, retVal, int32(1)) - - return instance - }) -} diff --git a/integrationTests/vm/wasm/wasmvm/scenariosConverter/scenariosConverterUtils.go b/integrationTests/vm/wasm/wasmvm/scenariosConverter/scenariosConverterUtils.go index 2d3d15f681d..ad23085011f 100644 --- a/integrationTests/vm/wasm/wasmvm/scenariosConverter/scenariosConverterUtils.go +++ b/integrationTests/vm/wasm/wasmvm/scenariosConverter/scenariosConverterUtils.go @@ -119,7 +119,7 @@ func SetStateFromScenariosTest(scenariosTestPath string) (testContext *vm.VMTest if err != nil { return nil, nil, exporter.InvalidBenchmarkTxPos, err } - testContext, err = vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err = vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) if err != nil { return nil, nil, exporter.InvalidBenchmarkTxPos, err } @@ -140,7 +140,7 @@ func SetStateFromScenariosTest(scenariosTestPath string) (testContext *vm.VMTest func CheckConverter(t *testing.T, scenariosTestPath string) { stateAndBenchmarkInfo, err := exporter.GetAccountsAndTransactionsFromScenarios(scenariosTestPath) require.Nil(t, err) - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}, 1) require.Nil(t, err) err = CreateAccountsFromScenariosAccs(testContext, stateAndBenchmarkInfo.Accs) require.Nil(t, err) diff --git a/integrationTests/vm/wasm/wasmvm/testRunner.go b/integrationTests/vm/wasm/wasmvm/testRunner.go index e6756b1a4c2..f5384189d16 100644 --- a/integrationTests/vm/wasm/wasmvm/testRunner.go +++ b/integrationTests/vm/wasm/wasmvm/testRunner.go @@ -3,6 +3,7 @@ package wasmvm import ( "crypto/rand" "encoding/hex" + "errors" "fmt" "math/big" "time" @@ -171,7 +172,7 @@ func DeployAndExecuteERC20WithBigInt( return nil, err } if returnCode != vmcommon.Ok { - return nil, fmt.Errorf(returnCode.String()) + return nil, errors.New(returnCode.String()) } ownerNonce++ @@ -263,7 +264,7 @@ func SetupERC20Test( return err } if returnCode != vmcommon.Ok { - return fmt.Errorf(returnCode.String()) + return errors.New(returnCode.String()) } testContext.ContractOwner.Nonce++ diff --git a/integrationTests/vm/wasm/wasmvm/wasmVM_test.go b/integrationTests/vm/wasm/wasmvm/wasmVM_test.go index 0028787d84e..2957aa42add 100644 --- a/integrationTests/vm/wasm/wasmvm/wasmVM_test.go +++ b/integrationTests/vm/wasm/wasmvm/wasmVM_test.go @@ -945,11 +945,11 @@ func TestCommunityContract_CrossShard_TxProcessor(t *testing.T) { zero := big.NewInt(0) transferEGLD := big.NewInt(42) - testContextFunderSC, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}) + testContextFunderSC, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(0, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextFunderSC.Close() - testContextParentSC, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}) + testContextParentSC, err := vm.CreatePreparedTxProcessorWithVMsMultiShard(1, config.EnableEpochs{}, 1) require.Nil(t, err) defer testContextParentSC.Close() diff --git a/node/chainSimulator/chainSimulator.go b/node/chainSimulator/chainSimulator.go index 647b53b1a8a..6f4f732e6dd 100644 --- a/node/chainSimulator/chainSimulator.go +++ b/node/chainSimulator/chainSimulator.go @@ -42,23 +42,24 @@ type transactionWithResult struct { // ArgsChainSimulator holds the arguments needed to create a new instance of simulator type ArgsChainSimulator struct { - BypassTxSignatureCheck bool - TempDir string - PathToInitialConfig string - NumOfShards uint32 - MinNodesPerShard uint32 - MetaChainMinNodes uint32 - Hysteresis float32 - NumNodesWaitingListShard uint32 - NumNodesWaitingListMeta uint32 - GenesisTimestamp int64 - InitialRound int64 - InitialEpoch uint32 - InitialNonce uint64 - RoundDurationInMillis uint64 - RoundsPerEpoch core.OptionalUint64 - ApiInterface components.APIConfigurator - AlterConfigsFunction func(cfg *config.Configs) + BypassTxSignatureCheck bool + TempDir string + PathToInitialConfig string + NumOfShards uint32 + MinNodesPerShard uint32 + MetaChainMinNodes uint32 + Hysteresis float32 + NumNodesWaitingListShard uint32 + NumNodesWaitingListMeta uint32 + GenesisTimestamp int64 + InitialRound int64 + InitialEpoch uint32 + InitialNonce uint64 + RoundDurationInMillis uint64 + RoundsPerEpoch core.OptionalUint64 + ApiInterface components.APIConfigurator + AlterConfigsFunction func(cfg *config.Configs) + VmQueryDelayAfterStartInMs uint64 } // ArgsBaseChainSimulator holds the arguments needed to create a new instance of simulator @@ -158,7 +159,7 @@ func (s *simulator) createChainHandlers(args ArgsBaseChainSimulator) error { } allValidatorsInfo, errGet := node.GetProcessComponents().ValidatorsStatistics().GetValidatorInfoForRootHash(currentRootHash) - if errRootHash != nil { + if errGet != nil { return errGet } @@ -214,6 +215,7 @@ func (s *simulator) createTestNode( MinNodesMeta: args.MetaChainMinNodes, MetaChainConsensusGroupSize: args.MetaChainConsensusGroupSize, RoundDurationInMillis: args.RoundDurationInMillis, + VmQueryDelayAfterStartInMs: args.VmQueryDelayAfterStartInMs, } return components.NewTestOnlyProcessingNode(argsTestOnlyProcessorNode) @@ -298,15 +300,18 @@ func (s *simulator) incrementRoundOnAllValidators() { // ForceChangeOfEpoch will force the change of current epoch // This method will call the epoch change trigger and generate block till a new epoch is reached func (s *simulator) ForceChangeOfEpoch() error { + s.mutex.Lock() log.Info("force change of epoch") for shardID, node := range s.nodes { err := node.ForceChangeOfEpoch() if err != nil { + s.mutex.Unlock() return fmt.Errorf("force change of epoch shardID-%d: error-%w", shardID, err) } } epoch := s.nodes[core.MetachainShardId].GetProcessComponents().EpochStartTrigger().Epoch() + s.mutex.Unlock() return s.GenerateBlocksUntilEpochIsReached(int32(epoch + 1)) } diff --git a/node/chainSimulator/chainSimulator_test.go b/node/chainSimulator/chainSimulator_test.go index 3ed39bc8fba..18f54ccbfe9 100644 --- a/node/chainSimulator/chainSimulator_test.go +++ b/node/chainSimulator/chainSimulator_test.go @@ -1,16 +1,19 @@ package chainSimulator import ( + "github.com/multiversx/mx-chain-go/errors" "math/big" + "strings" "testing" "time" + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" chainSimulatorCommon "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" + "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" - - "github.com/multiversx/mx-chain-core-go/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -380,3 +383,50 @@ func TestSimulator_SendTransactions(t *testing.T) { chainSimulatorCommon.CheckGenerateTransactions(t, chainSimulator) } + +func TestSimulator_SentMoveBalanceNoGasForFee(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startTime := time.Now().Unix() + roundDurationInMillis := uint64(6000) + roundsPerEpoch := core.OptionalUint64{ + HasValue: true, + Value: 20, + } + chainSimulator, err := NewChainSimulator(ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: 3, + GenesisTimestamp: startTime, + RoundDurationInMillis: roundDurationInMillis, + RoundsPerEpoch: roundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 1, + MetaChainMinNodes: 1, + }) + require.Nil(t, err) + require.NotNil(t, chainSimulator) + + defer chainSimulator.Close() + + wallet0, err := chainSimulator.GenerateAndMintWalletAddress(0, big.NewInt(0)) + require.Nil(t, err) + + ftx := &transaction.Transaction{ + Nonce: 0, + Value: big.NewInt(0), + SndAddr: wallet0.Bytes, + RcvAddr: wallet0.Bytes, + Data: []byte(""), + GasLimit: 50_000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 1, + Signature: []byte("010101"), + } + _, err = chainSimulator.sendTx(ftx) + require.True(t, strings.Contains(err.Error(), errors.ErrInsufficientFunds.Error())) +} diff --git a/node/chainSimulator/components/memoryComponents.go b/node/chainSimulator/components/memoryComponents.go index 3b12e720756..639dafc753d 100644 --- a/node/chainSimulator/components/memoryComponents.go +++ b/node/chainSimulator/components/memoryComponents.go @@ -9,11 +9,11 @@ import ( // CreateMemUnit creates a new in-memory storage unit func CreateMemUnit() storage.Storer { - capacity := uint32(10) + capacity := uint32(10_000_000) shards := uint32(1) sizeInBytes := uint64(0) cache, _ := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: capacity, Shards: shards, SizeInBytes: sizeInBytes}) - persist, _ := database.NewlruDB(100000) + persist, _ := database.NewlruDB(10_000_000) unit, _ := storageunit.NewStorageUnit(cache, persist) return unit diff --git a/node/chainSimulator/components/nodeFacade.go b/node/chainSimulator/components/nodeFacade.go index 3cc660e3711..0136efefc31 100644 --- a/node/chainSimulator/components/nodeFacade.go +++ b/node/chainSimulator/components/nodeFacade.go @@ -18,7 +18,7 @@ import ( "github.com/multiversx/mx-chain-go/process/mock" ) -func (node *testOnlyProcessingNode) createFacade(configs config.Configs, apiInterface APIConfigurator) error { +func (node *testOnlyProcessingNode) createFacade(configs config.Configs, apiInterface APIConfigurator, vmQueryDelayAfterStartInMs uint64) error { log.Debug("creating api resolver structure") err := node.createMetrics(configs) @@ -39,7 +39,7 @@ func (node *testOnlyProcessingNode) createFacade(configs config.Configs, apiInte allowVMQueriesChan := make(chan struct{}) go func() { - time.Sleep(time.Second) + time.Sleep(time.Duration(vmQueryDelayAfterStartInMs) * time.Millisecond) close(allowVMQueriesChan) node.StatusCoreComponents.AppStatusHandler().SetStringValue(common.MetricAreVMQueriesReady, strconv.FormatBool(true)) }() @@ -176,6 +176,7 @@ func (node *testOnlyProcessingNode) createMetrics(configs config.Configs) error metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricMinGasPrice, node.CoreComponentsHolder.EconomicsData().MinGasPrice()) metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricMinGasLimit, node.CoreComponentsHolder.EconomicsData().MinGasLimit()) metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitGuardedTx, node.CoreComponentsHolder.EconomicsData().ExtraGasLimitGuardedTx()) + metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitRelayedTx, node.CoreComponentsHolder.EconomicsData().MinGasLimit()) metrics.SaveStringMetric(node.StatusCoreComponents.AppStatusHandler(), common.MetricRewardsTopUpGradientPoint, node.CoreComponentsHolder.EconomicsData().RewardsTopUpGradientPoint().String()) metrics.SaveStringMetric(node.StatusCoreComponents.AppStatusHandler(), common.MetricTopUpFactor, fmt.Sprintf("%g", node.CoreComponentsHolder.EconomicsData().RewardsTopUpFactor())) metrics.SaveStringMetric(node.StatusCoreComponents.AppStatusHandler(), common.MetricGasPriceModifier, fmt.Sprintf("%g", node.CoreComponentsHolder.EconomicsData().GasPriceModifier())) diff --git a/node/chainSimulator/components/processComponents.go b/node/chainSimulator/components/processComponents.go index 8a2dd6baf1d..cbd119fa517 100644 --- a/node/chainSimulator/components/processComponents.go +++ b/node/chainSimulator/components/processComponents.go @@ -21,9 +21,8 @@ import ( processComp "github.com/multiversx/mx-chain-go/factory/processing" "github.com/multiversx/mx-chain-go/genesis" "github.com/multiversx/mx-chain-go/genesis/parsing" - nodeDisabled "github.com/multiversx/mx-chain-go/node/disabled" "github.com/multiversx/mx-chain-go/process" - "github.com/multiversx/mx-chain-go/process/interceptors/disabled" + "github.com/multiversx/mx-chain-go/process/interceptors" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/storage/cache" @@ -154,12 +153,25 @@ func CreateProcessComponents(args ArgsProcessComponentsHolder) (*processComponen return nil, err } - whiteListRequest, err := disabled.NewDisabledWhiteListDataVerifier() + lruCacheRequest, err := cache.NewLRUCache(int(args.Config.WhiteListPool.Capacity)) + if err != nil { + return nil, err + + } + whiteListHandler, err := interceptors.NewWhiteListDataVerifier(lruCacheRequest) if err != nil { return nil, err } - whiteListerVerifiedTxs := nodeDisabled.NewDisabledWhiteListDataVerifier() + lruCacheTx, err := cache.NewLRUCache(int(args.Config.WhiteListerVerifiedTxs.Capacity)) + if err != nil { + return nil, err + + } + whiteListVerifiedTxs, err := interceptors.NewWhiteListDataVerifier(lruCacheTx) + if err != nil { + return nil, err + } historyRepository, err := historyRepositoryFactory.Create() if err != nil { @@ -194,8 +206,8 @@ func CreateProcessComponents(args ArgsProcessComponentsHolder) (*processComponen GasSchedule: gasScheduleNotifier, NodesCoordinator: args.NodesCoordinator, RequestedItemsHandler: requestedItemsHandler, - WhiteListHandler: whiteListRequest, - WhiteListerVerifiedTxs: whiteListerVerifiedTxs, + WhiteListHandler: whiteListHandler, + WhiteListerVerifiedTxs: whiteListVerifiedTxs, MaxRating: 50, SystemSCConfig: &args.SystemSCConfig, ImportStartHandler: importStartHandler, diff --git a/node/chainSimulator/components/statusComponents.go b/node/chainSimulator/components/statusComponents.go index fa0027ca967..be094472fc1 100644 --- a/node/chainSimulator/components/statusComponents.go +++ b/node/chainSimulator/components/statusComponents.go @@ -10,6 +10,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/appStatusPolling" "github.com/multiversx/mx-chain-core-go/core/check" factoryMarshalizer "github.com/multiversx/mx-chain-core-go/marshal/factory" + indexerFactory "github.com/multiversx/mx-chain-es-indexer-go/process/factory" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/statistics" "github.com/multiversx/mx-chain-go/config" @@ -34,7 +35,7 @@ type statusComponentsHolder struct { } // CreateStatusComponents will create a new instance of status components holder -func CreateStatusComponents(shardID uint32, appStatusHandler core.AppStatusHandler, statusPollingIntervalSec int, external config.ExternalConfig) (*statusComponentsHolder, error) { +func CreateStatusComponents(shardID uint32, appStatusHandler core.AppStatusHandler, statusPollingIntervalSec int, external config.ExternalConfig, coreComponents process.CoreComponentsHolder) (*statusComponentsHolder, error) { if check.IfNil(appStatusHandler) { return nil, core.ErrNilAppStatusHandler } @@ -51,11 +52,12 @@ func CreateStatusComponents(shardID uint32, appStatusHandler core.AppStatusHandl return nil, err } instance.outportHandler, err = factory.CreateOutport(&factory.OutportFactoryArgs{ - IsImportDB: false, - ShardID: shardID, - RetrialInterval: time.Second, - HostDriversArgs: hostDriverArgs, - EventNotifierFactoryArgs: &factory.EventNotifierFactoryArgs{}, + IsImportDB: false, + ShardID: shardID, + RetrialInterval: time.Second, + HostDriversArgs: hostDriverArgs, + EventNotifierFactoryArgs: &factory.EventNotifierFactoryArgs{}, + ElasticIndexerFactoryArgs: makeElasticIndexerArgs(external, coreComponents), }) if err != nil { return nil, err @@ -90,6 +92,26 @@ func makeHostDriversArgs(external config.ExternalConfig) ([]factory.ArgsHostDriv return argsHostDriverFactorySlice, nil } +func makeElasticIndexerArgs(external config.ExternalConfig, coreComponents process.CoreComponentsHolder) indexerFactory.ArgsIndexerFactory { + elasticSearchConfig := external.ElasticSearchConnector + return indexerFactory.ArgsIndexerFactory{ + Enabled: elasticSearchConfig.Enabled, + BulkRequestMaxSize: elasticSearchConfig.BulkRequestMaxSizeInBytes, + Url: elasticSearchConfig.URL, + UserName: elasticSearchConfig.Username, + Password: elasticSearchConfig.Password, + Marshalizer: coreComponents.InternalMarshalizer(), + Hasher: coreComponents.Hasher(), + AddressPubkeyConverter: coreComponents.AddressPubKeyConverter(), + ValidatorPubkeyConverter: coreComponents.ValidatorPubKeyConverter(), + EnabledIndexes: elasticSearchConfig.EnabledIndexes, + Denomination: 18, + UseKibana: elasticSearchConfig.UseKibana, + ImportDB: false, + HeaderMarshaller: coreComponents.InternalMarshalizer(), + } +} + // OutportHandler will return the outport handler func (s *statusComponentsHolder) OutportHandler() outport.OutportHandler { return s.outportHandler diff --git a/node/chainSimulator/components/statusComponents_test.go b/node/chainSimulator/components/statusComponents_test.go index b6e2e296fbb..24f3b4595c1 100644 --- a/node/chainSimulator/components/statusComponents_test.go +++ b/node/chainSimulator/components/statusComponents_test.go @@ -21,7 +21,7 @@ func TestCreateStatusComponents(t *testing.T) { t.Run("should work", func(t *testing.T) { t.Parallel() - comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.NoError(t, err) require.NotNil(t, comp) @@ -31,7 +31,7 @@ func TestCreateStatusComponents(t *testing.T) { t.Run("nil app status handler should error", func(t *testing.T) { t.Parallel() - comp, err := CreateStatusComponents(0, nil, 5, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, nil, 5, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.Equal(t, core.ErrNilAppStatusHandler, err) require.Nil(t, comp) }) @@ -43,7 +43,7 @@ func TestStatusComponentsHolder_IsInterfaceNil(t *testing.T) { var comp *statusComponentsHolder require.True(t, comp.IsInterfaceNil()) - comp, _ = CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}) + comp, _ = CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.False(t, comp.IsInterfaceNil()) require.Nil(t, comp.Close()) } @@ -51,7 +51,7 @@ func TestStatusComponentsHolder_IsInterfaceNil(t *testing.T) { func TestStatusComponentsHolder_Getters(t *testing.T) { t.Parallel() - comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.NoError(t, err) require.NotNil(t, comp.OutportHandler()) @@ -65,7 +65,7 @@ func TestStatusComponentsHolder_Getters(t *testing.T) { func TestStatusComponentsHolder_SetForkDetector(t *testing.T) { t.Parallel() - comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.NoError(t, err) err = comp.SetForkDetector(nil) @@ -83,7 +83,7 @@ func TestStatusComponentsHolder_StartPolling(t *testing.T) { t.Run("nil fork detector should error", func(t *testing.T) { t.Parallel() - comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 5, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.NoError(t, err) err = comp.StartPolling() @@ -92,7 +92,7 @@ func TestStatusComponentsHolder_StartPolling(t *testing.T) { t.Run("NewAppStatusPolling failure should error", func(t *testing.T) { t.Parallel() - comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 0, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, &statusHandler.AppStatusHandlerStub{}, 0, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.NoError(t, err) err = comp.SetForkDetector(&mock.ForkDetectorStub{}) @@ -114,7 +114,7 @@ func TestStatusComponentsHolder_StartPolling(t *testing.T) { wasSetUInt64ValueCalled.SetValue(true) }, } - comp, err := CreateStatusComponents(0, appStatusHandler, providedStatusPollingIntervalSec, config.ExternalConfig{}) + comp, err := CreateStatusComponents(0, appStatusHandler, providedStatusPollingIntervalSec, config.ExternalConfig{}, &mock.CoreComponentsStub{}) require.NoError(t, err) forkDetector := &mock.ForkDetectorStub{ diff --git a/node/chainSimulator/components/syncedMessenger.go b/node/chainSimulator/components/syncedMessenger.go index cc437d02038..09786c45842 100644 --- a/node/chainSimulator/components/syncedMessenger.go +++ b/node/chainSimulator/components/syncedMessenger.go @@ -80,13 +80,21 @@ func (messenger *syncedMessenger) receive(fromConnectedPeer core.PeerID, message handlers := messenger.topics[message.Topic()] messenger.mutOperation.RUnlock() + wg := &sync.WaitGroup{} + wg.Add(len(handlers)) for _, handler := range handlers { - err := handler.ProcessReceivedMessage(message, fromConnectedPeer, messenger) - if err != nil { - log.Trace("received message syncedMessenger", - "error", err, "topic", message.Topic(), "from connected peer", fromConnectedPeer.Pretty()) - } + // this is needed to process all received messages on multiple go routines + go func(proc p2p.MessageProcessor, p2pMessage p2p.MessageP2P, peer core.PeerID, localWG *sync.WaitGroup) { + err := proc.ProcessReceivedMessage(p2pMessage, peer, messenger) + if err != nil { + log.Trace("received message syncedMessenger", "error", err, "topic", p2pMessage.Topic(), "from connected peer", peer.Pretty()) + } + + localWG.Done() + }(handler, message, fromConnectedPeer, wg) } + + wg.Wait() } // ProcessReceivedMessage does nothing and returns nil diff --git a/node/chainSimulator/components/testOnlyProcessingNode.go b/node/chainSimulator/components/testOnlyProcessingNode.go index 6c799b203a6..85e41f37189 100644 --- a/node/chainSimulator/components/testOnlyProcessingNode.go +++ b/node/chainSimulator/components/testOnlyProcessingNode.go @@ -49,6 +49,7 @@ type ArgsTestOnlyProcessingNode struct { MinNodesMeta uint32 MetaChainConsensusGroupSize uint32 RoundDurationInMillis uint64 + VmQueryDelayAfterStartInMs uint64 } type testOnlyProcessingNode struct { @@ -152,6 +153,7 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces instance.StatusCoreComponents.AppStatusHandler(), args.Configs.GeneralConfig.GeneralSettings.StatusPollingIntervalSec, *args.Configs.ExternalConfig, + instance.CoreComponentsHolder, ) if err != nil { return nil, err @@ -173,10 +175,17 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces return nil, err } - err = instance.createDataPool(args) + instance.DataPool, err = dataRetrieverFactory.NewDataPoolFromConfig(dataRetrieverFactory.ArgsDataPool{ + Config: args.Configs.GeneralConfig, + EconomicsData: instance.CoreComponentsHolder.EconomicsData(), + ShardCoordinator: instance.BootstrapComponentsHolder.ShardCoordinator(), + Marshalizer: instance.CoreComponentsHolder.InternalMarshalizer(), + PathManager: instance.CoreComponentsHolder.PathHandler(), + }) if err != nil { return nil, err } + err = instance.createNodesCoordinator(args.Configs.PreferencesConfig.Preferences, *args.Configs.GeneralConfig) if err != nil { return nil, err @@ -233,7 +242,7 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces return nil, err } - err = instance.createFacade(args.Configs, args.APIInterface) + err = instance.createFacade(args.Configs, args.APIInterface, args.VmQueryDelayAfterStartInMs) if err != nil { return nil, err } @@ -259,22 +268,6 @@ func (node *testOnlyProcessingNode) createBlockChain(selfShardID uint32) error { return err } -func (node *testOnlyProcessingNode) createDataPool(args ArgsTestOnlyProcessingNode) error { - var err error - - argsDataPool := dataRetrieverFactory.ArgsDataPool{ - Config: args.Configs.GeneralConfig, - EconomicsData: node.CoreComponentsHolder.EconomicsData(), - ShardCoordinator: node.BootstrapComponentsHolder.ShardCoordinator(), - Marshalizer: node.CoreComponentsHolder.InternalMarshalizer(), - PathManager: node.CoreComponentsHolder.PathHandler(), - } - - node.DataPool, err = dataRetrieverFactory.NewDataPoolFromConfig(argsDataPool) - - return err -} - func (node *testOnlyProcessingNode) createNodesCoordinator(pref config.PreferencesConfig, generalConfig config.Config) error { nodesShufflerOut, err := bootstrapComp.CreateNodesShuffleOut( node.CoreComponentsHolder.GenesisNodesSetup(), diff --git a/node/chainSimulator/configs/configs.go b/node/chainSimulator/configs/configs.go index 718329381e3..335064ba01e 100644 --- a/node/chainSimulator/configs/configs.go +++ b/node/chainSimulator/configs/configs.go @@ -134,12 +134,14 @@ func CreateChainSimulatorConfigs(args ArgsChainSimulatorConfigs) (*ArgsConfigsSi return nil, err } - configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[0].ShardMinNumNodes = args.MinNodesPerShard - configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[0].MetachainMinNumNodes = args.MetaChainMinNodes - configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[0].MetachainConsensusGroupSize = args.MetaChainConsensusGroupSize - configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[0].ShardConsensusGroupSize = args.ConsensusGroupSize - configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[0].RoundDuration = args.RoundDurationInMillis - configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[0].Hysteresis = args.Hysteresis + for idx := 0; idx < len(configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch); idx++ { + configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[idx].ShardMinNumNodes = args.MinNodesPerShard + configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[idx].MetachainMinNumNodes = args.MetaChainMinNodes + configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[idx].MetachainConsensusGroupSize = args.MetaChainConsensusGroupSize + configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[idx].ShardConsensusGroupSize = args.ConsensusGroupSize + configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[idx].RoundDuration = args.RoundDurationInMillis + configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[idx].Hysteresis = args.Hysteresis + } // TODO[Sorin]: remove this once all equivalent messages PRs are merged configs.EpochConfig.EnableEpochs.EquivalentMessagesEnableEpoch = integrationTests.UnreachableEpoch diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 63bfe673f37..df637f338d6 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -125,6 +125,18 @@ func (bap *baseAPIBlockProcessor) getAndAttachTxsToMb( firstProcessed := mbHeader.GetIndexOfFirstTxProcessed() lastProcessed := mbHeader.GetIndexOfLastTxProcessed() + + // When options.ForHyperblock is true, there are two scenarios: + // 1 - If not all transactions were executed, no transactions will be returned. + // 2 - If all transactions were executed, all transactions starting from index 0 will be returned. + if options.ForHyperblock { + allTxsWereExecuted := lastProcessed == int32(len(miniBlock.TxHashes)-1) + if !allTxsWereExecuted { + return nil + } + firstProcessed = 0 + } + return bap.getAndAttachTxsToMbByEpoch(miniblockHash, miniBlock, header, apiMiniblock, firstProcessed, lastProcessed, options) } diff --git a/node/external/dtos.go b/node/external/dtos.go index f884d8d32c9..b1789054f5e 100644 --- a/node/external/dtos.go +++ b/node/external/dtos.go @@ -2,19 +2,21 @@ package external // ArgsCreateTransaction defines arguments for creating a transaction type ArgsCreateTransaction struct { - Nonce uint64 - Value string - Receiver string - ReceiverUsername []byte - Sender string - SenderUsername []byte - GasPrice uint64 - GasLimit uint64 - DataField []byte - SignatureHex string - ChainID string - Version uint32 - Options uint32 - Guardian string - GuardianSigHex string + Nonce uint64 + Value string + Receiver string + ReceiverUsername []byte + Sender string + SenderUsername []byte + GasPrice uint64 + GasLimit uint64 + DataField []byte + SignatureHex string + ChainID string + Version uint32 + Options uint32 + Guardian string + GuardianSigHex string + Relayer string + RelayerSignatureHex string } diff --git a/node/external/interface.go b/node/external/interface.go index a12ef177ce1..e70367e201d 100644 --- a/node/external/interface.go +++ b/node/external/interface.go @@ -61,6 +61,7 @@ type DelegatedListHandler interface { // APITransactionHandler defines what an API transaction handler should be able to do type APITransactionHandler interface { GetTransaction(txHash string, withResults bool) (*transaction.ApiTransactionResult, error) + GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) GetTransactionsPool(fields string) (*common.TransactionsPoolAPIResponse, error) GetTransactionsPoolForSender(sender, fields string) (*common.TransactionsPoolForSenderApiResponse, error) GetLastPoolNonceForSender(sender string) (uint64, error) diff --git a/node/external/nodeApiResolver.go b/node/external/nodeApiResolver.go index 0ae0356f4f7..7f1bd2269b4 100644 --- a/node/external/nodeApiResolver.go +++ b/node/external/nodeApiResolver.go @@ -189,6 +189,11 @@ func (nar *nodeApiResolver) GetTransaction(hash string, withResults bool) (*tran return nar.apiTransactionHandler.GetTransaction(hash, withResults) } +// GetSCRsByTxHash will return a list of smart contract results based on a provided tx hash and smart contract result hash +func (nar *nodeApiResolver) GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) { + return nar.apiTransactionHandler.GetSCRsByTxHash(txHash, scrHash) +} + // GetTransactionsPool will return a structure containing the transactions pool that is to be returned on API calls func (nar *nodeApiResolver) GetTransactionsPool(fields string) (*common.TransactionsPoolAPIResponse, error) { return nar.apiTransactionHandler.GetTransactionsPool(fields) diff --git a/node/external/transactionAPI/apiTransactionArgs.go b/node/external/transactionAPI/apiTransactionArgs.go index bb1aa10a659..1e4099390fd 100644 --- a/node/external/transactionAPI/apiTransactionArgs.go +++ b/node/external/transactionAPI/apiTransactionArgs.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/typeConverters" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dblookupext" "github.com/multiversx/mx-chain-go/process" @@ -27,4 +28,6 @@ type ArgAPITransactionProcessor struct { TxTypeHandler process.TxTypeHandler LogsFacade LogsFacade DataFieldParser DataFieldParser + TxMarshaller marshal.Marshalizer + EnableEpochsHandler common.EnableEpochsHandler } diff --git a/node/external/transactionAPI/apiTransactionProcessor.go b/node/external/transactionAPI/apiTransactionProcessor.go index b12aa9ac86f..f15a096ea5b 100644 --- a/node/external/transactionAPI/apiTransactionProcessor.go +++ b/node/external/transactionAPI/apiTransactionProcessor.go @@ -2,6 +2,7 @@ package transactionAPI import ( "encoding/hex" + "errors" "fmt" "sort" "strings" @@ -19,6 +20,7 @@ import ( "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dblookupext" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/smartContract" "github.com/multiversx/mx-chain-go/process/txstatus" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/storage/txcache" @@ -43,6 +45,7 @@ type apiTransactionProcessor struct { transactionResultsProcessor *apiTransactionResultsProcessor refundDetector *refundDetector gasUsedAndFeeProcessor *gasUsedAndFeeProcessor + enableEpochsHandler common.EnableEpochsHandler } // NewAPITransactionProcessor will create a new instance of apiTransactionProcessor @@ -65,7 +68,13 @@ func NewAPITransactionProcessor(args *ArgAPITransactionProcessor) (*apiTransacti ) refundDetectorInstance := NewRefundDetector() - gasUsedAndFeeProc := newGasUsedAndFeeProcessor(args.FeeComputer, args.AddressPubKeyConverter) + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + args.FeeComputer, + args.AddressPubKeyConverter, + smartContract.NewArgumentParser(), + args.TxMarshaller, + args.EnableEpochsHandler, + ) return &apiTransactionProcessor{ roundDuration: args.RoundDuration, @@ -83,9 +92,53 @@ func NewAPITransactionProcessor(args *ArgAPITransactionProcessor) (*apiTransacti transactionResultsProcessor: txResultsProc, refundDetector: refundDetectorInstance, gasUsedAndFeeProcessor: gasUsedAndFeeProc, + enableEpochsHandler: args.EnableEpochsHandler, }, nil } +// GetSCRsByTxHash will return a list of smart contract results based on a provided tx hash and smart contract result hash +func (atp *apiTransactionProcessor) GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) { + decodedScrHash, err := hex.DecodeString(scrHash) + if err != nil { + return nil, err + } + + decodedTxHash, err := hex.DecodeString(txHash) + if err != nil { + return nil, err + } + + if !atp.historyRepository.IsEnabled() { + return nil, fmt.Errorf("cannot return smat contract results: %w", ErrDBLookExtensionIsNotEnabled) + } + + miniblockMetadata, err := atp.historyRepository.GetMiniblockMetadataByTxHash(decodedScrHash) + if err != nil { + return nil, fmt.Errorf("%s: %w", ErrTransactionNotFound.Error(), err) + } + + resultsHashes, err := atp.historyRepository.GetResultsHashesByTxHash(decodedTxHash, miniblockMetadata.Epoch) + if err != nil { + // It's perfectly normal to have transactions without SCRs. + if errors.Is(err, dblookupext.ErrNotFoundInStorage) { + return []*transaction.ApiSmartContractResult{}, nil + } + return nil, err + } + + scrsAPI := make([]*transaction.ApiSmartContractResult, 0, len(resultsHashes.ScResultsHashesAndEpoch)) + for _, scrHashesEpoch := range resultsHashes.ScResultsHashesAndEpoch { + scrs, errGet := atp.transactionResultsProcessor.getSmartContractResultsInTransactionByHashesAndEpoch(scrHashesEpoch.ScResultsHashes, scrHashesEpoch.Epoch) + if errGet != nil { + return nil, errGet + } + + scrsAPI = append(scrsAPI, scrs...) + } + + return scrsAPI, nil +} + // GetTransaction gets the transaction based on the given hash. It will search in the cache and the storage and // will return the transaction in a format which can be respected by all types of transactions (normal, reward or unsigned) func (atp *apiTransactionProcessor) GetTransaction(txHash string, withResults bool) (*transaction.ApiTransactionResult, error) { @@ -130,7 +183,7 @@ func (atp *apiTransactionProcessor) PopulateComputedFields(tx *transaction.ApiTr } func (atp *apiTransactionProcessor) populateComputedFieldsProcessingType(tx *transaction.ApiTransactionResult) { - typeOnSource, typeOnDestination := atp.txTypeHandler.ComputeTransactionType(tx.Tx) + typeOnSource, typeOnDestination, _ := atp.txTypeHandler.ComputeTransactionType(tx.Tx) tx.ProcessingTypeOnSource = typeOnSource.String() tx.ProcessingTypeOnDestination = typeOnDestination.String() } @@ -144,6 +197,13 @@ func (atp *apiTransactionProcessor) populateComputedFieldInitiallyPaidFee(tx *tr fee := atp.feeComputer.ComputeTransactionFee(tx) // For user-initiated transactions, we can assume the fee is always strictly positive (note: BigInt(0) is stringified as ""). tx.InitiallyPaidFee = fee.String() + + isFeeFixActive := atp.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, tx.Epoch) + _, fee, isRelayedV1V2 := atp.gasUsedAndFeeProcessor.getFeeOfRelayedV1V2(tx) + isRelayedAfterFix := tx.IsRelayed && isFeeFixActive && isRelayedV1V2 + if isRelayedAfterFix { + tx.InitiallyPaidFee = fee.String() + } } func (atp *apiTransactionProcessor) populateComputedFieldIsRefund(tx *transaction.ApiTransactionResult) { @@ -334,15 +394,25 @@ func (atp *apiTransactionProcessor) getFieldGettersForTx(wrappedTx *txcache.Wrap rcvUsernameField: wrappedTx.Tx.GetRcvUserName(), dataField: wrappedTx.Tx.GetData(), valueField: getTxValue(wrappedTx), - senderShardID: wrappedTx.SenderShardID, - receiverShardID: wrappedTx.ReceiverShardID, + senderShardID: atp.shardCoordinator.ComputeId(wrappedTx.Tx.GetSndAddr()), + receiverShardID: atp.shardCoordinator.ComputeId(wrappedTx.Tx.GetRcvAddr()), } guardedTx, isGuardedTx := wrappedTx.Tx.(data.GuardedTransactionHandler) - if isGuardedTx { + if isGuardedTx && len(guardedTx.GetGuardianAddr()) > 0 { fieldGetters[signatureField] = hex.EncodeToString(guardedTx.GetSignature()) - fieldGetters[guardianField] = atp.addressPubKeyConverter.SilentEncode(guardedTx.GetGuardianAddr(), log) - fieldGetters[guardianSignatureField] = hex.EncodeToString(guardedTx.GetGuardianSignature()) + + if len(guardedTx.GetGuardianAddr()) > 0 { + fieldGetters[guardianField] = atp.addressPubKeyConverter.SilentEncode(guardedTx.GetGuardianAddr(), log) + fieldGetters[guardianSignatureField] = hex.EncodeToString(guardedTx.GetGuardianSignature()) + } + } + + relayedTx, ok := wrappedTx.Tx.(data.RelayedTransactionHandler) + if ok && len(relayedTx.GetRelayerAddr()) > 0 { + fieldGetters[signatureField] = hex.EncodeToString(relayedTx.GetSignature()) + fieldGetters[relayerField] = atp.addressPubKeyConverter.SilentEncode(relayedTx.GetRelayerAddr(), log) + fieldGetters[relayerSignatureField] = hex.EncodeToString(relayedTx.GetRelayerSignature()) } return fieldGetters diff --git a/node/external/transactionAPI/apiTransactionProcessor_test.go b/node/external/transactionAPI/apiTransactionProcessor_test.go index 7d86a1610c5..d7bd038bd7f 100644 --- a/node/external/transactionAPI/apiTransactionProcessor_test.go +++ b/node/external/transactionAPI/apiTransactionProcessor_test.go @@ -32,7 +32,9 @@ import ( "github.com/multiversx/mx-chain-go/testscommon" dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" dblookupextMock "github.com/multiversx/mx-chain-go/testscommon/dblookupext" + "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" + "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" datafield "github.com/multiversx/mx-chain-vm-common-go/parsers/dataField" @@ -59,6 +61,8 @@ func createMockArgAPITransactionProcessor() *ArgAPITransactionProcessor { return &datafield.ResponseParseData{} }, }, + TxMarshaller: &marshallerMock.MarshalizerMock{}, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), } } @@ -181,6 +185,24 @@ func TestNewAPITransactionProcessor(t *testing.T) { _, err := NewAPITransactionProcessor(arguments) require.Equal(t, ErrNilDataFieldParser, err) }) + t.Run("NilTxMarshaller", func(t *testing.T) { + t.Parallel() + + arguments := createMockArgAPITransactionProcessor() + arguments.TxMarshaller = nil + + _, err := NewAPITransactionProcessor(arguments) + require.True(t, strings.Contains(err.Error(), process.ErrNilMarshalizer.Error())) + }) + t.Run("NilEnableEpochsHandler", func(t *testing.T) { + t.Parallel() + + arguments := createMockArgAPITransactionProcessor() + arguments.EnableEpochsHandler = nil + + _, err := NewAPITransactionProcessor(arguments) + require.Equal(t, process.ErrNilEnableEpochsHandler, err) + }) } func TestNode_GetTransactionInvalidHashShouldErr(t *testing.T) { @@ -268,6 +290,95 @@ func TestNode_GetTransactionFromPool(t *testing.T) { require.Equal(t, transaction.TxStatusPending, actualG.Status) } +func TestNode_GetSCRs(t *testing.T) { + scResultHash := []byte("scHash") + txHash := []byte("txHash") + + marshalizer := &mock.MarshalizerFake{} + scResult := &smartContractResult.SmartContractResult{ + Nonce: 1, + SndAddr: []byte("snd"), + RcvAddr: []byte("rcv"), + OriginalTxHash: txHash, + Data: []byte("test"), + } + + resultHashesByTxHash := &dblookupext.ResultsHashesByTxHash{ + ScResultsHashesAndEpoch: []*dblookupext.ScResultsHashesAndEpoch{ + { + Epoch: 0, + ScResultsHashes: [][]byte{scResultHash}, + }, + }, + } + + chainStorer := &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + switch unitType { + case dataRetriever.UnsignedTransactionUnit: + return &storageStubs.StorerStub{ + GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { + return marshalizer.Marshal(scResult) + }, + }, nil + default: + return nil, storage.ErrKeyNotFound + } + }, + } + + historyRepo := &dblookupextMock.HistoryRepositoryStub{ + GetMiniblockMetadataByTxHashCalled: func(hash []byte) (*dblookupext.MiniblockMetadata, error) { + return &dblookupext.MiniblockMetadata{}, nil + }, + GetEventsHashesByTxHashCalled: func(hash []byte, epoch uint32) (*dblookupext.ResultsHashesByTxHash, error) { + return resultHashesByTxHash, nil + }, + } + + feeComp := &testscommon.FeeComputerStub{ + ComputeTransactionFeeCalled: func(tx *transaction.ApiTransactionResult) *big.Int { + return big.NewInt(1000) + }, + } + + args := &ArgAPITransactionProcessor{ + RoundDuration: 0, + GenesisTime: time.Time{}, + Marshalizer: &mock.MarshalizerFake{}, + AddressPubKeyConverter: &testscommon.PubkeyConverterMock{}, + ShardCoordinator: &mock.ShardCoordinatorMock{}, + HistoryRepository: historyRepo, + StorageService: chainStorer, + DataPool: dataRetrieverMock.NewPoolsHolderMock(), + Uint64ByteSliceConverter: mock.NewNonceHashConverterMock(), + FeeComputer: feeComp, + TxTypeHandler: &testscommon.TxTypeHandlerMock{}, + LogsFacade: &testscommon.LogsFacadeStub{}, + DataFieldParser: &testscommon.DataFieldParserStub{ + ParseCalled: func(dataField []byte, sender, receiver []byte, _ uint32) *datafield.ResponseParseData { + return &datafield.ResponseParseData{} + }, + }, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + TxMarshaller: &mock.MarshalizerFake{}, + } + apiTransactionProc, _ := NewAPITransactionProcessor(args) + + scrs, err := apiTransactionProc.GetSCRsByTxHash(hex.EncodeToString(txHash), hex.EncodeToString(scResultHash)) + require.Nil(t, err) + require.Equal(t, 1, len(scrs)) + require.Equal(t, &transaction.ApiSmartContractResult{ + Nonce: 1, + Data: "test", + Hash: "736348617368", + RcvAddr: "726376", + SndAddr: "736e64", + OriginalTxHash: "747848617368", + Receivers: []string{}, + }, scrs[0]) +} + func TestNode_GetTransactionFromStorage(t *testing.T) { t.Parallel() @@ -459,6 +570,8 @@ func TestNode_GetTransactionWithResultsFromStorage(t *testing.T) { return &datafield.ResponseParseData{} }, }, + TxMarshaller: &marshallerMock.MarshalizerMock{}, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), } apiTransactionProc, _ := NewAPITransactionProcessor(args) @@ -763,34 +876,37 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { txHash0, txHash1, txHash2 := []byte("txHash0"), []byte("txHash1"), []byte("txHash2") sender := "alice" - txCacheIntraShard, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + txCacheIntraShard, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) + + require.NoError(t, err) + txCacheIntraShard.AddTx(createTx(txHash2, sender, 3)) txCacheIntraShard.AddTx(createTx(txHash0, sender, 1)) txCacheIntraShard.AddTx(createTx(txHash1, sender, 2)) txHash3, txHash4 := []byte("txHash3"), []byte("txHash4") - txCacheWithMeta, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test-meta", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + txCacheWithMeta, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test-meta", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) txCacheWithMeta.AddTx(createTx(txHash3, sender, 4)) txCacheWithMeta.AddTx(createTx(txHash4, sender, 5)) + require.NoError(t, err) + args := createMockArgAPITransactionProcessor() args.DataPool = &dataRetrieverMock.PoolsHolderStub{ TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { @@ -820,6 +936,9 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { NumberOfShardsCalled: func() uint32 { return 1 }, + ComputeIdCalled: func(address []byte) uint32 { + return 1 // force to return different from 0 + }, } atp, err := NewAPITransactionProcessor(args) require.NoError(t, err) @@ -832,7 +951,17 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { for i, tx := range res.Transactions { require.Equal(t, expectedHashes[i], tx.TxFields[hashField]) require.Equal(t, expectedValues[i], tx.TxFields[valueField]) - require.Equal(t, sender, tx.TxFields["sender"]) + require.Equal(t, sender, tx.TxFields[senderField]) + require.Equal(t, uint32(1), tx.TxFields[senderShardID]) + require.Equal(t, uint32(1), tx.TxFields[senderShardID]) + } + + res, err = atp.GetTransactionsPoolForSender(sender, "sender,value") // no hash, should be by default + require.NoError(t, err) + for i, tx := range res.Transactions { + require.Equal(t, expectedHashes[i], tx.TxFields[hashField]) + require.Equal(t, expectedValues[i], tx.TxFields[valueField]) + require.Equal(t, sender, tx.TxFields[senderField]) } // if no tx is found in pool for a sender, it isn't an error, but return empty slice @@ -851,15 +980,14 @@ func TestApiTransactionProcessor_GetLastPoolNonceForSender(t *testing.T) { sender := "alice" lastNonce := uint64(10) txCacheIntraShard, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + Name: "test", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) txCacheIntraShard.AddTx(createTx(txHash2, sender, 3)) txCacheIntraShard.AddTx(createTx(txHash0, sender, 1)) txCacheIntraShard.AddTx(createTx(txHash1, sender, 2)) @@ -903,27 +1031,29 @@ func TestApiTransactionProcessor_GetTransactionsPoolNonceGapsForSender(t *testin txHash1, txHash2, txHash3, txHash4 := []byte("txHash1"), []byte("txHash2"), []byte("txHash3"), []byte("txHash4") sender := "alice" - txCacheIntraShard, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + txCacheIntraShard, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) - txCacheWithMeta, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test-meta", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + require.NoError(t, err) + + txCacheWithMeta, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test-meta", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) + + require.NoError(t, err) accountNonce := uint64(20) // expected nonce gaps: 21-31, 33-33, 36-38 @@ -1027,6 +1157,8 @@ func createAPITransactionProc(t *testing.T, epoch uint32, withDbLookupExt bool) TxTypeHandler: &testscommon.TxTypeHandlerMock{}, LogsFacade: &testscommon.LogsFacadeStub{}, DataFieldParser: dataFieldParser, + TxMarshaller: &marshallerMock.MarshalizerMock{}, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), } apiTransactionProc, err := NewAPITransactionProcessor(args) require.Nil(t, err) @@ -1188,8 +1320,8 @@ func TestApiTransactionProcessor_GetTransactionPopulatesComputedFields(t *testin }) t.Run("ProcessingType", func(t *testing.T) { - txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCDeployment + txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCDeployment, false } dataPool.Transactions().AddData([]byte{0, 2}, &transaction.Transaction{Nonce: 7, SndAddr: []byte("alice"), RcvAddr: []byte("bob")}, 42, "1") @@ -1200,10 +1332,23 @@ func TestApiTransactionProcessor_GetTransactionPopulatesComputedFields(t *testin require.Equal(t, process.SCDeployment.String(), tx.ProcessingTypeOnDestination) }) + t.Run("ProcessingType (with relayed v3)", func(t *testing.T) { + txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCDeployment, true + } + + dataPool.Transactions().AddData([]byte{0, 3}, &transaction.Transaction{Nonce: 7, SndAddr: []byte("alice"), RcvAddr: []byte("bob")}, 42, "1") + tx, err := processor.GetTransaction("0003", true) + + require.Nil(t, err) + require.Equal(t, process.MoveBalance.String(), tx.ProcessingTypeOnSource) + require.Equal(t, process.SCDeployment.String(), tx.ProcessingTypeOnDestination) + }) + t.Run("IsRefund (false)", func(t *testing.T) { scr := &smartContractResult.SmartContractResult{GasLimit: 0, Data: []byte("@ok"), Value: big.NewInt(0)} - dataPool.UnsignedTransactions().AddData([]byte{0, 3}, scr, 42, "foo") - tx, err := processor.GetTransaction("0003", true) + dataPool.UnsignedTransactions().AddData([]byte{0, 4}, scr, 42, "foo") + tx, err := processor.GetTransaction("0004", true) require.Nil(t, err) require.Equal(t, false, tx.IsRefund) @@ -1211,8 +1356,8 @@ func TestApiTransactionProcessor_GetTransactionPopulatesComputedFields(t *testin t.Run("IsRefund (true)", func(t *testing.T) { scr := &smartContractResult.SmartContractResult{GasLimit: 0, Data: []byte("@6f6b"), Value: big.NewInt(500)} - dataPool.UnsignedTransactions().AddData([]byte{0, 4}, scr, 42, "foo") - tx, err := processor.GetTransaction("0004", true) + dataPool.UnsignedTransactions().AddData([]byte{0, 5}, scr, 42, "foo") + tx, err := processor.GetTransaction("0005", true) require.Nil(t, err) require.Equal(t, true, tx.IsRefund) @@ -1232,8 +1377,8 @@ func TestApiTransactionProcessor_PopulateComputedFields(t *testing.T) { require.Nil(t, err) require.NotNil(t, processor) - txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCDeployment + txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCDeployment, false } feeComputer.ComputeTransactionFeeCalled = func(tx *transaction.ApiTransactionResult) *big.Int { diff --git a/node/external/transactionAPI/apiTransactionResults.go b/node/external/transactionAPI/apiTransactionResults.go index 125376f39da..d4a89edfd15 100644 --- a/node/external/transactionAPI/apiTransactionResults.go +++ b/node/external/transactionAPI/apiTransactionResults.go @@ -102,10 +102,12 @@ func (arp *apiTransactionResultsProcessor) putSmartContractResultsInTransaction( scrHashesEpoch []*dblookupext.ScResultsHashesAndEpoch, ) error { for _, scrHashesE := range scrHashesEpoch { - err := arp.putSmartContractResultsInTransactionByHashesAndEpoch(tx, scrHashesE.ScResultsHashes, scrHashesE.Epoch) + scrsAPI, err := arp.getSmartContractResultsInTransactionByHashesAndEpoch(scrHashesE.ScResultsHashes, scrHashesE.Epoch) if err != nil { return err } + + tx.SmartContractResults = append(tx.SmartContractResults, scrsAPI...) } statusFilters := filters.NewStatusFilters(arp.shardCoordinator.SelfId()) @@ -113,21 +115,22 @@ func (arp *apiTransactionResultsProcessor) putSmartContractResultsInTransaction( return nil } -func (arp *apiTransactionResultsProcessor) putSmartContractResultsInTransactionByHashesAndEpoch(tx *transaction.ApiTransactionResult, scrsHashes [][]byte, epoch uint32) error { +func (arp *apiTransactionResultsProcessor) getSmartContractResultsInTransactionByHashesAndEpoch(scrsHashes [][]byte, epoch uint32) ([]*transaction.ApiSmartContractResult, error) { + scrsAPI := make([]*transaction.ApiSmartContractResult, 0, len(scrsHashes)) for _, scrHash := range scrsHashes { scr, err := arp.getScrFromStorage(scrHash, epoch) if err != nil { - return fmt.Errorf("%w: %v, hash = %s", errCannotLoadContractResults, err, hex.EncodeToString(scrHash)) + return nil, fmt.Errorf("%w: %v, hash = %s", errCannotLoadContractResults, err, hex.EncodeToString(scrHash)) } scrAPI := arp.adaptSmartContractResult(scrHash, scr) arp.loadLogsIntoContractResults(scrHash, epoch, scrAPI) - tx.SmartContractResults = append(tx.SmartContractResults, scrAPI) + scrsAPI = append(scrsAPI, scrAPI) } - return nil + return scrsAPI, nil } func (arp *apiTransactionResultsProcessor) loadLogsIntoTransaction(hash []byte, tx *transaction.ApiTransactionResult, epoch uint32) { diff --git a/node/external/transactionAPI/check.go b/node/external/transactionAPI/check.go index 0959ba6c5db..012aae77618 100644 --- a/node/external/transactionAPI/check.go +++ b/node/external/transactionAPI/check.go @@ -1,6 +1,8 @@ package transactionAPI import ( + "fmt" + "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-go/process" ) @@ -42,6 +44,12 @@ func checkNilArgs(arg *ArgAPITransactionProcessor) error { if check.IfNilReflect(arg.DataFieldParser) { return ErrNilDataFieldParser } + if check.IfNil(arg.TxMarshaller) { + return fmt.Errorf("%w for tx marshaller", process.ErrNilMarshalizer) + } + if check.IfNil(arg.EnableEpochsHandler) { + return process.ErrNilEnableEpochsHandler + } return nil } diff --git a/node/external/transactionAPI/errors.go b/node/external/transactionAPI/errors.go index 924bd6040a5..105d6c3e930 100644 --- a/node/external/transactionAPI/errors.go +++ b/node/external/transactionAPI/errors.go @@ -31,3 +31,6 @@ var ErrCannotRetrieveTransactions = errors.New("transactions cannot be retrieved // ErrInvalidAddress signals that the address is invalid var ErrInvalidAddress = errors.New("invalid address") + +// ErrDBLookExtensionIsNotEnabled signals that the db look extension is not enabled +var ErrDBLookExtensionIsNotEnabled = errors.New("db look extension is not enabled") diff --git a/node/external/transactionAPI/fieldsHandler.go b/node/external/transactionAPI/fieldsHandler.go index 4f837968cb7..f631de38a82 100644 --- a/node/external/transactionAPI/fieldsHandler.go +++ b/node/external/transactionAPI/fieldsHandler.go @@ -19,6 +19,8 @@ const ( guardianSignatureField = "guardiansignature" senderShardID = "sendershard" receiverShardID = "receivershard" + relayerField = "relayer" + relayerSignatureField = "relayersignature" wildCard = "*" separator = "," @@ -38,8 +40,11 @@ func newFieldsHandler(parameters string) fieldsHandler { } parameters = strings.ToLower(parameters) + fieldsMap := sliceToMap(strings.Split(parameters, separator)) + fieldsMap[hashField] = struct{}{} // hashField should always be returned + return fieldsHandler{ - fieldsMap: sliceToMap(strings.Split(parameters, separator)), + fieldsMap: fieldsMap, } } diff --git a/node/external/transactionAPI/fieldsHandler_test.go b/node/external/transactionAPI/fieldsHandler_test.go index fab3b3a41d9..75b3ae6f81a 100644 --- a/node/external/transactionAPI/fieldsHandler_test.go +++ b/node/external/transactionAPI/fieldsHandler_test.go @@ -20,9 +20,11 @@ func Test_newFieldsHandler(t *testing.T) { for _, field := range splitFields { require.True(t, fh.IsFieldSet(field), fmt.Sprintf("field %s is not set", field)) } + require.True(t, fh.IsFieldSet(hashField), "hashField should have been returned by default") fh = newFieldsHandler("*") for _, field := range splitFields { require.True(t, fh.IsFieldSet(field)) } + require.True(t, fh.IsFieldSet(hashField), "hashField should have been returned by default") } diff --git a/node/external/transactionAPI/gasUsedAndFeeProcessor.go b/node/external/transactionAPI/gasUsedAndFeeProcessor.go index f0036bc136b..7bbb197c69f 100644 --- a/node/external/transactionAPI/gasUsedAndFeeProcessor.go +++ b/node/external/transactionAPI/gasUsedAndFeeProcessor.go @@ -4,19 +4,35 @@ import ( "math/big" "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/process" datafield "github.com/multiversx/mx-chain-vm-common-go/parsers/dataField" ) type gasUsedAndFeeProcessor struct { - feeComputer feeComputer - pubKeyConverter core.PubkeyConverter + feeComputer feeComputer + pubKeyConverter core.PubkeyConverter + argsParser process.ArgumentsParser + marshaller marshal.Marshalizer + enableEpochsHandler common.EnableEpochsHandler } -func newGasUsedAndFeeProcessor(txFeeCalculator feeComputer, pubKeyConverter core.PubkeyConverter) *gasUsedAndFeeProcessor { +func newGasUsedAndFeeProcessor( + txFeeCalculator feeComputer, + pubKeyConverter core.PubkeyConverter, + argsParser process.ArgumentsParser, + marshaller marshal.Marshalizer, + enableEpochsHandler common.EnableEpochsHandler, +) *gasUsedAndFeeProcessor { return &gasUsedAndFeeProcessor{ - feeComputer: txFeeCalculator, - pubKeyConverter: pubKeyConverter, + feeComputer: txFeeCalculator, + pubKeyConverter: pubKeyConverter, + argsParser: argsParser, + marshaller: marshaller, + enableEpochsHandler: enableEpochsHandler, } } @@ -27,30 +43,125 @@ func (gfp *gasUsedAndFeeProcessor) computeAndAttachGasUsedAndFee(tx *transaction tx.GasUsed = gasUsed tx.Fee = fee.String() - if tx.IsRelayed || gfp.isESDTOperationWithSCCall(tx) { + isFeeFixActive := gfp.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, tx.Epoch) + isRelayedBeforeFix := tx.IsRelayed && !isFeeFixActive + if isRelayedBeforeFix || gfp.isESDTOperationWithSCCall(tx) { tx.GasUsed = tx.GasLimit tx.Fee = tx.InitiallyPaidFee } + userTx, initialTotalFee, isRelayedV1V2 := gfp.getFeeOfRelayedV1V2(tx) + isRelayedAfterFix := isRelayedV1V2 && isFeeFixActive + if isRelayedAfterFix { + tx.InitiallyPaidFee = initialTotalFee.String() + tx.Fee = initialTotalFee.String() + tx.GasUsed = big.NewInt(0).Div(initialTotalFee, big.NewInt(0).SetUint64(tx.GasPrice)).Uint64() + } + + isRelayedV3 := common.IsValidRelayedTxV3(tx.Tx) hasRefundForSender := false + totalRefunds := big.NewInt(0) for _, scr := range tx.SmartContractResults { - if !scr.IsRefund || scr.RcvAddr != tx.Sender { + if !scr.IsRefund { + continue + } + if !isRelayedV3 && scr.RcvAddr != tx.Sender { continue } - if scr.RcvAddr != tx.Sender { + if isRelayedV3 && scr.RcvAddr != tx.RelayerAddress { continue } - gfp.setGasUsedAndFeeBaseOnRefundValue(tx, scr.Value) hasRefundForSender = true - break + totalRefunds.Add(totalRefunds, scr.Value) } - gfp.prepareTxWithResultsBasedOnLogs(tx, hasRefundForSender) + if totalRefunds.Cmp(big.NewInt(0)) > 0 { + gfp.setGasUsedAndFeeBaseOnRefundValue(tx, userTx, totalRefunds) + } + + gfp.prepareTxWithResultsBasedOnLogs(tx, userTx, hasRefundForSender) +} + +func (gfp *gasUsedAndFeeProcessor) getFeeOfRelayedV1V2(tx *transaction.ApiTransactionResult) (*transaction.ApiTransactionResult, *big.Int, bool) { + if !tx.IsRelayed { + return nil, nil, false + } + + isRelayedV3 := common.IsValidRelayedTxV3(tx.Tx) + if isRelayedV3 { + return nil, nil, false + } + + if len(tx.Data) == 0 { + return nil, nil, false + } + + funcName, args, err := gfp.argsParser.ParseCallData(string(tx.Data)) + if err != nil { + return nil, nil, false + } + + if funcName == core.RelayedTransaction { + return gfp.handleRelayedV1(args, tx) + } + + if funcName == core.RelayedTransactionV2 { + return gfp.handleRelayedV2(args, tx) + } + + return nil, nil, false +} + +func (gfp *gasUsedAndFeeProcessor) handleRelayedV1(args [][]byte, tx *transaction.ApiTransactionResult) (*transaction.ApiTransactionResult, *big.Int, bool) { + if len(args) != 1 { + return nil, nil, false + } + + innerTx := &transaction.Transaction{} + err := gfp.marshaller.Unmarshal(innerTx, args[0]) + if err != nil { + return nil, nil, false + } + + gasUsed := gfp.feeComputer.ComputeGasLimit(tx) + fee := gfp.feeComputer.ComputeTxFeeBasedOnGasUsed(tx, gasUsed) + + innerTxApiResult := &transaction.ApiTransactionResult{ + Tx: innerTx, + Epoch: tx.Epoch, + } + innerFee := gfp.feeComputer.ComputeTransactionFee(innerTxApiResult) + + return innerTxApiResult, big.NewInt(0).Add(fee, innerFee), true +} + +func (gfp *gasUsedAndFeeProcessor) handleRelayedV2(args [][]byte, tx *transaction.ApiTransactionResult) (*transaction.ApiTransactionResult, *big.Int, bool) { + innerTx := &transaction.Transaction{} + innerTx.RcvAddr = args[0] + innerTx.Nonce = big.NewInt(0).SetBytes(args[1]).Uint64() + innerTx.Data = args[2] + innerTx.Signature = args[3] + innerTx.Value = big.NewInt(0) + innerTx.GasPrice = tx.GasPrice + innerTx.GasLimit = tx.GasLimit - gfp.feeComputer.ComputeGasLimit(tx) + innerTx.SndAddr = tx.Tx.GetRcvAddr() + + gasUsed := gfp.feeComputer.ComputeGasLimit(tx) + fee := gfp.feeComputer.ComputeTxFeeBasedOnGasUsed(tx, gasUsed) + + innerTxApiResult := &transaction.ApiTransactionResult{ + Tx: innerTx, + Epoch: tx.Epoch, + } + innerFee := gfp.feeComputer.ComputeTransactionFee(innerTxApiResult) + + return innerTxApiResult, big.NewInt(0).Add(fee, innerFee), true } func (gfp *gasUsedAndFeeProcessor) prepareTxWithResultsBasedOnLogs( tx *transaction.ApiTransactionResult, + userTx *transaction.ApiTransactionResult, hasRefund bool, ) { if tx.Logs == nil || (tx.Function == "" && tx.Operation == datafield.OperationTransfer) { @@ -58,15 +169,13 @@ func (gfp *gasUsedAndFeeProcessor) prepareTxWithResultsBasedOnLogs( } for _, event := range tx.Logs.Events { - gfp.setGasUsedAndFeeBaseOnLogEvent(tx, hasRefund, event) + gfp.setGasUsedAndFeeBaseOnLogEvent(tx, userTx, hasRefund, event) } } -func (gfp *gasUsedAndFeeProcessor) setGasUsedAndFeeBaseOnLogEvent(tx *transaction.ApiTransactionResult, hasRefund bool, event *transaction.Events) { +func (gfp *gasUsedAndFeeProcessor) setGasUsedAndFeeBaseOnLogEvent(tx *transaction.ApiTransactionResult, userTx *transaction.ApiTransactionResult, hasRefund bool, event *transaction.Events) { if core.WriteLogIdentifier == event.Identifier && !hasRefund { - gasUsed, fee := gfp.feeComputer.ComputeGasUsedAndFeeBasedOnRefundValue(tx, big.NewInt(0)) - tx.GasUsed = gasUsed - tx.Fee = fee.String() + gfp.setGasUsedAndFeeBaseOnRefundValue(tx, userTx, big.NewInt(0)) } if core.SignalErrorOperation == event.Identifier { fee := gfp.feeComputer.ComputeTxFeeBasedOnGasUsed(tx, tx.GasLimit) @@ -75,9 +184,31 @@ func (gfp *gasUsedAndFeeProcessor) setGasUsedAndFeeBaseOnLogEvent(tx *transactio } } -func (gfp *gasUsedAndFeeProcessor) setGasUsedAndFeeBaseOnRefundValue(tx *transaction.ApiTransactionResult, refund *big.Int) { +func (gfp *gasUsedAndFeeProcessor) setGasUsedAndFeeBaseOnRefundValue( + tx *transaction.ApiTransactionResult, + userTx *transaction.ApiTransactionResult, + refund *big.Int, +) { + isRelayedV3 := len(tx.RelayerAddress) == len(tx.Sender) && + len(tx.RelayerSignature) == len(tx.Signature) + isValidUserTxAfterBaseCostActivation := !check.IfNilReflect(userTx) && gfp.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, tx.Epoch) + if isValidUserTxAfterBaseCostActivation && !isRelayedV3 { + gasUsed, fee := gfp.feeComputer.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) + gasUsedRelayedTx := gfp.feeComputer.ComputeGasLimit(tx) + feeRelayedTx := gfp.feeComputer.ComputeTxFeeBasedOnGasUsed(tx, gasUsedRelayedTx) + + tx.GasUsed = gasUsed + gasUsedRelayedTx + + fee.Add(fee, feeRelayedTx) + tx.Fee = fee.String() + + return + } + gasUsed, fee := gfp.feeComputer.ComputeGasUsedAndFeeBasedOnRefundValue(tx, refund) + tx.GasUsed = gasUsed + tx.Fee = fee.String() } diff --git a/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go b/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go index 99541bfef5d..b81ad1e03b9 100644 --- a/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go +++ b/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go @@ -1,16 +1,19 @@ package transactionAPI import ( + "encoding/hex" "math/big" "testing" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/pubkeyConverter" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/node/external/timemachine/fee" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/economics" + "github.com/multiversx/mx-chain-go/process/smartContract" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" @@ -38,7 +41,13 @@ func TestComputeTransactionGasUsedAndFeeMoveBalance(t *testing.T) { feeComp, _ := fee.NewFeeComputer(createEconomicsData(&enableEpochsHandlerMock.EnableEpochsHandlerStub{})) computer := fee.NewTestFeeComputer(feeComp) - gasUsedAndFeeProc := newGasUsedAndFeeProcessor(computer, pubKeyConverter) + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + ) sender := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" receiver := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" @@ -68,7 +77,13 @@ func TestComputeTransactionGasUsedAndFeeLogWithError(t *testing.T) { })) computer := fee.NewTestFeeComputer(feeComp) - gasUsedAndFeeProc := newGasUsedAndFeeProcessor(computer, pubKeyConverter) + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + ) sender := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" receiver := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" @@ -111,7 +126,13 @@ func TestComputeTransactionGasUsedAndFeeRelayedTxWithWriteLog(t *testing.T) { })) computer := fee.NewTestFeeComputer(feeComp) - gasUsedAndFeeProc := newGasUsedAndFeeProcessor(computer, pubKeyConverter) + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + ) sender := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" receiver := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" @@ -149,7 +170,13 @@ func TestComputeTransactionGasUsedAndFeeTransactionWithScrWithRefund(t *testing. })) computer := fee.NewTestFeeComputer(feeComp) - gasUsedAndFeeProc := newGasUsedAndFeeProcessor(computer, pubKeyConverter) + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + ) sender := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" receiver := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" @@ -197,7 +224,13 @@ func TestNFTTransferWithScCall(t *testing.T) { computer := fee.NewTestFeeComputer(feeComp) req.Nil(err) - gasUsedAndFeeProc := newGasUsedAndFeeProcessor(computer, pubKeyConverter) + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + ) sender := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" receiver := "erd1wc3uh22g2aved3qeehkz9kzgrjwxhg9mkkxp2ee7jj7ph34p2csq0n2y5x" @@ -221,3 +254,198 @@ func TestNFTTransferWithScCall(t *testing.T) { req.Equal(uint64(55_000_000), tx.GasUsed) req.Equal("822250000000000", tx.Fee) } + +func TestComputeAndAttachGasUsedAndFeeTransactionWithMultipleScrWithRefund(t *testing.T) { + t.Parallel() + + eeh := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.GasPriceModifierFlag || + flag == common.PenalizedTooMuchGasFlag || + flag == common.FixRelayedBaseCostFlag + }, + } + feeComp, _ := fee.NewFeeComputer(createEconomicsData(eeh)) + computer := fee.NewTestFeeComputer(feeComp) + + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + eeh, + ) + + txWithSRefundSCR := &transaction.ApiTransactionResult{} + err := core.LoadJsonFile(txWithSRefundSCR, "testData/scInvokingWithMultipleRefunds.json") + require.NoError(t, err) + + txWithSRefundSCR.Fee = "" + txWithSRefundSCR.GasUsed = 0 + + snd, _ := pubKeyConverter.Decode(txWithSRefundSCR.Sender) + rcv, _ := pubKeyConverter.Decode(txWithSRefundSCR.Receiver) + val, _ := big.NewInt(0).SetString(txWithSRefundSCR.Value, 10) + txWithSRefundSCR.Tx = &transaction.Transaction{ + Nonce: txWithSRefundSCR.Nonce, + Value: val, + RcvAddr: rcv, + SndAddr: snd, + GasPrice: txWithSRefundSCR.GasPrice, + GasLimit: txWithSRefundSCR.GasLimit, + Data: txWithSRefundSCR.Data, + } + + gasUsedAndFeeProc.computeAndAttachGasUsedAndFee(txWithSRefundSCR) + require.Equal(t, uint64(20313408), txWithSRefundSCR.GasUsed) + require.Equal(t, "319459080000000", txWithSRefundSCR.Fee) +} + +func TestComputeAndAttachGasUsedAndFeeFailedRelayedV1(t *testing.T) { + t.Parallel() + + enableEpochsHandler := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.GasPriceModifierFlag || + flag == common.PenalizedTooMuchGasFlag || + flag == common.FixRelayedBaseCostFlag + }, + } + feeComp, _ := fee.NewFeeComputer(createEconomicsData(enableEpochsHandler)) + computer := fee.NewTestFeeComputer(feeComp) + + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + smartContract.NewArgumentParser(), + &marshal.JsonMarshalizer{}, + enableEpochsHandler, + ) + + txWithSRefundSCR := &transaction.ApiTransactionResult{} + err := core.LoadJsonFile(txWithSRefundSCR, "testData/failedRelayedV1.json") + require.NoError(t, err) + + snd, _ := pubKeyConverter.Decode(txWithSRefundSCR.Sender) + rcv, _ := pubKeyConverter.Decode(txWithSRefundSCR.Receiver) + val, _ := big.NewInt(0).SetString(txWithSRefundSCR.Value, 10) + txWithSRefundSCR.Tx = &transaction.Transaction{ + Nonce: txWithSRefundSCR.Nonce, + Value: val, + RcvAddr: rcv, + SndAddr: snd, + GasPrice: txWithSRefundSCR.GasPrice, + GasLimit: txWithSRefundSCR.GasLimit, + Data: txWithSRefundSCR.Data, + } + + txWithSRefundSCR.InitiallyPaidFee = "" + txWithSRefundSCR.Fee = "" + txWithSRefundSCR.GasUsed = 0 + + gasUsedAndFeeProc.computeAndAttachGasUsedAndFee(txWithSRefundSCR) + require.Equal(t, uint64(6148000), txWithSRefundSCR.GasUsed) + require.Equal(t, "1198000000000000", txWithSRefundSCR.Fee) + require.Equal(t, "1274230000000000", txWithSRefundSCR.InitiallyPaidFee) +} + +func TestComputeAndAttachGasUsedAndFeeRelayedV1CreateNewDelegationContractWithRefund(t *testing.T) { + t.Parallel() + + enableEpochsHandler := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.GasPriceModifierFlag || + flag == common.PenalizedTooMuchGasFlag || + flag == common.FixRelayedBaseCostFlag + }, + } + feeComp, _ := fee.NewFeeComputer(createEconomicsData(enableEpochsHandler)) + computer := fee.NewTestFeeComputer(feeComp) + + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + smartContract.NewArgumentParser(), + &marshal.JsonMarshalizer{}, + enableEpochsHandler, + ) + + txWithSRefundSCR := &transaction.ApiTransactionResult{} + err := core.LoadJsonFile(txWithSRefundSCR, "testData/relayedV1CreateNewDelegationContract.json") + require.NoError(t, err) + + snd, _ := pubKeyConverter.Decode(txWithSRefundSCR.Sender) + rcv, _ := pubKeyConverter.Decode(txWithSRefundSCR.Receiver) + val, _ := big.NewInt(0).SetString(txWithSRefundSCR.Value, 10) + txWithSRefundSCR.Tx = &transaction.Transaction{ + Nonce: txWithSRefundSCR.Nonce, + Value: val, + RcvAddr: rcv, + SndAddr: snd, + GasPrice: txWithSRefundSCR.GasPrice, + GasLimit: txWithSRefundSCR.GasLimit, + Data: txWithSRefundSCR.Data, + } + + txWithSRefundSCR.InitiallyPaidFee = "" + txWithSRefundSCR.Fee = "" + txWithSRefundSCR.GasUsed = 0 + + gasUsedAndFeeProc.computeAndAttachGasUsedAndFee(txWithSRefundSCR) + require.Equal(t, uint64(56328500), txWithSRefundSCR.GasUsed) + require.Equal(t, "1878500000000000", txWithSRefundSCR.Fee) + require.Equal(t, "2177505000000000", txWithSRefundSCR.InitiallyPaidFee) +} + +func TestComputeAndAttachGasUsedAndFeeRelayedV3WithMultipleRefunds(t *testing.T) { + t.Parallel() + + eeh := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.GasPriceModifierFlag || + flag == common.PenalizedTooMuchGasFlag || + flag == common.FixRelayedBaseCostFlag || + flag == common.RelayedTransactionsV3Flag + }, + } + feeComp, _ := fee.NewFeeComputer(createEconomicsData(eeh)) + computer := fee.NewTestFeeComputer(feeComp) + + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + eeh, + ) + + txWithRefunds := &transaction.ApiTransactionResult{} + err := core.LoadJsonFile(txWithRefunds, "testData/relayedV3WithMultipleRefunds.json") + require.NoError(t, err) + + txWithRefunds.Fee = "" + txWithRefunds.GasUsed = 0 + + snd, _ := pubKeyConverter.Decode(txWithRefunds.Sender) + rcv, _ := pubKeyConverter.Decode(txWithRefunds.Receiver) + rel, _ := pubKeyConverter.Decode(txWithRefunds.RelayerAddress) + val, _ := big.NewInt(0).SetString(txWithRefunds.Value, 10) + sig, _ := hex.DecodeString(txWithRefunds.Signature) + relayerSig, _ := hex.DecodeString(txWithRefunds.RelayerSignature) + txWithRefunds.Tx = &transaction.Transaction{ + Nonce: txWithRefunds.Nonce, + Value: val, + RcvAddr: rcv, + SndAddr: snd, + RelayerAddr: rel, + GasPrice: txWithRefunds.GasPrice, + GasLimit: txWithRefunds.GasLimit, + Data: txWithRefunds.Data, + Signature: sig, + RelayerSignature: relayerSig, + } + + gasUsedAndFeeProc.computeAndAttachGasUsedAndFee(txWithRefunds) + require.Equal(t, uint64(4220447), txWithRefunds.GasUsed) + require.Equal(t, "289704470000000", txWithRefunds.Fee) +} diff --git a/node/external/transactionAPI/testData/failedRelayedV1.json b/node/external/transactionAPI/testData/failedRelayedV1.json new file mode 100644 index 00000000000..e2b1be88bee --- /dev/null +++ b/node/external/transactionAPI/testData/failedRelayedV1.json @@ -0,0 +1,60 @@ +{ + "type": "normal", + "processingTypeOnSource": "RelayedTx", + "processingTypeOnDestination": "RelayedTx", + "hash": "efe3e8fa273fc2db4d559185e9729f7bbf17f617e28424b6a6533fb193caf561", + "nonce": 125, + "value": "0", + "receiver": "erd1x9hax7mdqux0nak9ahxrc2g75h5ckpfs4ssr8l7tkxscaa293x6q8ffd4e", + "sender": "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6", + "gasPrice": 1000000000, + "gasLimit": 6148000, + "data": "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTM0MzcyYzIyNzM2NTZlNjQ2NTcyMjIzYTIyNGQ1NzJmNTQ2NTMyMzA0ODQ0NTA2ZTMyNzg2NTMzNGQ1MDQzNmI2NTcwNjU2ZDRjNDI1NDQzNzM0OTQ0NTAyZjc5Mzc0NzY4NmE3NjU2NDY2OTYyNTEzZDIyMmMyMjcyNjU2MzY1Njk3NjY1NzIyMjNhMjI0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDY0MTQ0MzU1MzM3NTg2MjUwNzk1NTQzNzU3NzRiNDk1MTM0NmUzNzMxNTE3OTZmNGE1ODM1NzM1NDQyNzI2NzNkMjIyYzIyNzY2MTZjNzU2NTIyM2EzMTMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDJjMjI2NzYxNzM1MDcyNjk2MzY1MjIzYTMxMzAzMDMwMzAzMDMwMzAzMDMwMmMyMjY3NjE3MzRjNjk2ZDY5NzQyMjNhMzUzMDMwMzAzMDMwMzAyYzIyNjQ2MTc0NjEyMjNhMjI1OTU3NTI2YjUxNDQ0MTc4NTE0NDQxNzk1MTQ0NDE3YTUxNDQ0MTMwNTE0NDQxMzEyMjJjMjI3MzY5Njc2ZTYxNzQ3NTcyNjUyMjNhMjI3NjRjNjQ2ZDMyMmY0ZTQ0NTQ0NDc3MzgzMTMxNTEzMDYyNzgyYjU4NGI1MTc4NTA0NTJmNzY0OTU3NDM2ODRlNzI2OTU1NmE3ODcyNzAyZjU5Mzg1MDRlNzI0ZDQ1NmM2NTQ3NmUzNTMxNTM3NjUyNTk2NDQ3MzQ2ZjQ3NTc3MjQ5Njk0MzQ4Mzk2YTMxNzY0ZjRiNmY1OTJmNDg1NTUyNjE0ZTQxNDg0MjUxM2QzZDIyMmMyMjYzNjg2MTY5NmU0OTQ0MjIzYTIyNTY0MTNkM2QyMjJjMjI3NjY1NzI3MzY5NmY2ZTIyM2EzMjdk", + "sourceShard": 0, + "destinationShard": 0, + "logs": { + "address": "erd1qqqqqqqqqqqqqpgq8efw6ak0e9q2as9zzr38aa2r9gy4lxcnq6uqpefzvu", + "events": [ + { + "address": "erd1qqqqqqqqqqqqqpgq8efw6ak0e9q2as9zzr38aa2r9gy4lxcnq6uqpefzvu", + "identifier": "signalError", + "topics": [ + "MW/Te20HDPn2xe3MPCkepemLBTCsIDP/y7GhjvVFibQ=", + "ZnVuY3Rpb24gZG9lcyBub3QgYWNjZXB0IEVHTEQgcGF5bWVudA==" + ], + "data": "QDY1Nzg2NTYzNzU3NDY5NmY2ZTIwNjY2MTY5NmM2NTY0", + "additionalData": [ + "QDY1Nzg2NTYzNzU3NDY5NmY2ZTIwNjY2MTY5NmM2NTY0" + ] + }, + { + "address": "erd1qqqqqqqqqqqqqpgq8efw6ak0e9q2as9zzr38aa2r9gy4lxcnq6uqpefzvu", + "identifier": "signalError", + "topics": [ + "XPSryD5QxTCdgH/D9naYh1mh4wEAG8mgJlgE9Cr4Brg=", + "ZnVuY3Rpb24gZG9lcyBub3QgYWNjZXB0IEVHTEQgcGF5bWVudA==" + ], + "data": null, + "additionalData": [ + "" + ] + }, + { + "address": "erd1x9hax7mdqux0nak9ahxrc2g75h5ckpfs4ssr8l7tkxscaa293x6q8ffd4e", + "identifier": "internalVMErrors", + "topics": [ + "AAAAAAAAAAAFAD5S7XbPyUCuwKIQ4n71QyoJX5sTBrg=", + "YWRk" + ], + "data": "CglydW50aW1lLmdvOjg1NiBbZXhlY3V0aW9uIGZhaWxlZF0gW2FkZF0KCXJ1bnRpbWUuZ286ODU2IFtleGVjdXRpb24gZmFpbGVkXSBbYWRkXQoJcnVudGltZS5nbzo4NTMgW2Z1bmN0aW9uIGRvZXMgbm90IGFjY2VwdCBFR0xEIHBheW1lbnRd", + "additionalData": [ + "CglydW50aW1lLmdvOjg1NiBbZXhlY3V0aW9uIGZhaWxlZF0gW2FkZF0KCXJ1bnRpbWUuZ286ODU2IFtleGVjdXRpb24gZmFpbGVkXSBbYWRkXQoJcnVudGltZS5nbzo4NTMgW2Z1bmN0aW9uIGRvZXMgbm90IGFjY2VwdCBFR0xEIHBheW1lbnRd" + ] + } + ] + }, + "status": "success", + "operation": "transfer", + "function": "add", + "isRelayed": true +} diff --git a/node/external/transactionAPI/testData/relayedV1CreateNewDelegationContract.json b/node/external/transactionAPI/testData/relayedV1CreateNewDelegationContract.json new file mode 100644 index 00000000000..a15e3c533ae --- /dev/null +++ b/node/external/transactionAPI/testData/relayedV1CreateNewDelegationContract.json @@ -0,0 +1,158 @@ +{ + "type": "normal", + "processingTypeOnSource": "RelayedTx", + "processingTypeOnDestination": "RelayedTx", + "hash": "94cb3bd3e2dca9920115f05549c9eee4dfc1d33e4ac3edd0741eb51165148b52", + "nonce": 0, + "round": 7, + "epoch": 1, + "value": "0", + "receiver": "erd1s89rm6mv6xyct38r3vqadj74rmqunamhwyz7c84a6u9thedj2wus5nlchg", + "sender": "erd1tp66n2lkhs2fm7elvh9lmzfajpg480v55sd8lf2lvu4fw92zsrasvn2wze", + "gasPrice": 1000000000, + "gasLimit": 86229000, + "data": "cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMwMmMyMjczNjU2ZTY0NjU3MjIyM2EyMjY3NjM2ZjM5MzYzMjdhNTI2OTU5NTg0NTM0MzQ3MzQyMzE3Mzc2NTY0ODczNDg0YTM5MzM2NDc4NDI2NTc3NjU3NjY0NjM0Yjc1MmI1Nzc5NTUzNzZiM2QyMjJjMjI3MjY1NjM2NTY5NzY2NTcyMjIzYTIyNDE0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDE1MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0MTQxNDUyZjJmMzgzZDIyMmMyMjc2NjE2Yzc1NjUyMjNhMzIzNTMwMzEzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNTA3MjY5NjM2NTIyM2EzMTMwMzAzMDMwMzAzMDMwMzAzMDJjMjI2NzYxNzM0YzY5NmQ2OTc0MjIzYTM4MzUzMDMwMzAzMDMwMzAyYzIyNjQ2MTc0NjEyMjNhMjI1OTMzNGE2YzU5NTg1MjZjNTQ2ZDU2MzM1MjQ3NTY3MzVhNTc2NDY4NjQ0NzZjNzY2MjZiNGU3NjYyNmU1Mjc5NTk1NzRlMzA1MTQ0NDE3NzUxNDQ0MTc3MjIyYzIyNzM2OTY3NmU2MTc0NzU3MjY1MjIzYTIyNTk1MTUzNTQ0MjMwMzE0ZjUwNGY0NDU5MzM0ZDU2NDQ1NDRkNWE3NzRkNzY2NjZiNzI1MDUwMzk2ZTM1NjQ1MzU4NjI3MDQ5NWE2MjcwNDM1MTQ5MzMzNDRkNTY2NzZiNzYzMzc0Njk2MTRmNGMzNjQ0NWE2NjM4NTU2NTJmNjg2Zjc5MzkzNTRiNGI2NTVhNDI2YjcwNzAzMTU1NzY3NzU5MzY0NzU3NDI3NzNkM2QyMjJjMjI2MzY4NjE2OTZlNDk0NDIyM2EyMjU5MzI2ODY4NjE1NzM0M2QyMjJjMjI3NjY1NzI3MzY5NmY2ZTIyM2EzMjdk", + "signature": "bf5d14d237b951d23247e99113fa15566ebbd0dccd215b743ae9629f226ab3f9ba333622567c5606e2ed06104df52f81f05fad2488be9cb50a83e1356e0d070e", + "sourceShard": 1, + "destinationShard": 1, + "blockNonce": 7, + "blockHash": "5f2a597d07b4b5ae84195178eb6f83493bb1230c7f316b40a0d9b6efbe1a4da5", + "notarizedAtSourceInMetaNonce": 9, + "NotarizedAtSourceInMetaHash": "34cd7dc91fc9773ddd2e09d900087b96cfecf6280a6dc25a1894c2db161cded1", + "notarizedAtDestinationInMetaNonce": 9, + "notarizedAtDestinationInMetaHash": "34cd7dc91fc9773ddd2e09d900087b96cfecf6280a6dc25a1894c2db161cded1", + "miniblockType": "TxBlock", + "miniblockHash": "03ef037eb1fd03f89a9b5ece12aa422223b440cb1fd02c4a6b82d65268121d28", + "hyperblockNonce": 9, + "hyperblockHash": "34cd7dc91fc9773ddd2e09d900087b96cfecf6280a6dc25a1894c2db161cded1", + "timestamp": 1729691671, + "smartContractResults": [ + { + "hash": "6e71137c75ad162d97ce45502d38f0af96362c06f03378f03a4e50bec6ce31ea", + "nonce": 1, + "value": 299005000000000, + "receiver": "erd1tp66n2lkhs2fm7elvh9lmzfajpg480v55sd8lf2lvu4fw92zsrasvn2wze", + "sender": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + "prevTxHash": "c825db2f9e6d17e41ed12e1aac86eecd7a828eeea0caf63a6e6a979f02a74f70", + "originalTxHash": "94cb3bd3e2dca9920115f05549c9eee4dfc1d33e4ac3edd0741eb51165148b52", + "gasLimit": 0, + "gasPrice": 1000000000, + "callType": 0, + "returnMessage": "gas refund for relayer", + "logs": { + "address": "erd1tp66n2lkhs2fm7elvh9lmzfajpg480v55sd8lf2lvu4fw92zsrasvn2wze", + "events": [ + { + "address": "erd1tp66n2lkhs2fm7elvh9lmzfajpg480v55sd8lf2lvu4fw92zsrasvn2wze", + "identifier": "completedTxEvent", + "topics": [ + "yCXbL55tF+Qe0S4arIbuzXqCju6gyvY6bmqXnwKnT3A=" + ], + "data": null, + "additionalData": null + } + ] + }, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "c825db2f9e6d17e41ed12e1aac86eecd7a828eeea0caf63a6e6a979f02a74f70", + "nonce": 0, + "value": 2501000000000000000000, + "receiver": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + "sender": "erd1s89rm6mv6xyct38r3vqadj74rmqunamhwyz7c84a6u9thedj2wus5nlchg", + "relayerAddress": "erd1tp66n2lkhs2fm7elvh9lmzfajpg480v55sd8lf2lvu4fw92zsrasvn2wze", + "relayedValue": 0, + "data": "createNewDelegationContract@00@00", + "prevTxHash": "94cb3bd3e2dca9920115f05549c9eee4dfc1d33e4ac3edd0741eb51165148b52", + "originalTxHash": "94cb3bd3e2dca9920115f05549c9eee4dfc1d33e4ac3edd0741eb51165148b52", + "gasLimit": 84900500, + "gasPrice": 1000000000, + "callType": 0, + "logs": { + "address": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + "events": [ + { + "address": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + "identifier": "transferValueOnly", + "topics": [ + "h5RY6SJT9AAA", + "AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAL///8=" + ], + "data": "RGVwbG95U21hcnRDb250cmFjdA==", + "additionalData": [ + "RGVwbG95U21hcnRDb250cmFjdA==", + "X2luaXQ=", + "AA==", + "AA==" + ] + }, + { + "address": "erd1s89rm6mv6xyct38r3vqadj74rmqunamhwyz7c84a6u9thedj2wus5nlchg", + "identifier": "delegate", + "topics": [ + "h5RY6SJT9AAA", + "h5RY6SJT9AAA", + "AQ==", + "h5RY6SJT9AAA", + "AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAL///8=" + ], + "data": null, + "additionalData": null + }, + { + "address": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhllllsajxzat", + "identifier": "transferValueOnly", + "topics": [ + "h5RY6SJT9AAA", + "AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAB//8=" + ], + "data": "RXhlY3V0ZU9uRGVzdENvbnRleHQ=", + "additionalData": [ + "RXhlY3V0ZU9uRGVzdENvbnRleHQ=", + "c3Rha2U=" + ] + }, + { + "address": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhllllsajxzat", + "identifier": "SCDeploy", + "topics": [ + "AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAL///8=", + "gco962zRiYXE44sB1svVHsHJ93dxBewevdcKu+WyU7k=", + "/uYNe6O98aIOSpF57HocNxS4JQ7FILx6+N7MEN3oAQY=" + ], + "data": null, + "additionalData": null + }, + { + "address": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + "identifier": "writeLog", + "topics": [ + "gco962zRiYXE44sB1svVHsHJ93dxBewevdcKu+WyU7k=" + ], + "data": "QDZmNmJAMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMmZmZmZmZg==", + "additionalData": [ + "QDZmNmJAMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMmZmZmZmZg==" + ] + } + ] + }, + "operation": "transfer", + "function": "createNewDelegationContract" + } + ], + "status": "success", + "receivers": [ + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6" + ], + "receiversShardIDs": [ + 4294967295 + ], + "operation": "transfer", + "function": "createNewDelegationContract", + "isRelayed": true, + "chainID": "chain", + "version": 2, + "options": 0 +} diff --git a/node/external/transactionAPI/testData/relayedV3WithMultipleRefunds.json b/node/external/transactionAPI/testData/relayedV3WithMultipleRefunds.json new file mode 100644 index 00000000000..26d3005c9d8 --- /dev/null +++ b/node/external/transactionAPI/testData/relayedV3WithMultipleRefunds.json @@ -0,0 +1,159 @@ +{ + "type": "normal", + "processingTypeOnSource": "SCInvoking", + "processingTypeOnDestination": "SCInvoking", + "value": "0", + "receiver": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "sender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "gasPrice": 1000000000, + "gasLimit": 15000000, + "gasUsed": 14536537, + "data": "Zm9yd2FyZEAwMUAwMDAwMDAwMDAwMDAwMDAwMDUwMGMxMzVlMjc2NmM3MDcyMTA2ZjIzYjAzNWIzODUxZDYzZDdmNjIxYzY5NmRhQDAwQDYxNjQ2NDQwMzAzMUAwMDdmZmZmZg==", + "signature": "645d88221a50bbf5173a9a46a70308f0c272d9b4701fe935ae2565147a32b484c4ea695bcaa102a299877f7e7699bb7990c491a601ae636851f5485dd59fe10b", + "smartContractResults": [ + { + "hash": "16b05474872d51840ca9270d8790fc65d50f5d076bb491388344f5095f6f0df0", + "nonce": 24, + "value": 4634630000000, + "receiver": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "sender": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "prevTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 0, + "gasPrice": 1000000000, + "callType": 0, + "returnMessage": "gas refund for relayer", + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "logs": { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "events": [ + { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "identifier": "completedTxEvent", + "topics": [ + "j+UTOzPTFTkgBtEdvrLqnA1fHrCFaq8wROn1URhlaEM=" + ], + "data": null, + "additionalData": null + } + ] + }, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "9090261203cf20e3b51e4c921ce27fa595fcca3f37c83543c7a4a57733139f4d", + "nonce": 1, + "value": 103160900000000, + "receiver": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "sender": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "prevTxHash": "a74d26756c58c522637b3c73b299828901533c93d5ccd75d6ec6a3410755d3e4", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 0, + "gasPrice": 1000000000, + "callType": 0, + "returnMessage": "gas refund for relayer", + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "logs": { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "events": [ + { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "identifier": "completedTxEvent", + "topics": [ + "p00mdWxYxSJjezxzspmCiQFTPJPVzNddbsajQQdV0+Q=" + ], + "data": null, + "additionalData": null + } + ] + }, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "5fb1c0d5d04b80331839de417523dff45a1be9489d4d84f9c5535314e0542525", + "nonce": 0, + "value": 0, + "receiver": "erd1qqqqqqqqqqqqqpgqcy67yanvwpepqmerkq6m8pgav0tlvgwxjmdq4hukxw", + "sender": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "relayerAddress": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "relayedValue": 0, + "data": "add@01@21fdaf01f965469c1bebf13c3c49a18f0dde9d8e380cec78cea065246fabb2d9@8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843@4185d4", + "prevTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 12682707, + "gasPrice": 1000000000, + "callType": 1, + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "operation": "transfer", + "function": "add" + }, + { + "hash": "a74d26756c58c522637b3c73b299828901533c93d5ccd75d6ec6a3410755d3e4", + "nonce": 0, + "value": 0, + "receiver": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "sender": "erd1qqqqqqqqqqqqqpgqcy67yanvwpepqmerkq6m8pgav0tlvgwxjmdq4hukxw", + "relayerAddress": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "relayedValue": 0, + "data": "@00@95a836710c0857f995e670bf3aa944263473534c8c5f4b7959c938045f62023b@21fdaf01f965469c1bebf13c3c49a18f0dde9d8e380cec78cea065246fabb2d9@8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843@00", + "prevTxHash": "5fb1c0d5d04b80331839de417523dff45a1be9489d4d84f9c5535314e0542525", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 11512215, + "gasPrice": 1000000000, + "callType": 2, + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "logs": { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "events": [ + { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "identifier": "writeLog", + "topics": [ + "AAAAAAAAAAAFAO2OJalO+oN6rg5ZMRLPuwG0SHVQaeE=" + ], + "data": "QDZmNmJAMDBjYTc3YmFjNEAwNjBjY2U1NQ==", + "additionalData": [ + "QDZmNmJAMDBjYTc3YmFjNEAwNjBjY2U1NQ==" + ] + } + ] + }, + "operation": "transfer" + } + ], + "logs": { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "events": [ + { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "identifier": "transferValueOnly", + "topics": [ + "", + "AAAAAAAAAAAFAME14nZscHIQbyOwNbOFHWPX9iHGlto=" + ], + "data": "QXN5bmNDYWxs", + "additionalData": [ + "QXN5bmNDYWxs", + "YWRk", + "AQ==" + ] + }, + { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "identifier": "writeLog", + "topics": [ + "6sts9++Sb93jXufBXEN4veMc5sfxE0vcWGfcfNAfMcQ=" + ], + "data": "QDZmNmI=", + "additionalData": [ + "QDZmNmI=" + ] + } + ] + }, + "isRelayed": true, + "relayerAddress": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "relayerSignature": "dfb60f7d13c024335ec9586b7af579ed47da002a23313272069711bceebddc2d9216b7cc1395c37a1c328288fcc774d5cbeb610db2900f76647307d35a593e08" +} diff --git a/node/external/transactionAPI/testData/scInvokingWithMultipleRefunds.json b/node/external/transactionAPI/testData/scInvokingWithMultipleRefunds.json new file mode 100644 index 00000000000..6223da3db9f --- /dev/null +++ b/node/external/transactionAPI/testData/scInvokingWithMultipleRefunds.json @@ -0,0 +1,115 @@ +{ + "type": "normal", + "processingTypeOnSource": "SCInvoking", + "processingTypeOnDestination": "SCInvoking", + "hash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "nonce": 5720, + "value": "88000000000000000", + "receiver": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "sender": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "gasPrice": 1000000000, + "gasLimit": 45000000, + "gasUsed": 20313408, + "data": "YnV5QDEzMDA5N0A1NjQzNGY0OTRlMmQzMjY0MzQzNDMzNjRAMDFAMDE4NmEw", + "smartContractResults": [ + { + "hash": "ebc994f2e46037fbf16f3426ab3ea40a43b648620209372bcb158153053fb5f9", + "nonce": 5721, + "value": 246865920000000, + "receiver": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "sender": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "data": "@6f6b", + "prevTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 0, + "gasPrice": 1000000000, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "402b39eba60202424565e224ab7121fd49eb8da366bc8b7b5d38496f1ce29f0d", + "nonce": 0, + "value": 2640000000000000, + "receiver": "erd1qqqqqqqqqqqqqpgq8538ku69p97lq4eug75y8d6g6yfwhd7c45qs4zvejt", + "sender": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "data": "depositRoyalties@0000000000000000050073175b6392a2f2661f2fb1fd2552787534433af98638", + "prevTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 5500000, + "gasPrice": 1000000000, + "originalSender": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "operation": "transfer", + "function": "depositRoyalties" + }, + { + "hash": "9912a1b3d3918a79f9c560ff2d81b24b316a89cbefd195325807132319147502", + "nonce": 0, + "value": 84480000000000000, + "receiver": "erd1qtkml3n4lwy5hxdmdfe67nm5jg5v6kqrtsdjt76za35ms4nnvghsjfujaw", + "sender": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "prevTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 0, + "gasPrice": 1000000000, + "originalSender": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "operation": "transfer" + }, + { + "hash": "11c18202b3d117fa3301e8cbc72f6d258d56b70686fa174bffa331079b753b31", + "nonce": 0, + "value": 0, + "receiver": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "sender": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "data": "ESDTNFTTransfer@56434f494e2d326434343364@01@0186a0@08011204000186a022ef020801120d56696c6c6167657220436f696e1a200000000000000000050073175b6392a2f2661f2fb1fd2552787534433af9863820ac022a203e0e25256b5c0a5d5825deb7519eeae0468c2597533db2de10bb560ae317b7f0325068747470733a2f2f697066732e696f2f697066732f6261666b7265696864703577797975706a6174356a6d68726e706c786d3333616570737a67667333656733716c336e776f6268736d72376e747434325068747470733a2f2f697066732e696f2f697066732f6261666b726569636d727763366b6b7472646d6669797a66616f687664357977796f34746f343670776c6b3767363562366d6b74376835697a6f793a71746167733a6769616e74732c6769616e74732076696c6c6167652c6e70632c76696c6c6167657220636f696e3b6d657461646174613a6261666b726569636d727763366b6b7472646d6669797a66616f687664357977796f34746f343670776c6b3767363562366d6b74376835697a6f79", + "prevTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 0, + "gasPrice": 1000000000, + "originalSender": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "operation": "ESDTNFTTransfer" + }, + { + "hash": "3a814d0abb4c00c44bc8ba29007c51edc487daa17c7666efc9fad1f5a86d03a2", + "nonce": 1, + "value": 880000000000000, + "receiver": "erd1qqqqqqqqqqqqqpgq8538ku69p97lq4eug75y8d6g6yfwhd7c45qs4zvejt", + "sender": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "data": "deposit", + "prevTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 3500000, + "gasPrice": 1000000000, + "originalSender": "erd1trqrpnl7e5nsagfqh4qp2tfpqg7hjacsesrstuhjk76rgagag9ws87gy6l", + "operation": "transfer", + "function": "deposit" + }, + { + "hash": "b7a2e5cf1ddad2630ccf8c703c9494b9b446e3a25ce362f6ec7c739448e41cbf", + "nonce": 1, + "value": 26131840000000, + "receiver": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "sender": "erd1qqqqqqqqqqqqqpgq8538ku69p97lq4eug75y8d6g6yfwhd7c45qs4zvejt", + "data": "@6f6b", + "prevTxHash": "402b39eba60202424565e224ab7121fd49eb8da366bc8b7b5d38496f1ce29f0d", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 0, + "gasPrice": 1000000000, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "40d329ecd05ffa543a41efd216c463f2ec229432d7b5c1c9132af29e406f389d", + "nonce": 2, + "value": 7494280000000, + "receiver": "erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8", + "sender": "erd1qqqqqqqqqqqqqpgq8538ku69p97lq4eug75y8d6g6yfwhd7c45qs4zvejt", + "data": "@6f6b", + "prevTxHash": "3a814d0abb4c00c44bc8ba29007c51edc487daa17c7666efc9fad1f5a86d03a2", + "originalTxHash": "293256314626408acc866793cb4e762ec4b2925dbce3bfb166e39e4deea66e93", + "gasLimit": 0, + "gasPrice": 1000000000, + "operation": "transfer", + "isRefund": true + } + ] +} diff --git a/node/external/transactionAPI/unmarshaller.go b/node/external/transactionAPI/unmarshaller.go index c9526217f4f..24d8961885b 100644 --- a/node/external/transactionAPI/unmarshaller.go +++ b/node/external/transactionAPI/unmarshaller.go @@ -101,7 +101,11 @@ func (tu *txUnmarshaller) unmarshalTransaction(txBytes []byte, txType transactio } apiTx.ReceiversShardIDs = res.ReceiversShardID - apiTx.IsRelayed = res.IsRelayed + + hasValidRelayer := len(apiTx.RelayerAddress) == len(apiTx.Sender) && len(apiTx.RelayerAddress) > 0 + hasValidRelayerSignature := len(apiTx.RelayerSignature) == len(apiTx.Signature) && len(apiTx.RelayerSignature) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature + apiTx.IsRelayed = res.IsRelayed || isRelayedV3 return apiTx, nil } @@ -132,6 +136,12 @@ func (tu *txUnmarshaller) prepareNormalTx(tx *transaction.Transaction) *transact apiTx.GuardianAddr = tu.addressPubKeyConverter.SilentEncode(tx.GuardianAddr, log) apiTx.GuardianSignature = hex.EncodeToString(tx.GuardianSignature) } + if len(tx.RelayerAddr) > 0 { + apiTx.RelayerAddress = tu.addressPubKeyConverter.SilentEncode(tx.RelayerAddr, log) + } + if len(tx.RelayerSignature) > 0 { + apiTx.RelayerSignature = hex.EncodeToString(tx.RelayerSignature) + } return apiTx } @@ -162,6 +172,12 @@ func (tu *txUnmarshaller) prepareInvalidTx(tx *transaction.Transaction) *transac apiTx.GuardianAddr = tu.addressPubKeyConverter.SilentEncode(tx.GuardianAddr, log) apiTx.GuardianSignature = hex.EncodeToString(tx.GuardianSignature) } + if len(tx.RelayerAddr) > 0 { + apiTx.RelayerAddress = tu.addressPubKeyConverter.SilentEncode(tx.RelayerAddr, log) + } + if len(tx.RelayerSignature) > 0 { + apiTx.RelayerSignature = hex.EncodeToString(tx.RelayerSignature) + } return apiTx } diff --git a/node/metrics/metrics.go b/node/metrics/metrics.go index 25356b0513c..5c74f42eacd 100644 --- a/node/metrics/metrics.go +++ b/node/metrics/metrics.go @@ -121,6 +121,7 @@ func InitConfigMetrics( appStatusHandler.SetUInt64Value(common.MetricReturnDataToLastTransferEnableEpoch, uint64(enableEpochs.ReturnDataToLastTransferEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricSenderInOutTransferEnableEpoch, uint64(enableEpochs.SenderInOutTransferEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricRelayedTransactionsV2EnableEpoch, uint64(enableEpochs.RelayedTransactionsV2EnableEpoch)) + appStatusHandler.SetUInt64Value(common.MetricFixRelayedBaseCostEnableEpoch, uint64(enableEpochs.FixRelayedBaseCostEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricUnbondTokensV2EnableEpoch, uint64(enableEpochs.UnbondTokensV2EnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricSaveJailedAlwaysEnableEpoch, uint64(enableEpochs.SaveJailedAlwaysEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricValidatorToDelegationEnableEpoch, uint64(enableEpochs.ValidatorToDelegationEnableEpoch)) @@ -199,6 +200,9 @@ func InitConfigMetrics( appStatusHandler.SetUInt64Value(common.MetricDynamicESDTEnableEpoch, uint64(enableEpochs.DynamicESDTEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricEGLDInMultiTransferEnableEpoch, uint64(enableEpochs.EGLDInMultiTransferEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricCryptoOpcodesV2EnableEpoch, uint64(enableEpochs.CryptoOpcodesV2EnableEpoch)) + appStatusHandler.SetUInt64Value(common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch, uint64(enableEpochs.MultiESDTNFTTransferAndExecuteByUserEnableEpoch)) + appStatusHandler.SetUInt64Value(common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch, uint64(enableEpochs.FixRelayedMoveBalanceToNonPayableSCEnableEpoch)) + appStatusHandler.SetUInt64Value(common.MetricRelayedTransactionsV3EnableEpoch, uint64(enableEpochs.RelayedTransactionsV3EnableEpoch)) for i, nodesChangeConfig := range enableEpochs.MaxNodesChangeEnableEpoch { epochEnable := fmt.Sprintf("%s%d%s", common.MetricMaxNodesChangeEnableEpoch, i, common.EpochEnableSuffix) diff --git a/node/metrics/metrics_test.go b/node/metrics/metrics_test.go index fdfbc3bb533..3ffe2cf7a5a 100644 --- a/node/metrics/metrics_test.go +++ b/node/metrics/metrics_test.go @@ -208,6 +208,10 @@ func TestInitConfigMetrics(t *testing.T) { EGLDInMultiTransferEnableEpoch: 101, CryptoOpcodesV2EnableEpoch: 102, ScToScLogEventEnableEpoch: 103, + FixRelayedBaseCostEnableEpoch: 104, + MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 105, + FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 106, + RelayedTransactionsV3EnableEpoch: 107, MaxNodesChangeEnableEpoch: []config.MaxNodesChangeConfig{ { EpochEnable: 0, @@ -326,6 +330,10 @@ func TestInitConfigMetrics(t *testing.T) { "erd_egld_in_multi_transfer_enable_epoch": uint32(101), "erd_crypto_opcodes_v2_enable_epoch": uint32(102), "erd_set_sc_to_sc_log_event_enable_epoch": uint32(103), + "erd_fix_relayed_base_cost_enable_epoch": uint32(104), + "erd_multi_esdt_transfer_execute_by_user_enable_epoch": uint32(105), + "erd_fix_relayed_move_balance_to_non_payable_sc_enable_epoch": uint32(106), + "erd_relayed_transactions_v3_enable_epoch": uint32(107), "erd_max_nodes_change_enable_epoch": nil, "erd_total_supply": "12345", "erd_hysteresis": "0.100000", diff --git a/node/mock/apiTransactionHandlerStub.go b/node/mock/apiTransactionHandlerStub.go index 2ae18622197..4bd9ca4633f 100644 --- a/node/mock/apiTransactionHandlerStub.go +++ b/node/mock/apiTransactionHandlerStub.go @@ -15,6 +15,16 @@ type TransactionAPIHandlerStub struct { UnmarshalTransactionCalled func(txBytes []byte, txType transaction.TxType) (*transaction.ApiTransactionResult, error) UnmarshalReceiptCalled func(receiptBytes []byte) (*transaction.ApiReceipt, error) PopulateComputedFieldsCalled func(tx *transaction.ApiTransactionResult) + GetSCRsByTxHashCalled func(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) +} + +// GetSCRsByTxHash -- +func (tas *TransactionAPIHandlerStub) GetSCRsByTxHash(txHash string, scrHash string) ([]*transaction.ApiSmartContractResult, error) { + if tas.GetSCRsByTxHashCalled != nil { + return tas.GetSCRsByTxHashCalled(txHash, scrHash) + } + + return nil, nil } // GetTransaction - diff --git a/node/node.go b/node/node.go index 470962f73c8..f85f02e7b01 100644 --- a/node/node.go +++ b/node/node.go @@ -54,8 +54,7 @@ var log = logger.GetOrCreate("node") var _ facade.NodeHandler = (*Node)(nil) // Option represents a functional configuration parameter that can operate -// -// over the None struct. +// over the None struct. type Option func(*Node) error type filter interface { @@ -246,7 +245,7 @@ func (n *Node) GetAllIssuedESDTs(tokenType string, ctx context.Context) ([]strin continue } - if bytes.Equal(esdtToken.TokenType, []byte(tokenType)) { + if tokenTypeEquals(esdtToken.TokenType, tokenType) { tokens = append(tokens, tokenName) } } @@ -263,6 +262,19 @@ func (n *Node) GetAllIssuedESDTs(tokenType string, ctx context.Context) ([]strin return tokens, nil } +func tokenTypeEquals(tokenType []byte, providedTokenType string) bool { + if providedTokenType == core.NonFungibleESDTv2 || + providedTokenType == core.NonFungibleESDT { + return bytes.Equal(tokenType, []byte(core.NonFungibleESDTv2)) || bytes.Equal(tokenType, []byte(core.NonFungibleESDT)) || bytes.Equal(tokenType, []byte(core.DynamicNFTESDT)) + } + + if providedTokenType == core.SemiFungibleESDT { + return bytes.Equal(tokenType, []byte(core.SemiFungibleESDT)) || bytes.Equal(tokenType, []byte(core.DynamicSFTESDT)) + } + + return bytes.Equal(tokenType, []byte(providedTokenType)) +} + func (n *Node) getEsdtDataFromLeaf(leaf core.KeyValueHolder) (*systemSmartContracts.ESDTDataV2, bool) { esdtToken := &systemSmartContracts.ESDTDataV2{} @@ -288,7 +300,7 @@ func (n *Node) GetKeyValuePairs(address string, options api.AccountQueryOptions, } if check.IfNil(userAccount.DataTrie()) { - return map[string]string{}, api.BlockInfo{}, nil + return map[string]string{}, blockInfo, nil } mapToReturn, err := n.getKeys(userAccount, ctx) @@ -718,7 +730,7 @@ func (n *Node) ValidateTransaction(tx *transaction.Transaction) error { if errors.Is(err, process.ErrAccountNotFound) { return fmt.Errorf("%w for address %s", process.ErrInsufficientFunds, - n.coreComponents.AddressPubKeyConverter().SilentEncode(tx.SndAddr, log), + n.extractAddressFromError(err), ) } @@ -793,6 +805,7 @@ func (n *Node) commonTransactionValidation( enableSignWithTxHash, n.coreComponents.TxSignHasher(), n.coreComponents.TxVersionChecker(), + n.coreComponents.EnableEpochsHandler(), ) if err != nil { return nil, nil, err @@ -841,6 +854,9 @@ func (n *Node) CreateTransaction(txArgs *external.ArgsCreateTransaction) (*trans if len(txArgs.GuardianSigHex) > n.addressSignatureHexSize { return nil, nil, fmt.Errorf("%w for guardian signature", ErrInvalidSignatureLength) } + if len(txArgs.RelayerSignatureHex) > n.addressSignatureHexSize { + return nil, nil, fmt.Errorf("%w for relayer signature", ErrInvalidSignatureLength) + } if uint32(len(txArgs.Receiver)) > n.coreComponents.EncodedAddressLen() { return nil, nil, fmt.Errorf("%w for receiver", ErrInvalidAddressLength) @@ -851,6 +867,9 @@ func (n *Node) CreateTransaction(txArgs *external.ArgsCreateTransaction) (*trans if uint32(len(txArgs.Guardian)) > n.coreComponents.EncodedAddressLen() { return nil, nil, fmt.Errorf("%w for guardian", ErrInvalidAddressLength) } + if uint32(len(txArgs.Relayer)) > n.coreComponents.EncodedAddressLen() { + return nil, nil, fmt.Errorf("%w for relayer", ErrInvalidAddressLength) + } if len(txArgs.SenderUsername) > core.MaxUserNameLength { return nil, nil, ErrInvalidSenderUsernameLength } @@ -907,6 +926,20 @@ func (n *Node) CreateTransaction(txArgs *external.ArgsCreateTransaction) (*trans return nil, nil, err } } + if len(txArgs.Relayer) > 0 { + relayerAddress, errDecode := addrPubKeyConverter.Decode(txArgs.Relayer) + if errDecode != nil { + return nil, nil, fmt.Errorf("%w while decoding relayer address", errDecode) + } + tx.RelayerAddr = relayerAddress + } + if len(txArgs.RelayerSignatureHex) > 0 { + relayerSigBytes, errDecodeString := hex.DecodeString(txArgs.RelayerSignatureHex) + if errDecodeString != nil { + return nil, nil, fmt.Errorf("%w while decoding relayer signature", errDecodeString) + } + tx.RelayerSignature = relayerSigBytes + } var txHash []byte txHash, err = core.CalculateHash(n.coreComponents.InternalMarshalizer(), n.coreComponents.Hasher(), tx) @@ -956,6 +989,10 @@ func (n *Node) GetAccountWithKeys(address string, options api.AccountQueryOption var keys map[string]string if options.WithKeys { + if accInfo.account == nil || accInfo.account.DataTrie() == nil { + return accInfo.accountResponse, accInfo.block, nil + } + keys, err = n.getKeys(accInfo.account, ctx) if err != nil { return api.AccountResponse{}, api.BlockInfo{}, err @@ -1516,6 +1553,22 @@ func (n *Node) getKeyBytes(key string) ([]byte, error) { return hex.DecodeString(key) } +func (n *Node) extractAddressFromError(err error) string { + if !strings.Contains(err.Error(), "for address") { + return "" + } + + errWords := strings.Split(err.Error(), " ") + for _, word := range errWords { + _, errDecode := n.coreComponents.AddressPubKeyConverter().Decode(word) + if errDecode == nil { + return word + } + } + + return "" +} + // IsInterfaceNil returns true if there is no value under the interface func (n *Node) IsInterfaceNil() bool { return n == nil diff --git a/node/nodeRunner.go b/node/nodeRunner.go index f6fa53a660e..1bbc73259be 100644 --- a/node/nodeRunner.go +++ b/node/nodeRunner.go @@ -322,7 +322,12 @@ func (nr *nodeRunner) executeOneComponentCreationCycle( } log.Debug("creating bootstrap components") - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + ) if err != nil { return true, err } @@ -330,7 +335,12 @@ func (nr *nodeRunner) executeOneComponentCreationCycle( nr.logInformation(managedCoreComponents, managedCryptoComponents, managedBootstrapComponents) log.Debug("creating data components") - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + ) if err != nil { return true, err } @@ -837,6 +847,7 @@ func (nr *nodeRunner) createMetrics( metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricMinGasPrice, coreComponents.EconomicsData().MinGasPrice()) metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricMinGasLimit, coreComponents.EconomicsData().MinGasLimit()) metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitGuardedTx, coreComponents.EconomicsData().ExtraGasLimitGuardedTx()) + metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitRelayedTx, coreComponents.EconomicsData().MinGasLimit()) metrics.SaveStringMetric(statusCoreComponents.AppStatusHandler(), common.MetricRewardsTopUpGradientPoint, coreComponents.EconomicsData().RewardsTopUpGradientPoint().String()) metrics.SaveStringMetric(statusCoreComponents.AppStatusHandler(), common.MetricTopUpFactor, fmt.Sprintf("%g", coreComponents.EconomicsData().RewardsTopUpFactor())) metrics.SaveStringMetric(statusCoreComponents.AppStatusHandler(), common.MetricGasPriceModifier, fmt.Sprintf("%g", coreComponents.EconomicsData().GasPriceModifier())) diff --git a/node/node_test.go b/node/node_test.go index 37efcdd4f50..83b164dc390 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -1160,7 +1160,10 @@ func TestNode_GetAllIssuedESDTs(t *testing.T) { acc := createAcc([]byte("newaddress")) esdtToken := []byte("TCK-RANDOM") sftToken := []byte("SFT-RANDOM") + sftTokenDynamic := []byte("SFT-Dynamic") nftToken := []byte("NFT-RANDOM") + nftTokenV2 := []byte("NFT-RANDOM-V2") + nftTokenDynamic := []byte("NFT-Dynamic") esdtData := &systemSmartContracts.ESDTDataV2{TokenName: []byte("fungible"), TokenType: []byte(core.FungibleESDT)} marshalledData, _ := getMarshalizer().Marshal(esdtData) @@ -1170,13 +1173,28 @@ func TestNode_GetAllIssuedESDTs(t *testing.T) { sftMarshalledData, _ := getMarshalizer().Marshal(sftData) _ = acc.SaveKeyValue(sftToken, sftMarshalledData) + sftDataDynamic := &systemSmartContracts.ESDTDataV2{TokenName: []byte("semi fungible dynamic"), TokenType: []byte(core.DynamicSFTESDT)} + sftMarshalledDataDynamic, _ := getMarshalizer().Marshal(sftDataDynamic) + _ = acc.SaveKeyValue(sftTokenDynamic, sftMarshalledDataDynamic) + nftData := &systemSmartContracts.ESDTDataV2{TokenName: []byte("non fungible"), TokenType: []byte(core.NonFungibleESDT)} nftMarshalledData, _ := getMarshalizer().Marshal(nftData) _ = acc.SaveKeyValue(nftToken, nftMarshalledData) + nftData2 := &systemSmartContracts.ESDTDataV2{TokenName: []byte("non fungible v2"), TokenType: []byte(core.NonFungibleESDT)} + nftMarshalledData2, _ := getMarshalizer().Marshal(nftData2) + _ = acc.SaveKeyValue(nftTokenV2, nftMarshalledData2) + + nftDataDynamic := &systemSmartContracts.ESDTDataV2{TokenName: []byte("non fungible dynamic"), TokenType: []byte(core.DynamicNFTESDT)} + nftMarshalledDataDyamic, _ := getMarshalizer().Marshal(nftDataDynamic) + _ = acc.SaveKeyValue(nftTokenDynamic, nftMarshalledDataDyamic) + esdtSuffix := append(esdtToken, acc.AddressBytes()...) nftSuffix := append(nftToken, acc.AddressBytes()...) + nftSuffix2 := append(nftTokenV2, acc.AddressBytes()...) + nftDynamicSuffix := append(nftTokenDynamic, acc.AddressBytes()...) sftSuffix := append(sftToken, acc.AddressBytes()...) + sftDynamicSuffix := append(sftTokenDynamic, acc.AddressBytes()...) acc.SetDataTrie( &trieMock.TrieStub{ @@ -1187,9 +1205,16 @@ func TestNode_GetAllIssuedESDTs(t *testing.T) { trieLeaf, _ = tlp.ParseLeaf(sftToken, append(sftMarshalledData, sftSuffix...), core.NotSpecified) leavesChannels.LeavesChan <- trieLeaf + trieLeaf, _ = tlp.ParseLeaf(sftTokenDynamic, append(sftMarshalledDataDynamic, sftDynamicSuffix...), core.NotSpecified) + leavesChannels.LeavesChan <- trieLeaf trieLeaf, _ = tlp.ParseLeaf(nftToken, append(nftMarshalledData, nftSuffix...), core.NotSpecified) leavesChannels.LeavesChan <- trieLeaf + trieLeaf, _ = tlp.ParseLeaf(nftTokenV2, append(nftMarshalledData2, nftSuffix2...), core.NotSpecified) + leavesChannels.LeavesChan <- trieLeaf + trieLeaf, _ = tlp.ParseLeaf(nftTokenDynamic, append(nftMarshalledDataDyamic, nftDynamicSuffix...), core.NotSpecified) + leavesChannels.LeavesChan <- trieLeaf + close(leavesChannels.LeavesChan) leavesChannels.ErrChan.Close() }() @@ -1237,17 +1262,27 @@ func TestNode_GetAllIssuedESDTs(t *testing.T) { value, err = n.GetAllIssuedESDTs(core.SemiFungibleESDT, context.Background()) assert.Nil(t, err) - assert.Equal(t, 1, len(value)) + assert.Equal(t, 2, len(value)) assert.Equal(t, string(sftToken), value[0]) + assert.Equal(t, string(sftTokenDynamic), value[1]) value, err = n.GetAllIssuedESDTs(core.NonFungibleESDT, context.Background()) assert.Nil(t, err) - assert.Equal(t, 1, len(value)) + assert.Equal(t, 3, len(value)) // for both versions assert.Equal(t, string(nftToken), value[0]) + assert.Equal(t, string(nftTokenV2), value[1]) + assert.Equal(t, string(nftTokenDynamic), value[2]) + + value, err = n.GetAllIssuedESDTs(core.NonFungibleESDTv2, context.Background()) + assert.Nil(t, err) + assert.Equal(t, 3, len(value)) // for both versions + assert.Equal(t, string(nftToken), value[0]) + assert.Equal(t, string(nftTokenV2), value[1]) + assert.Equal(t, string(nftTokenDynamic), value[2]) value, err = n.GetAllIssuedESDTs("", context.Background()) assert.Nil(t, err) - assert.Equal(t, 3, len(value)) + assert.Equal(t, 6, len(value)) } func TestNode_GetESDTsWithRole(t *testing.T) { @@ -3005,15 +3040,63 @@ func TestValidateTransaction_ShouldAdaptAccountNotFoundError(t *testing.T) { node.WithCryptoComponents(getDefaultCryptoComponents()), ) - tx := &transaction.Transaction{ - SndAddr: bytes.Repeat([]byte("1"), 32), - RcvAddr: bytes.Repeat([]byte("1"), 32), - Value: big.NewInt(37), - Signature: []byte("signature"), - ChainID: []byte("chainID"), - } - err := n.ValidateTransaction(tx) - require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) + t.Run("normal tx", func(t *testing.T) { + tx := &transaction.Transaction{ + SndAddr: bytes.Repeat([]byte("1"), 32), + RcvAddr: bytes.Repeat([]byte("1"), 32), + Value: big.NewInt(37), + Signature: []byte("signature"), + ChainID: []byte("chainID"), + } + + err := n.ValidateTransaction(tx) + require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) + }) + t.Run("relayed tx v3, no funds for sender", func(t *testing.T) { + tx := &transaction.Transaction{ + SndAddr: bytes.Repeat([]byte("1"), 32), + RcvAddr: bytes.Repeat([]byte("1"), 32), + Value: big.NewInt(37), + Signature: []byte("sSignature"), + RelayerAddr: bytes.Repeat([]byte("2"), 32), + RelayerSignature: []byte("rSignature"), + ChainID: []byte("chainID"), + } + err := n.ValidateTransaction(tx) + require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) + }) + t.Run("relayed tx v3, no funds for relayer", func(t *testing.T) { + tx := &transaction.Transaction{ + SndAddr: bytes.Repeat([]byte("1"), 32), + RcvAddr: bytes.Repeat([]byte("1"), 32), + Value: big.NewInt(37), + Signature: []byte("sSignature"), + RelayerAddr: bytes.Repeat([]byte("2"), 32), + RelayerSignature: []byte("rSignature"), + ChainID: []byte("chainID"), + } + + stateComp := getDefaultStateComponents() + stateComp.AccountsAPI = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(addressContainer []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(addressContainer, tx.SndAddr) { + return &stateMock.UserAccountStub{}, nil + } + + return nil, errors.New("account not found") + }, + } + nLocal, _ := node.NewNode( + node.WithCoreComponents(getDefaultCoreComponents()), + node.WithBootstrapComponents(getDefaultBootstrapComponents()), + node.WithProcessComponents(getDefaultProcessComponents()), + node.WithStateComponents(stateComp), + node.WithCryptoComponents(getDefaultCryptoComponents()), + ) + + err := nLocal.ValidateTransaction(tx) + require.Equal(t, "insufficient funds for address erd1xgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeqvw86cj", err.Error()) + }) } func TestCreateShardedStores_NilShardCoordinatorShouldError(t *testing.T) { @@ -3537,6 +3620,54 @@ func TestNode_GetAccountAccountWithKeysShouldWork(t *testing.T) { require.Equal(t, hex.EncodeToString(v2), recovAccnt.Pairs[hex.EncodeToString(k2)]) } +func TestNode_GetAccountAccountWithKeysNeverUsedAccountShouldWork(t *testing.T) { + t.Parallel() + + accDB := &stateMock.AccountsStub{ + GetAccountWithBlockInfoCalled: func(address []byte, options common.RootHashHolder) (vmcommon.AccountHandler, common.BlockInfo, error) { + return nil, nil, nil + }, + RecreateTrieCalled: func(options common.RootHashHolder) error { + return nil + }, + } + + n := getNodeWithAccount(accDB) + + recovAccnt, blockInfo, err := n.GetAccountWithKeys(testscommon.TestAddressBob, api.AccountQueryOptions{WithKeys: true}, context.Background()) + + require.Nil(t, err) + require.Equal(t, uint64(0), recovAccnt.Nonce) + require.Equal(t, testscommon.TestAddressBob, recovAccnt.Address) + require.Equal(t, api.BlockInfo{}, blockInfo) +} + +func TestNode_GetAccountAccountWithKeysNilDataTrieShouldWork(t *testing.T) { + t.Parallel() + + accnt := createAcc(testscommon.TestPubKeyBob) + accnt.SetDataTrie(nil) + _ = accnt.AddToBalance(big.NewInt(1)) + + accDB := &stateMock.AccountsStub{ + GetAccountWithBlockInfoCalled: func(address []byte, options common.RootHashHolder) (vmcommon.AccountHandler, common.BlockInfo, error) { + return accnt, nil, nil + }, + RecreateTrieCalled: func(options common.RootHashHolder) error { + return nil + }, + } + + n := getNodeWithAccount(accDB) + + recovAccnt, blockInfo, err := n.GetAccountWithKeys(testscommon.TestAddressBob, api.AccountQueryOptions{WithKeys: true}, context.Background()) + + require.Nil(t, err) + require.Equal(t, uint64(0), recovAccnt.Nonce) + require.Equal(t, testscommon.TestAddressBob, recovAccnt.Address) + require.Equal(t, api.BlockInfo{}, blockInfo) +} + func getNodeWithAccount(accDB *stateMock.AccountsStub) *node.Node { coreComponents := getDefaultCoreComponents() dataComponents := getDefaultDataComponents() @@ -5197,18 +5328,19 @@ func getDefaultCoreComponents() *nodeMockFactory.CoreComponentsMock { MinTransactionVersionCalled: func() uint32 { return 1 }, - WDTimer: &testscommon.WatchdogMock{}, - Alarm: &testscommon.AlarmSchedulerStub{}, - NtpTimer: &testscommon.SyncTimerStub{}, - RoundHandlerField: &testscommon.RoundHandlerMock{}, - EconomicsHandler: &economicsmocks.EconomicsHandlerMock{}, - APIEconomicsHandler: &economicsmocks.EconomicsHandlerMock{}, - RatingsConfig: &testscommon.RatingsInfoMock{}, - RatingHandler: &testscommon.RaterMock{}, - NodesConfig: &genesisMocks.NodesSetupStub{}, - StartTime: time.Time{}, - EpochChangeNotifier: &epochNotifier.EpochNotifierStub{}, - TxVersionCheckHandler: versioning.NewTxVersionChecker(0), + WDTimer: &testscommon.WatchdogMock{}, + Alarm: &testscommon.AlarmSchedulerStub{}, + NtpTimer: &testscommon.SyncTimerStub{}, + RoundHandlerField: &testscommon.RoundHandlerMock{}, + EconomicsHandler: &economicsmocks.EconomicsHandlerMock{}, + APIEconomicsHandler: &economicsmocks.EconomicsHandlerMock{}, + RatingsConfig: &testscommon.RatingsInfoMock{}, + RatingHandler: &testscommon.RaterMock{}, + NodesConfig: &genesisMocks.NodesSetupStub{}, + StartTime: time.Time{}, + EpochChangeNotifier: &epochNotifier.EpochNotifierStub{}, + TxVersionCheckHandler: versioning.NewTxVersionChecker(0), + EnableEpochsHandlerField: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), } } diff --git a/outport/factory/hostDriverFactory_test.go b/outport/factory/hostDriverFactory_test.go index d41079c0f55..d005ea4ba74 100644 --- a/outport/factory/hostDriverFactory_test.go +++ b/outport/factory/hostDriverFactory_test.go @@ -15,7 +15,7 @@ func TestCreateHostDriver(t *testing.T) { args := ArgsHostDriverFactory{ HostConfig: config.HostDriversConfig{ - URL: "localhost", + URL: "ws://localhost", RetryDurationInSec: 1, MarshallerType: "json", Mode: data.ModeClient, diff --git a/outport/factory/outportFactory_test.go b/outport/factory/outportFactory_test.go index 93b4657427b..94d8c38839c 100644 --- a/outport/factory/outportFactory_test.go +++ b/outport/factory/outportFactory_test.go @@ -122,7 +122,7 @@ func TestCreateOutport_SubscribeMultipleHostDrivers(t *testing.T) { Marshaller: &testscommon.MarshalizerMock{}, HostConfig: config.HostDriversConfig{ Enabled: true, - URL: "localhost", + URL: "ws://localhost", RetryDurationInSec: 1, MarshallerType: "json", Mode: data.ModeClient, @@ -132,7 +132,7 @@ func TestCreateOutport_SubscribeMultipleHostDrivers(t *testing.T) { Marshaller: &testscommon.MarshalizerMock{}, HostConfig: config.HostDriversConfig{ Enabled: false, - URL: "localhost", + URL: "ws://localhost", RetryDurationInSec: 1, MarshallerType: "json", Mode: data.ModeClient, @@ -142,7 +142,7 @@ func TestCreateOutport_SubscribeMultipleHostDrivers(t *testing.T) { Marshaller: &testscommon.MarshalizerMock{}, HostConfig: config.HostDriversConfig{ Enabled: true, - URL: "localhost", + URL: "ws://localhost", RetryDurationInSec: 1, MarshallerType: "json", Mode: data.ModeClient, diff --git a/outport/mock/economicsDataMock.go b/outport/mock/economicsDataMock.go index cf9cf4dc848..36da6e9a7d4 100644 --- a/outport/mock/economicsDataMock.go +++ b/outport/mock/economicsDataMock.go @@ -20,6 +20,11 @@ const ( type EconomicsHandlerMock struct { } +// ComputeGasUnitsFromRefundValue - +func (e *EconomicsHandlerMock) ComputeGasUnitsFromRefundValue(_ coreData.TransactionWithFeeHandler, _ *big.Int, _ uint32) uint64 { + return 0 +} + // MaxGasLimitPerBlock - func (e *EconomicsHandlerMock) MaxGasLimitPerBlock(_ uint32) uint64 { return 0 diff --git a/outport/process/alteredaccounts/tokensProcessor.go b/outport/process/alteredaccounts/tokensProcessor.go index bb0839ef44a..672a29fc7af 100644 --- a/outport/process/alteredaccounts/tokensProcessor.go +++ b/outport/process/alteredaccounts/tokensProcessor.go @@ -7,6 +7,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" outportcore "github.com/multiversx/mx-chain-core-go/data/outport" "github.com/multiversx/mx-chain-go/sharding" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) const ( @@ -116,7 +117,7 @@ func (tp *tokensProcessor) processMultiTransferEvent(event data.EventHandler, ma // N = len(topics) // i := 0; i < N-1; i+=3 // { - // topics[i] --- token identifier + // topics[i] --- token identifier or EGLD token identifier // topics[i+1] --- token nonce // topics[i+2] --- transferred value // } @@ -133,6 +134,12 @@ func (tp *tokensProcessor) processMultiTransferEvent(event data.EventHandler, ma for i := 0; i < numOfTopics-1; i += 3 { tokenID := topics[i] nonceBigInt := big.NewInt(0).SetBytes(topics[i+1]) + + if string(tokenID) == vmcommon.EGLDIdentifier { + tp.processNativeEGLDTransferWithMultiTransfer(destinationAddress, markedAlteredAccounts) + continue + } + // process event for the sender address tp.processEsdtDataForAddress(address, nonceBigInt, string(tokenID), markedAlteredAccounts, false) @@ -177,6 +184,24 @@ func (tp *tokensProcessor) processEsdtDataForAddress( } } +func (tp *tokensProcessor) processNativeEGLDTransferWithMultiTransfer(address []byte, markedAlteredAccounts map[string]*markedAlteredAccount) { + if !tp.isSameShard(address) { + return + } + + addressStr := string(address) + _, addressAlreadySelected := markedAlteredAccounts[addressStr] + if addressAlreadySelected { + markedAlteredAccounts[addressStr].balanceChanged = true + return + } + + markedAlteredAccounts[addressStr] = &markedAlteredAccount{ + balanceChanged: true, + } + +} + func (tp *tokensProcessor) isSameShard(address []byte) bool { return tp.shardCoordinator.SelfId() == tp.shardCoordinator.ComputeId(address) } diff --git a/outport/process/alteredaccounts/tokensProcessor_test.go b/outport/process/alteredaccounts/tokensProcessor_test.go index a7a6a65af96..2ac172b88bf 100644 --- a/outport/process/alteredaccounts/tokensProcessor_test.go +++ b/outport/process/alteredaccounts/tokensProcessor_test.go @@ -7,6 +7,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/process/mock" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" ) @@ -61,3 +62,81 @@ func TestTokenProcessorProcessEventMultiTransferV2(t *testing.T) { require.Equal(t, markedAccount, markedAccounts["addr"]) require.Equal(t, markedAccount, markedAccounts["receiver"]) } + +func TestTokenProcessorProcessEventMultiTransferV2WithEGLD(t *testing.T) { + t.Parallel() + + tp := newTokensProcessor(&mock.ShardCoordinatorStub{}) + + markedAccounts := make(map[string]*markedAlteredAccount) + tp.processEvent(&transaction.Event{ + Identifier: []byte(core.BuiltInFunctionMultiESDTNFTTransfer), + Address: []byte("addr"), + Topics: [][]byte{[]byte("token1"), big.NewInt(0).Bytes(), []byte("2"), []byte(vmcommon.EGLDIdentifier), big.NewInt(0).Bytes(), []byte("3"), []byte("receiver")}, + }, markedAccounts) + + require.Equal(t, 2, len(markedAccounts)) + markedAccount1 := &markedAlteredAccount{ + tokens: map[string]*markedAlteredAccountToken{ + "token1": { + identifier: "token1", + nonce: 0, + }, + }, + } + require.Equal(t, markedAccount1, markedAccounts["addr"]) + + markedAccount2 := &markedAlteredAccount{ + balanceChanged: true, + tokens: map[string]*markedAlteredAccountToken{ + "token1": { + identifier: "token1", + nonce: 0, + }, + }, + } + require.Equal(t, markedAccount2, markedAccounts["receiver"]) +} + +func TestTokenProcessorProcessEventMultiTransferV2WithEGLDAndMoreTokens(t *testing.T) { + t.Parallel() + + tp := newTokensProcessor(&mock.ShardCoordinatorStub{}) + + markedAccounts := make(map[string]*markedAlteredAccount) + tp.processEvent(&transaction.Event{ + Identifier: []byte(core.BuiltInFunctionMultiESDTNFTTransfer), + Address: []byte("addr"), + Topics: [][]byte{[]byte("token1"), big.NewInt(0).Bytes(), []byte("2"), []byte(vmcommon.EGLDIdentifier), big.NewInt(0).Bytes(), []byte("3"), []byte("token2"), big.NewInt(0).Bytes(), []byte("2"), []byte("receiver")}, + }, markedAccounts) + + require.Equal(t, 2, len(markedAccounts)) + markedAccount1 := &markedAlteredAccount{ + tokens: map[string]*markedAlteredAccountToken{ + "token1": { + identifier: "token1", + nonce: 0, + }, + "token2": { + identifier: "token2", + nonce: 0, + }, + }, + } + require.Equal(t, markedAccount1, markedAccounts["addr"]) + + markedAccount2 := &markedAlteredAccount{ + balanceChanged: true, + tokens: map[string]*markedAlteredAccountToken{ + "token1": { + identifier: "token1", + nonce: 0, + }, + "token2": { + identifier: "token2", + nonce: 0, + }, + }, + } + require.Equal(t, markedAccount2, markedAccounts["receiver"]) +} diff --git a/outport/process/factory/outportDataProviderFactory.go b/outport/process/factory/outportDataProviderFactory.go index 68546df1c50..5bb2c698136 100644 --- a/outport/process/factory/outportDataProviderFactory.go +++ b/outport/process/factory/outportDataProviderFactory.go @@ -11,6 +11,7 @@ import ( "github.com/multiversx/mx-chain-go/outport/process/disabled" "github.com/multiversx/mx-chain-go/outport/process/transactionsfee" processTxs "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/smartContract" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/state" @@ -60,11 +61,13 @@ func CreateOutportDataProvider(arg ArgOutportDataProviderFactory) (outport.DataP } transactionsFeeProc, err := transactionsfee.NewTransactionsFeeProcessor(transactionsfee.ArgTransactionsFeeProcessor{ - Marshaller: arg.Marshaller, - TransactionsStorer: arg.TransactionsStorer, - ShardCoordinator: arg.ShardCoordinator, - TxFeeCalculator: arg.EconomicsData, - PubKeyConverter: arg.AddressConverter, + Marshaller: arg.Marshaller, + TransactionsStorer: arg.TransactionsStorer, + ShardCoordinator: arg.ShardCoordinator, + TxFeeCalculator: arg.EconomicsData, + PubKeyConverter: arg.AddressConverter, + ArgsParser: smartContract.NewArgumentParser(), + EnableEpochsHandler: arg.EnableEpochsHandler, }) if err != nil { return nil, err diff --git a/outport/process/interface.go b/outport/process/interface.go index 5fcb19020f3..0c8b332aa31 100644 --- a/outport/process/interface.go +++ b/outport/process/interface.go @@ -17,7 +17,7 @@ type AlteredAccountsProviderHandler interface { // TransactionsFeeHandler defines the functionality needed for computation of the transaction fee and gas used type TransactionsFeeHandler interface { - PutFeeAndGasUsed(pool *outport.TransactionPool) error + PutFeeAndGasUsed(pool *outport.TransactionPool, epoch uint32) error IsInterfaceNil() bool } @@ -32,6 +32,7 @@ type GasConsumedProvider interface { // EconomicsDataHandler defines the functionality needed for economics data type EconomicsDataHandler interface { + ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 ComputeGasUsedAndFeeBasedOnRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsed(tx data.TransactionWithFeeHandler, gasUsed uint64) *big.Int ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int diff --git a/outport/process/outportDataProvider.go b/outport/process/outportDataProvider.go index 3c80b1db990..3d1fded1637 100644 --- a/outport/process/outportDataProvider.go +++ b/outport/process/outportDataProvider.go @@ -101,7 +101,7 @@ func (odp *outportDataProvider) PrepareOutportSaveBlockData(arg ArgPrepareOutpor return nil, err } - err = odp.transactionsFeeProcessor.PutFeeAndGasUsed(pool) + err = odp.transactionsFeeProcessor.PutFeeAndGasUsed(pool, arg.Header.GetEpoch()) if err != nil { return nil, fmt.Errorf("transactionsFeeProcessor.PutFeeAndGasUsed %w", err) } diff --git a/outport/process/outportDataProvider_test.go b/outport/process/outportDataProvider_test.go index 3b048eadf8e..97ac42e0443 100644 --- a/outport/process/outportDataProvider_test.go +++ b/outport/process/outportDataProvider_test.go @@ -18,6 +18,7 @@ import ( "github.com/multiversx/mx-chain-go/outport/process/transactionsfee" "github.com/multiversx/mx-chain-go/testscommon" commonMocks "github.com/multiversx/mx-chain-go/testscommon/common" + "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" @@ -26,10 +27,12 @@ import ( func createArgOutportDataProvider() ArgOutportDataProvider { txsFeeProc, _ := transactionsfee.NewTransactionsFeeProcessor(transactionsfee.ArgTransactionsFeeProcessor{ - Marshaller: &marshallerMock.MarshalizerMock{}, - TransactionsStorer: &genericMocks.StorerMock{}, - ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, - TxFeeCalculator: &mock.EconomicsHandlerMock{}, + Marshaller: &marshallerMock.MarshalizerMock{}, + TransactionsStorer: &genericMocks.StorerMock{}, + ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, + TxFeeCalculator: &mock.EconomicsHandlerMock{}, + ArgsParser: &testscommon.ArgumentParserMock{}, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), }) return ArgOutportDataProvider{ diff --git a/outport/process/transactionsfee/interface.go b/outport/process/transactionsfee/interface.go index 53042467442..551ee59d1e2 100644 --- a/outport/process/transactionsfee/interface.go +++ b/outport/process/transactionsfee/interface.go @@ -11,6 +11,7 @@ import ( // FeesProcessorHandler defines the interface for the transaction fees processor type FeesProcessorHandler interface { ComputeGasUsedAndFeeBasedOnRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int) (uint64, *big.Int) + ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 ComputeTxFeeBasedOnGasUsed(tx data.TransactionWithFeeHandler, gasUsed uint64) *big.Int ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int ComputeGasLimit(tx data.TransactionWithFeeHandler) uint64 diff --git a/outport/process/transactionsfee/transactionChecker.go b/outport/process/transactionsfee/transactionChecker.go index 546fdd9f432..28c2658a9a4 100644 --- a/outport/process/transactionsfee/transactionChecker.go +++ b/outport/process/transactionsfee/transactionChecker.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" + "github.com/multiversx/mx-chain-go/common" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -47,11 +48,25 @@ func isSCRForSenderWithRefund(scr *smartContractResult.SmartContractResult, txHa } func isRefundForRelayed(dbScResult *smartContractResult.SmartContractResult, tx data.TransactionHandler) bool { + isRelayedV3 := common.IsRelayedTxV3(tx) isForRelayed := string(dbScResult.ReturnMessage) == core.GasRefundForRelayerMessage isForSender := bytes.Equal(dbScResult.RcvAddr, tx.GetSndAddr()) + isForRelayerV3 := isForRelayerOfV3(dbScResult, tx) differentHash := !bytes.Equal(dbScResult.OriginalTxHash, dbScResult.PrevTxHash) - return isForRelayed && isForSender && differentHash + isRefundForRelayedV1V2 := isForRelayed && isForSender && differentHash && !isRelayedV3 + isRefundForRelayedV3 := isForRelayed && isForRelayerV3 && isRelayedV3 + + return isRefundForRelayedV1V2 || isRefundForRelayedV3 +} + +func isForRelayerOfV3(scr *smartContractResult.SmartContractResult, tx data.TransactionHandler) bool { + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if !isRelayedV3 { + return false + } + + return bytes.Equal(relayedTx.GetRelayerAddr(), scr.RcvAddr) } func isDataOk(data []byte) bool { diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index c77956f5365..6cbfb0ddbcf 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -6,9 +6,13 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" outportcore "github.com/multiversx/mx-chain-core-go/data/outport" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" + "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/storage" logger "github.com/multiversx/mx-chain-logger-go" @@ -19,19 +23,24 @@ const loggerName = "outport/process/transactionsfee" // ArgTransactionsFeeProcessor holds the arguments needed for creating a new instance of transactionsFeeProcessor type ArgTransactionsFeeProcessor struct { - Marshaller marshal.Marshalizer - TransactionsStorer storage.Storer - ShardCoordinator sharding.Coordinator - TxFeeCalculator FeesProcessorHandler - PubKeyConverter core.PubkeyConverter + Marshaller marshal.Marshalizer + TransactionsStorer storage.Storer + ShardCoordinator sharding.Coordinator + TxFeeCalculator FeesProcessorHandler + PubKeyConverter core.PubkeyConverter + ArgsParser process.ArgumentsParser + EnableEpochsHandler common.EnableEpochsHandler } type transactionsFeeProcessor struct { - txGetter transactionGetter - txFeeCalculator FeesProcessorHandler - shardCoordinator sharding.Coordinator - dataFieldParser dataFieldParser - log logger.Logger + txGetter transactionGetter + txFeeCalculator FeesProcessorHandler + shardCoordinator sharding.Coordinator + dataFieldParser dataFieldParser + log logger.Logger + marshaller marshal.Marshalizer + argsParser process.ArgumentsParser + enableEpochsHandler common.EnableEpochsHandler } // NewTransactionsFeeProcessor will create a new instance of transactionsFeeProcessor @@ -50,11 +59,14 @@ func NewTransactionsFeeProcessor(arg ArgTransactionsFeeProcessor) (*transactions } return &transactionsFeeProcessor{ - txFeeCalculator: arg.TxFeeCalculator, - shardCoordinator: arg.ShardCoordinator, - txGetter: newTxGetter(arg.TransactionsStorer, arg.Marshaller), - log: logger.GetOrCreate(loggerName), - dataFieldParser: parser, + txFeeCalculator: arg.TxFeeCalculator, + shardCoordinator: arg.ShardCoordinator, + txGetter: newTxGetter(arg.TransactionsStorer, arg.Marshaller), + log: logger.GetOrCreate(loggerName), + dataFieldParser: parser, + marshaller: arg.Marshaller, + argsParser: arg.ArgsParser, + enableEpochsHandler: arg.EnableEpochsHandler, }, nil } @@ -74,18 +86,24 @@ func checkArg(arg ArgTransactionsFeeProcessor) error { if check.IfNil(arg.PubKeyConverter) { return core.ErrNilPubkeyConverter } + if check.IfNil(arg.ArgsParser) { + return process.ErrNilArgumentParser + } + if check.IfNil(arg.EnableEpochsHandler) { + return process.ErrNilEnableEpochsHandler + } return nil } // PutFeeAndGasUsed will compute and set in transactions pool fee and gas used -func (tep *transactionsFeeProcessor) PutFeeAndGasUsed(pool *outportcore.TransactionPool) error { +func (tep *transactionsFeeProcessor) PutFeeAndGasUsed(pool *outportcore.TransactionPool, epoch uint32) error { tep.prepareInvalidTxs(pool) txsWithResultsMap := prepareTransactionsAndScrs(pool) - tep.prepareNormalTxs(txsWithResultsMap) + tep.prepareNormalTxs(txsWithResultsMap, epoch) - return tep.prepareScrsNoTx(txsWithResultsMap) + return tep.prepareScrsNoTx(txsWithResultsMap, epoch) } func (tep *transactionsFeeProcessor) prepareInvalidTxs(pool *outportcore.TransactionPool) { @@ -97,7 +115,7 @@ func (tep *transactionsFeeProcessor) prepareInvalidTxs(pool *outportcore.Transac } } -func (tep *transactionsFeeProcessor) prepareNormalTxs(transactionsAndScrs *transactionsAndScrsHolder) { +func (tep *transactionsFeeProcessor) prepareNormalTxs(transactionsAndScrs *transactionsAndScrsHolder, epoch uint32) { for txHashHex, txWithResult := range transactionsAndScrs.txsWithResults { txHandler := txWithResult.GetTxHandler() @@ -110,17 +128,34 @@ func (tep *transactionsFeeProcessor) prepareNormalTxs(transactionsAndScrs *trans feeInfo.SetFee(fee) feeInfo.SetInitialPaidFee(initialPaidFee) - if isRelayedTx(txWithResult) || tep.isESDTOperationWithSCCall(txHandler) { + isRelayed := isRelayedTx(txWithResult) + isFeeFixActive := tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) + isRelayedBeforeFix := isRelayed && !isFeeFixActive + if isRelayedBeforeFix || tep.isESDTOperationWithSCCall(txHandler) { feeInfo.SetGasUsed(txWithResult.GetTxHandler().GetGasLimit()) feeInfo.SetFee(initialPaidFee) } - tep.prepareTxWithResults(txHashHex, txWithResult) + userTx, totalFee, isRelayedV1V2 := tep.getFeeOfRelayedV1V2(txWithResult) + isRelayedAfterFix := isRelayedV1V2 && isFeeFixActive + if isRelayedAfterFix { + feeInfo.SetFee(totalFee) + feeInfo.SetInitialPaidFee(totalFee) + feeInfo.SetGasUsed(big.NewInt(0).Div(totalFee, big.NewInt(0).SetUint64(txHandler.GetGasPrice())).Uint64()) + } + + tep.prepareTxWithResults(txHashHex, txWithResult, userTx, epoch) } } -func (tep *transactionsFeeProcessor) prepareTxWithResults(txHashHex string, txWithResults *transactionWithResults) { +func (tep *transactionsFeeProcessor) prepareTxWithResults( + txHashHex string, + txWithResults *transactionWithResults, + userTx data.TransactionHandler, + epoch uint32, +) { hasRefund := false + totalRefunds := big.NewInt(0) for _, scrHandler := range txWithResults.scrs { scr, ok := scrHandler.GetTxHandler().(*smartContractResult.SmartContractResult) if !ok { @@ -128,23 +163,91 @@ func (tep *transactionsFeeProcessor) prepareTxWithResults(txHashHex string, txWi } if isSCRForSenderWithRefund(scr, txHashHex, txWithResults.GetTxHandler()) || isRefundForRelayed(scr, txWithResults.GetTxHandler()) { - gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(txWithResults.GetTxHandler(), scr.Value) - - txWithResults.GetFeeInfo().SetGasUsed(gasUsed) - txWithResults.GetFeeInfo().SetFee(fee) hasRefund = true - break + totalRefunds.Add(totalRefunds, scr.Value) } } - tep.prepareTxWithResultsBasedOnLogs(txHashHex, txWithResults, hasRefund) + if totalRefunds.Cmp(big.NewInt(0)) > 0 { + tep.setGasUsedAndFeeBasedOnRefundValue(txWithResults, userTx, totalRefunds, epoch) + + } + + tep.prepareTxWithResultsBasedOnLogs(txHashHex, txWithResults, userTx, hasRefund, epoch) +} + +func (tep *transactionsFeeProcessor) getFeeOfRelayedV1V2(tx *transactionWithResults) (data.TransactionHandler, *big.Int, bool) { + if common.IsValidRelayedTxV3(tx.GetTxHandler()) { + return nil, nil, false + } + + if len(tx.GetTxHandler().GetData()) == 0 { + return nil, nil, false + } + + funcName, args, err := tep.argsParser.ParseCallData(string(tx.GetTxHandler().GetData())) + if err != nil { + return nil, nil, false + } + + if funcName == core.RelayedTransaction { + return tep.handleRelayedV1(args, tx) + } + + if funcName == core.RelayedTransactionV2 { + return tep.handleRelayedV2(args, tx) + } + + return nil, nil, false +} + +func (tep *transactionsFeeProcessor) handleRelayedV1(args [][]byte, tx *transactionWithResults) (data.TransactionHandler, *big.Int, bool) { + if len(args) != 1 { + return nil, nil, false + } + + innerTx := &transaction.Transaction{} + err := tep.marshaller.Unmarshal(innerTx, args[0]) + if err != nil { + return nil, nil, false + } + + txHandler := tx.GetTxHandler() + gasUsed := tep.txFeeCalculator.ComputeGasLimit(txHandler) + fee := tep.txFeeCalculator.ComputeTxFeeBasedOnGasUsed(txHandler, gasUsed) + + innerFee := tep.txFeeCalculator.ComputeTxFee(innerTx) + + return innerTx, big.NewInt(0).Add(fee, innerFee), true +} + +func (tep *transactionsFeeProcessor) handleRelayedV2(args [][]byte, tx *transactionWithResults) (data.TransactionHandler, *big.Int, bool) { + txHandler := tx.GetTxHandler() + + innerTx := &transaction.Transaction{} + innerTx.RcvAddr = args[0] + innerTx.Nonce = big.NewInt(0).SetBytes(args[1]).Uint64() + innerTx.Data = args[2] + innerTx.Signature = args[3] + innerTx.Value = big.NewInt(0) + innerTx.GasPrice = txHandler.GetGasPrice() + innerTx.GasLimit = txHandler.GetGasLimit() - tep.txFeeCalculator.ComputeGasLimit(txHandler) + innerTx.SndAddr = txHandler.GetRcvAddr() + + gasUsed := tep.txFeeCalculator.ComputeGasLimit(txHandler) + fee := tep.txFeeCalculator.ComputeTxFeeBasedOnGasUsed(txHandler, gasUsed) + + innerFee := tep.txFeeCalculator.ComputeTxFee(innerTx) + return innerTx, big.NewInt(0).Add(fee, innerFee), true } func (tep *transactionsFeeProcessor) prepareTxWithResultsBasedOnLogs( txHashHex string, txWithResults *transactionWithResults, + userTx data.TransactionHandler, hasRefund bool, + epoch uint32, ) { tx := txWithResults.GetTxHandler() if check.IfNil(tx) { @@ -159,10 +262,7 @@ func (tep *transactionsFeeProcessor) prepareTxWithResultsBasedOnLogs( for _, event := range txWithResults.log.GetLogEvents() { if core.WriteLogIdentifier == string(event.GetIdentifier()) && !hasRefund { - gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(txWithResults.GetTxHandler(), big.NewInt(0)) - txWithResults.GetFeeInfo().SetGasUsed(gasUsed) - txWithResults.GetFeeInfo().SetFee(fee) - + tep.setGasUsedAndFeeBasedOnRefundValue(txWithResults, userTx, big.NewInt(0), epoch) continue } if core.SignalErrorOperation == string(event.GetIdentifier()) { @@ -171,10 +271,36 @@ func (tep *transactionsFeeProcessor) prepareTxWithResultsBasedOnLogs( txWithResults.GetFeeInfo().SetFee(fee) } } +} + +func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( + txWithResults *transactionWithResults, + userTx data.TransactionHandler, + refund *big.Int, + epoch uint32, +) { + txWithResults.GetFeeInfo().SetHadRefund() + + isValidUserTxAfterBaseCostActivation := !check.IfNil(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) + if isValidUserTxAfterBaseCostActivation && !common.IsValidRelayedTxV3(txWithResults.GetTxHandler()) { + gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) + + tx := txWithResults.GetTxHandler() + gasUsedRelayedTx := tep.txFeeCalculator.ComputeGasLimit(tx) + feeRelayedTx := tep.txFeeCalculator.ComputeTxFeeBasedOnGasUsed(tx, gasUsedRelayedTx) + + txWithResults.GetFeeInfo().SetGasUsed(gasUsed + gasUsedRelayedTx) + txWithResults.GetFeeInfo().SetFee(fee.Add(fee, feeRelayedTx)) + + return + } + gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(txWithResults.GetTxHandler(), refund) + txWithResults.GetFeeInfo().SetGasUsed(gasUsed) + txWithResults.GetFeeInfo().SetFee(fee) } -func (tep *transactionsFeeProcessor) prepareScrsNoTx(transactionsAndScrs *transactionsAndScrsHolder) error { +func (tep *transactionsFeeProcessor) prepareScrsNoTx(transactionsAndScrs *transactionsAndScrsHolder, epoch uint32) error { for _, scrHandler := range transactionsAndScrs.scrsNoTx { scr, ok := scrHandler.GetTxHandler().(*smartContractResult.SmartContractResult) if !ok { @@ -196,15 +322,80 @@ func (tep *transactionsFeeProcessor) prepareScrsNoTx(transactionsAndScrs *transa continue } + isRelayedV3 := common.IsValidRelayedTxV3(txFromStorage) isForInitialTxSender := bytes.Equal(scr.RcvAddr, txFromStorage.SndAddr) - if !isForInitialTxSender { + isForRelayerV3 := bytes.Equal(scr.RcvAddr, txFromStorage.RelayerAddr) + shouldSkipRelayedV3 := isRelayedV3 && !isForRelayerV3 + shouldSkipTx := !isRelayedV3 && !isForInitialTxSender || shouldSkipRelayedV3 + if shouldSkipTx { continue } - gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(txFromStorage, scr.Value) + userTx := tep.getUserTxOfRelayed(txFromStorage) + if check.IfNil(userTx) { + // relayed v3 and other txs + if isRelayedV3 { + gasUnits := tep.txFeeCalculator.ComputeGasUnitsFromRefundValue(txFromStorage, scr.Value, epoch) + scrHandler.GetFeeInfo().SetGasRefunded(gasUnits) + scrHandler.GetFeeInfo().SetFee(scr.Value) + continue + } + + gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(txFromStorage, scr.Value) + + scrHandler.GetFeeInfo().SetGasUsed(gasUsed) + scrHandler.GetFeeInfo().SetFee(fee) + } else { + // relayed v1 and v2 + gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, scr.Value) + + gasUsedRelayedTx := tep.txFeeCalculator.ComputeGasLimit(txFromStorage) + feeRelayedTx := tep.txFeeCalculator.ComputeTxFeeBasedOnGasUsed(txFromStorage, gasUsedRelayedTx) + + scrHandler.GetFeeInfo().SetGasUsed(gasUsed + gasUsedRelayedTx) + scrHandler.GetFeeInfo().SetFee(fee.Add(fee, feeRelayedTx)) + } + } + + return nil +} + +func (tep *transactionsFeeProcessor) getUserTxOfRelayed(tx data.TransactionHandler) data.TransactionHandler { + if len(tx.GetData()) == 0 { + return nil + } + + funcName, args, err := tep.argsParser.ParseCallData(string(tx.GetData())) + if err != nil { + return nil + } + + if funcName == core.RelayedTransaction { + if len(args) != 1 { + return nil + } + + userTx := &transaction.Transaction{} + err := tep.marshaller.Unmarshal(userTx, args[0]) + if err != nil { + return nil + } + + return userTx + } - scrHandler.GetFeeInfo().SetGasUsed(gasUsed) - scrHandler.GetFeeInfo().SetFee(fee) + if funcName == core.RelayedTransactionV2 { + userTx := &transaction.Transaction{} + userTx.RcvAddr = args[0] + userTx.Nonce = big.NewInt(0).SetBytes(args[1]).Uint64() + userTx.Data = args[2] + userTx.Signature = args[3] + userTx.Value = big.NewInt(0) + userTx.GasPrice = tx.GetGasPrice() + userTx.GasLimit = tx.GetGasLimit() - tep.txFeeCalculator.ComputeGasLimit(tx) + userTx.SndAddr = tx.GetRcvAddr() + + return userTx } return nil diff --git a/outport/process/transactionsfee/transactionsFeeProcessor_test.go b/outport/process/transactionsfee/transactionsFeeProcessor_test.go index 8ff4cf14501..2aa399a26fe 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor_test.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor_test.go @@ -10,8 +10,13 @@ import ( outportcore "github.com/multiversx/mx-chain-core-go/data/outport" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/outport/mock" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" + "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" logger "github.com/multiversx/mx-chain-logger-go" @@ -20,13 +25,27 @@ import ( var pubKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") +func createEconomicsData(enableEpochsHandler common.EnableEpochsHandler) process.EconomicsDataHandler { + economicsConfig := testscommon.GetEconomicsConfig() + economicsData, _ := economics.NewEconomicsData(economics.ArgsNewEconomicsData{ + Economics: &economicsConfig, + EnableEpochsHandler: enableEpochsHandler, + TxVersionChecker: &testscommon.TxVersionCheckerStub{}, + EpochNotifier: &epochNotifier.EpochNotifierStub{}, + }) + + return economicsData +} + func prepareMockArg() ArgTransactionsFeeProcessor { return ArgTransactionsFeeProcessor{ - Marshaller: marshallerMock.MarshalizerMock{}, - TransactionsStorer: genericMocks.NewStorerMock(), - ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, - TxFeeCalculator: &mock.EconomicsHandlerMock{}, - PubKeyConverter: pubKeyConverter, + Marshaller: marshallerMock.MarshalizerMock{}, + TransactionsStorer: genericMocks.NewStorerMock(), + ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, + TxFeeCalculator: &mock.EconomicsHandlerMock{}, + PubKeyConverter: pubKeyConverter, + ArgsParser: &testscommon.ArgumentParserMock{}, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), } } @@ -53,6 +72,16 @@ func TestNewTransactionFeeProcessor(t *testing.T) { _, err = NewTransactionsFeeProcessor(arg) require.Equal(t, ErrNilTransactionFeeCalculator, err) + arg = prepareMockArg() + arg.ArgsParser = nil + _, err = NewTransactionsFeeProcessor(arg) + require.Equal(t, process.ErrNilArgumentParser, err) + + arg = prepareMockArg() + arg.EnableEpochsHandler = nil + _, err = NewTransactionsFeeProcessor(arg) + require.Equal(t, process.ErrNilEnableEpochsHandler, err) + arg = prepareMockArg() txsFeeProc, err := NewTransactionsFeeProcessor(arg) require.NotNil(t, txsFeeProc) @@ -125,7 +154,7 @@ func TestPutFeeAndGasUsedTx1(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(1673728170000000), initialTx.GetFeeInfo().GetFee()) require.Equal(t, uint64(7982817), initialTx.GetFeeInfo().GetGasUsed()) @@ -175,7 +204,7 @@ func TestPutFeeAndGasUsedScrNoTx(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(123001460000000), scr.GetFeeInfo().GetFee()) require.Equal(t, uint64(7350146), scr.GetFeeInfo().GetGasUsed()) @@ -203,7 +232,7 @@ func TestPutFeeAndGasUsedInvalidTxs(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(349500000000000), tx.GetFeeInfo().GetFee()) require.Equal(t, tx.GetTxHandler().GetGasLimit(), tx.GetFeeInfo().GetGasUsed()) @@ -284,7 +313,7 @@ func TestPutFeeAndGasUsedLogWithErrorAndInformative(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, tx1.GetTxHandler().GetGasLimit(), tx1.GetFeeInfo().GetGasUsed()) @@ -335,7 +364,7 @@ func TestPutFeeAndGasUsedWrongRelayedTx(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(6103405000000000), initialTx.GetFeeInfo().GetFee()) require.Equal(t, uint64(550000000), initialTx.GetFeeInfo().GetGasUsed()) @@ -370,7 +399,7 @@ func TestPutFeeAndGasUsedESDTWithScCall(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(820765000000000), tx.GetFeeInfo().GetFee()) require.Equal(t, uint64(55_000_000), tx.GetFeeInfo().GetGasUsed()) @@ -425,7 +454,7 @@ func TestPutFeeAndGasUsedScrWithRefundNoTx(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(0), scr.GetFeeInfo().GetFee()) require.Equal(t, uint64(0), scr.GetFeeInfo().GetGasUsed()) @@ -474,7 +503,7 @@ func TestPutFeeAndGasUsedScrWithRefundNotForInitialSender(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(0), scr.GetFeeInfo().GetFee()) require.Equal(t, uint64(0), scr.GetFeeInfo().GetGasUsed()) @@ -522,7 +551,7 @@ func TestPutFeeAndGasUsedScrWithRefund(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, big.NewInt(552865000000000), initialTx.GetFeeInfo().GetFee()) require.Equal(t, uint64(50_336_500), initialTx.GetFeeInfo().GetGasUsed()) @@ -579,7 +608,70 @@ func TestMoveBalanceWithSignalError(t *testing.T) { require.NotNil(t, txsFeeProc) require.Nil(t, err) - err = txsFeeProc.PutFeeAndGasUsed(pool) + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) require.Nil(t, err) require.Equal(t, uint64(225_500), initialTx.GetFeeInfo().GetGasUsed()) } + +func TestPutFeeAndGasUsedRelayedTxV3(t *testing.T) { + t.Parallel() + + txHash := []byte("relayedTxV3") + scrWithRefund := []byte("scrWithRefund") + refundValueBig, _ := big.NewInt(0).SetString("37105580000000", 10) + initialTx := &outportcore.TxInfo{ + Transaction: &transaction.Transaction{ + Nonce: 9, + SndAddr: []byte("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + RcvAddr: []byte("erd1qqqqqqqqqqqqqpgq2nfn5uxjjkjlrzad3jrak8p3p30v79pseddsm73zpw"), + RelayerAddr: []byte("erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl"), + GasLimit: 5000000, + GasPrice: 1000000000, + Data: []byte("add@01"), + Value: big.NewInt(0), + }, + FeeInfo: &outportcore.FeeInfo{Fee: big.NewInt(0)}, + } + + pool := &outportcore.TransactionPool{ + Transactions: map[string]*outportcore.TxInfo{ + hex.EncodeToString(txHash): initialTx, + }, + SmartContractResults: map[string]*outportcore.SCRInfo{ + hex.EncodeToString(scrWithRefund): { + SmartContractResult: &smartContractResult.SmartContractResult{ + Nonce: 10, + GasPrice: 1000000000, + GasLimit: 0, + Value: refundValueBig, + SndAddr: []byte("erd1qqqqqqqqqqqqqpgq2nfn5uxjjkjlrzad3jrak8p3p30v79pseddsm73zpw"), + RcvAddr: []byte("erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl"), + Data: []byte(""), + PrevTxHash: txHash, + OriginalTxHash: txHash, + ReturnMessage: []byte("gas refund for relayer"), + }, + FeeInfo: &outportcore.FeeInfo{ + Fee: big.NewInt(0), + }, + }, + }, + } + + arg := prepareMockArg() + arg.TxFeeCalculator = createEconomicsData(&enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + }, + }) + txsFeeProc, err := NewTransactionsFeeProcessor(arg) + require.NotNil(t, txsFeeProc) + require.Nil(t, err) + + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) + require.Nil(t, err) + require.Equal(t, big.NewInt(120804420000000), initialTx.GetFeeInfo().GetFee()) + require.Equal(t, uint64(1289442), initialTx.GetFeeInfo().GetGasUsed()) + require.Equal(t, "157910000000000", initialTx.GetFeeInfo().GetInitialPaidFee().String()) + require.True(t, initialTx.GetFeeInfo().HadRefund) +} diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index 984cfed2335..be00f2f8a91 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -124,7 +124,8 @@ type baseProcessor struct { nonceOfFirstCommittedBlock core.OptionalUint64 extraDelayRequestBlockInfo time.Duration - proofsPool dataRetriever.ProofsPool + proofsPool dataRetriever.ProofsPool + chanNextHeader chan bool } type bootStorerDataArgs struct { @@ -2297,3 +2298,57 @@ func (bp *baseProcessor) getHeaderHash(header data.HeaderHandler) ([]byte, error return bp.hasher.Compute(string(marshalledHeader)), nil } + +func (bp *baseProcessor) checkProofRequestingNextHeaderBlockingIfMissing( + headerShard uint32, + headerHash []byte, + headerNonce uint64, +) error { + if bp.proofsPool.HasProof(headerShard, headerHash) { + return nil + } + + log.Trace("could not find proof for header, requesting the next one", + "current hash", hex.EncodeToString(headerHash), + "header shard", headerShard) + err := bp.requestNextHeaderBlocking(headerNonce+1, headerShard) + if err != nil { + return err + } + + if bp.proofsPool.HasProof(headerShard, headerHash) { + return nil + } + + return fmt.Errorf("%w for header hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(headerHash)) +} + +func (bp *baseProcessor) requestNextHeaderBlocking(nonce uint64, shardID uint32) error { + headersPool := bp.dataPool.Headers() + + _ = core.EmptyChannel(bp.chanNextHeader) + + if shardID == core.MetachainShardId { + go bp.requestHandler.RequestMetaHeaderByNonce(nonce) + } else { + go bp.requestHandler.RequestShardHeaderByNonce(shardID, nonce) + } + + err := bp.waitForNextHeader() + if err != nil { + return err + } + + _, _, err = process.GetShardHeaderFromPoolWithNonce(nonce, shardID, headersPool) + + return err +} + +func (bp *baseProcessor) waitForNextHeader() error { + select { + case <-bp.chanNextHeader: + return nil + case <-time.After(bp.extraDelayRequestBlockInfo): + return process.ErrTimeIsOut + } +} diff --git a/process/block/displayBlock.go b/process/block/displayBlock.go index 3b1ab7410cc..f61ca16b705 100644 --- a/process/block/displayBlock.go +++ b/process/block/displayBlock.go @@ -268,7 +268,7 @@ func (txc *transactionCounter) displayTxBlockBody( miniBlock.SenderShardID, miniBlock.ReceiverShardID) - if miniBlock.TxHashes == nil || len(miniBlock.TxHashes) == 0 { + if len(miniBlock.TxHashes) == 0 { lines = append(lines, display.NewLineData(false, []string{ part, "", ""})) } diff --git a/process/block/displayMetaBlock.go b/process/block/displayMetaBlock.go index 2018b819925..c23e9924d7e 100644 --- a/process/block/displayMetaBlock.go +++ b/process/block/displayMetaBlock.go @@ -145,7 +145,7 @@ func (hc *headersCounter) displayShardInfo(lines []*display.LineData, header *bl "Header hash", logger.DisplayByteSlice(shardData.HeaderHash)})) - if shardData.ShardMiniBlockHeaders == nil || len(shardData.ShardMiniBlockHeaders) == 0 { + if len(shardData.ShardMiniBlockHeaders) == 0 { lines = append(lines, display.NewLineData(false, []string{ "", "ShardMiniBlockHeaders", ""})) } @@ -197,7 +197,7 @@ func (hc *headersCounter) displayTxBlockBody( miniBlock.SenderShardID, miniBlock.ReceiverShardID) - if miniBlock.TxHashes == nil || len(miniBlock.TxHashes) == 0 { + if len(miniBlock.TxHashes) == 0 { lines = append(lines, display.NewLineData(false, []string{ part, "", ""})) } diff --git a/process/block/metablock.go b/process/block/metablock.go index ec89a7cc6b6..63180536e43 100644 --- a/process/block/metablock.go +++ b/process/block/metablock.go @@ -437,8 +437,9 @@ func (mp *metaProcessor) checkProofsForShardData(header *block.MetaBlock) error continue } - if !mp.proofsPool.HasProof(shardData.ShardID, shardData.HeaderHash) && shardData.GetNonce() > 1 { - return fmt.Errorf("%w for header hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(shardData.HeaderHash)) + err := mp.checkProofRequestingNextHeaderBlockingIfMissing(shardData.ShardID, shardData.HeaderHash, shardData.Nonce) + if err != nil { + return err } if !common.ShouldBlockHavePrevProof(shardHeader.hdr, mp.enableEpochsHandler, common.EquivalentMessagesFlag) { @@ -452,7 +453,7 @@ func (mp *metaProcessor) checkProofsForShardData(header *block.MetaBlock) error prevProof := shardData.GetPreviousProof() headersPool := mp.dataPool.Headers() - prevHeader, err := common.GetHeader(prevProof.GetHeaderHash(), headersPool, shardHeadersStorer, mp.marshalizer) + prevHeader, err := process.GetHeader(prevProof.GetHeaderHash(), headersPool, shardHeadersStorer, mp.marshalizer, prevProof.GetHeaderShardId()) if err != nil { return err } diff --git a/process/block/preprocess/gasComputation.go b/process/block/preprocess/gasComputation.go index 628c6de455f..f4e6a82b4a9 100644 --- a/process/block/preprocess/gasComputation.go +++ b/process/block/preprocess/gasComputation.go @@ -374,7 +374,7 @@ func (gc *gasComputation) ComputeGasProvidedByTx( return txHandler.GetGasLimit(), txHandler.GetGasLimit(), nil } - txTypeSndShard, txTypeDstShard := gc.txTypeHandler.ComputeTransactionType(txHandler) + txTypeSndShard, txTypeDstShard, _ := gc.txTypeHandler.ComputeTransactionType(txHandler) isSCCall := txTypeDstShard == process.SCDeployment || txTypeDstShard == process.SCInvoking || txTypeDstShard == process.BuiltInFunctionCall @@ -403,7 +403,7 @@ func (gc *gasComputation) computeGasProvidedByTxV1( ) (uint64, uint64, error) { moveBalanceConsumption := gc.economicsFee.ComputeGasLimit(txHandler) - txTypeInShard, _ := gc.txTypeHandler.ComputeTransactionType(txHandler) + txTypeInShard, _, _ := gc.txTypeHandler.ComputeTransactionType(txHandler) isSCCall := txTypeInShard == process.SCDeployment || txTypeInShard == process.SCInvoking || txTypeInShard == process.BuiltInFunctionCall || diff --git a/process/block/preprocess/gasComputation_test.go b/process/block/preprocess/gasComputation_test.go index b59d8b45bf1..f60a7455fa6 100644 --- a/process/block/preprocess/gasComputation_test.go +++ b/process/block/preprocess/gasComputation_test.go @@ -214,8 +214,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractI }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -237,8 +237,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractC }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -260,8 +260,8 @@ func TestComputeGasProvidedByTx_ShouldReturnZeroIf0GasLimit(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -283,8 +283,8 @@ func TestComputeGasProvidedByTx_ShouldReturnGasLimitIfLessThanMoveBalance(t *tes }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -306,8 +306,8 @@ func TestComputeGasProvidedByTx_ShouldReturnGasLimitWhenRelayed(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.RelayedTx, process.RelayedTx + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.RelayedTx, process.RelayedTx, false }}, createEnableEpochsHandler(), ) @@ -329,8 +329,8 @@ func TestComputeGasProvidedByTx_ShouldReturnGasLimitWhenRelayedV2(t *testing.T) }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.RelayedTxV2, process.RelayedTxV2 + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.RelayedTxV2, process.RelayedTxV2, false }}, createEnableEpochsHandler(), ) @@ -413,11 +413,11 @@ func TestComputeGasProvidedByMiniBlock_ShouldWork(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.MoveBalance, process.SCInvoking + return process.MoveBalance, process.SCInvoking, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }}, createEnableEpochsHandler(), ) @@ -453,11 +453,11 @@ func TestComputeGasProvidedByMiniBlock_ShouldWorkV1(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.SCInvoking, process.SCInvoking + return process.SCInvoking, process.SCInvoking, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }}, enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) @@ -513,8 +513,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractI }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -536,8 +536,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractC }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }}, enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) diff --git a/process/block/preprocess/interfaces.go b/process/block/preprocess/interfaces.go index fbc155138ad..b98f2271308 100644 --- a/process/block/preprocess/interfaces.go +++ b/process/block/preprocess/interfaces.go @@ -2,21 +2,20 @@ package preprocess import ( "math/big" + "time" "github.com/multiversx/mx-chain-go/storage/txcache" ) // SortedTransactionsProvider defines the public API of the transactions cache type SortedTransactionsProvider interface { - GetSortedTransactions() []*txcache.WrappedTransaction - NotifyAccountNonce(accountKey []byte, nonce uint64) + GetSortedTransactions(session txcache.SelectionSession) []*txcache.WrappedTransaction IsInterfaceNil() bool } // TxCache defines the functionality for the transactions cache type TxCache interface { - SelectTransactionsWithBandwidth(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction - NotifyAccountNonce(accountKey []byte, nonce uint64) + SelectTransactions(session txcache.SelectionSession, gasRequested uint64, maxNum int, selectionLoopMaximumDuration time.Duration) ([]*txcache.WrappedTransaction, uint64) IsInterfaceNil() bool } diff --git a/process/block/preprocess/miniBlockBuilder.go b/process/block/preprocess/miniBlockBuilder.go index a1a2e2bc82e..d10e6ba6ee5 100644 --- a/process/block/preprocess/miniBlockBuilder.go +++ b/process/block/preprocess/miniBlockBuilder.go @@ -22,7 +22,6 @@ import ( type miniBlocksBuilderArgs struct { gasTracker gasTracker accounts state.AccountsAdapter - accountTxsShards *accountTxsShards blockSizeComputation BlockSizeComputationHandler balanceComputationHandler BalanceComputationHandler haveTime func() bool @@ -50,7 +49,6 @@ type miniBlockBuilderStats struct { type miniBlocksBuilder struct { gasTracker accounts state.AccountsAdapter - accountTxsShards *accountTxsShards balanceComputationHandler BalanceComputationHandler blockSizeComputation BlockSizeComputationHandler gasConsumedInReceiverShard map[uint32]uint64 @@ -75,7 +73,6 @@ func newMiniBlockBuilder(args miniBlocksBuilderArgs) (*miniBlocksBuilder, error) return &miniBlocksBuilder{ gasTracker: args.gasTracker, accounts: args.accounts, - accountTxsShards: args.accountTxsShards, balanceComputationHandler: args.balanceComputationHandler, blockSizeComputation: args.blockSizeComputation, miniBlocks: initializeMiniBlocksMap(args.gasTracker.shardCoordinator), @@ -117,9 +114,6 @@ func checkMiniBlocksBuilderArgs(args miniBlocksBuilderArgs) error { if check.IfNil(args.txPool) { return process.ErrNilTransactionPool } - if args.accountTxsShards == nil { - return process.ErrNilAccountTxsPerShard - } if args.haveTime == nil { return process.ErrNilHaveTimeHandler } @@ -136,15 +130,6 @@ func checkMiniBlocksBuilderArgs(args miniBlocksBuilderArgs) error { return nil } -func (mbb *miniBlocksBuilder) updateAccountShardsInfo(tx *transaction.Transaction, wtx *txcache.WrappedTransaction) { - mbb.accountTxsShards.Lock() - mbb.accountTxsShards.accountsInfo[string(tx.GetSndAddr())] = &txShardInfo{ - senderShardID: wtx.SenderShardID, - receiverShardID: wtx.ReceiverShardID, - } - mbb.accountTxsShards.Unlock() -} - // checkAddTransaction method returns a set of actions which could be done afterwards, by checking the given transaction func (mbb *miniBlocksBuilder) checkAddTransaction(wtx *txcache.WrappedTransaction) (*processingActions, *transaction.Transaction) { tx, ok := wtx.Tx.(*transaction.Transaction) diff --git a/process/block/preprocess/miniBlockBuilder_test.go b/process/block/preprocess/miniBlockBuilder_test.go index d3a04147864..3ba49aa00a3 100644 --- a/process/block/preprocess/miniBlockBuilder_test.go +++ b/process/block/preprocess/miniBlockBuilder_test.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "errors" "math/big" - "sync" "testing" "github.com/multiversx/mx-chain-core-go/data" @@ -82,16 +81,6 @@ func Test_checkMiniBlocksBuilderArgsNilBlockSizeComputationHandlerShouldErr(t *t require.Equal(t, process.ErrNilBlockSizeComputationHandler, err) } -func Test_checkMiniBlocksBuilderArgsNilAccountsTxsPerShardsShouldErr(t *testing.T) { - t.Parallel() - - args := createDefaultMiniBlockBuilderArgs() - args.accountTxsShards = nil - - err := checkMiniBlocksBuilderArgs(args) - require.Equal(t, process.ErrNilAccountTxsPerShard, err) -} - func Test_checkMiniBlocksBuilderArgsNilBalanceComputationHandlerShouldErr(t *testing.T) { t.Parallel() @@ -151,29 +140,6 @@ func Test_checkMiniBlocksBuilderArgsOK(t *testing.T) { require.Nil(t, err) } -func Test_MiniBlocksBuilderUpdateAccountShardsInfo(t *testing.T) { - t.Parallel() - - args := createDefaultMiniBlockBuilderArgs() - - mbb, _ := newMiniBlockBuilder(args) - senderAddr := []byte("senderAddr") - receiverAddr := []byte("receiverAddr") - tx := createDefaultTx(senderAddr, receiverAddr, 50000) - - senderShardID := uint32(0) - receiverShardID := uint32(0) - wtx := createWrappedTransaction(tx, senderShardID, receiverShardID) - - mbb.updateAccountShardsInfo(tx, wtx) - - addrShardInfo, ok := mbb.accountTxsShards.accountsInfo[string(tx.SndAddr)] - require.True(t, ok) - - require.Equal(t, senderShardID, addrShardInfo.senderShardID) - require.Equal(t, receiverShardID, addrShardInfo.receiverShardID) -} - func Test_MiniBlocksBuilderHandleGasRefundIntraShard(t *testing.T) { t.Parallel() @@ -659,12 +625,11 @@ func Test_MiniBlocksBuilderCheckAddTransactionWrongTypeAssertion(t *testing.T) { t.Parallel() wtx := &txcache.WrappedTransaction{ - Tx: nil, - TxHash: nil, - SenderShardID: 0, - ReceiverShardID: 0, - Size: 0, - TxFeeScoreNormalized: 0, + Tx: nil, + TxHash: nil, + SenderShardID: 0, + ReceiverShardID: 0, + Size: 0, } args := createDefaultMiniBlockBuilderArgs() @@ -882,11 +847,7 @@ func createDefaultMiniBlockBuilderArgs() miniBlocksBuilderArgs { }, }, }, - accounts: &stateMock.AccountsStub{}, - accountTxsShards: &accountTxsShards{ - accountsInfo: make(map[string]*txShardInfo), - RWMutex: sync.RWMutex{}, - }, + accounts: &stateMock.AccountsStub{}, blockSizeComputation: &testscommon.BlockSizeComputationStub{}, balanceComputationHandler: &testscommon.BalanceComputationStub{}, haveTime: haveTimeTrue, @@ -911,12 +872,14 @@ func createWrappedTransaction( txMarshalled, _ := marshaller.Marshal(tx) txHash := hasher.Compute(string(txMarshalled)) - return &txcache.WrappedTransaction{ - Tx: tx, - TxHash: txHash, - SenderShardID: senderShardID, - ReceiverShardID: receiverShardID, - Size: int64(len(txMarshalled)), - TxFeeScoreNormalized: 10, + wrappedTx := &txcache.WrappedTransaction{ + Tx: tx, + TxHash: txHash, + SenderShardID: senderShardID, + ReceiverShardID: receiverShardID, + Size: int64(len(txMarshalled)), } + + wrappedTx.PricePerUnit = 1_000_000_000 + return wrappedTx } diff --git a/process/block/preprocess/selectionSession.go b/process/block/preprocess/selectionSession.go new file mode 100644 index 00000000000..7e3b35687a1 --- /dev/null +++ b/process/block/preprocess/selectionSession.go @@ -0,0 +1,107 @@ +package preprocess + +import ( + "errors" + + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/storage/txcache" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" +) + +type selectionSession struct { + accountsAdapter state.AccountsAdapter + transactionsProcessor process.TransactionProcessor + + // Cache of accounts, held in the scope of a single selection session. + // Not concurrency-safe, but never accessed concurrently. + ephemeralAccountsCache map[string]vmcommon.AccountHandler +} + +// ArgsSelectionSession holds the arguments for creating a new selection session. +type ArgsSelectionSession struct { + AccountsAdapter state.AccountsAdapter + TransactionsProcessor process.TransactionProcessor +} + +// NewSelectionSession creates a new selection session. +func NewSelectionSession(args ArgsSelectionSession) (*selectionSession, error) { + if check.IfNil(args.AccountsAdapter) { + return nil, process.ErrNilAccountsAdapter + } + if check.IfNil(args.TransactionsProcessor) { + return nil, process.ErrNilTxProcessor + } + + return &selectionSession{ + accountsAdapter: args.AccountsAdapter, + transactionsProcessor: args.TransactionsProcessor, + ephemeralAccountsCache: make(map[string]vmcommon.AccountHandler), + }, nil +} + +// GetAccountState returns the state of an account. +// Will be called by mempool during transaction selection. +func (session *selectionSession) GetAccountState(address []byte) (*txcache.AccountState, error) { + account, err := session.getExistingAccount(address) + if err != nil { + return nil, err + } + + userAccount, ok := account.(state.UserAccountHandler) + if !ok { + return nil, process.ErrWrongTypeAssertion + } + + return &txcache.AccountState{ + Nonce: userAccount.GetNonce(), + Balance: userAccount.GetBalance(), + }, nil +} + +func (session *selectionSession) getExistingAccount(address []byte) (vmcommon.AccountHandler, error) { + account, ok := session.ephemeralAccountsCache[string(address)] + if ok { + return account, nil + } + + account, err := session.accountsAdapter.GetExistingAccount(address) + if err != nil { + return nil, err + } + + session.ephemeralAccountsCache[string(address)] = account + return account, nil +} + +// IsIncorrectlyGuarded checks if a transaction is incorrectly guarded (not executable). +// Will be called by mempool during transaction selection. +func (session *selectionSession) IsIncorrectlyGuarded(tx data.TransactionHandler) bool { + address := tx.GetSndAddr() + account, err := session.getExistingAccount(address) + if err != nil { + return false + } + + userAccount, ok := account.(state.UserAccountHandler) + if !ok { + // On this branch, we are (approximately) mirroring the behavior of "transactionsProcessor.VerifyGuardian()". + return true + } + + txTyped, ok := tx.(*transaction.Transaction) + if !ok { + return false + } + + err = session.transactionsProcessor.VerifyGuardian(txTyped, userAccount) + return errors.Is(err, process.ErrTransactionNotExecutable) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (session *selectionSession) IsInterfaceNil() bool { + return session == nil +} diff --git a/process/block/preprocess/selectionSession_test.go b/process/block/preprocess/selectionSession_test.go new file mode 100644 index 00000000000..939da698cd1 --- /dev/null +++ b/process/block/preprocess/selectionSession_test.go @@ -0,0 +1,171 @@ +package preprocess + +import ( + "bytes" + "fmt" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/testscommon" + stateMock "github.com/multiversx/mx-chain-go/testscommon/state" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/require" +) + +func TestNewSelectionSession(t *testing.T) { + t.Parallel() + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: nil, + TransactionsProcessor: &testscommon.TxProcessorStub{}, + }) + require.Nil(t, session) + require.ErrorIs(t, err, process.ErrNilAccountsAdapter) + + session, err = NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: &stateMock.AccountsStub{}, + TransactionsProcessor: nil, + }) + require.Nil(t, session) + require.ErrorIs(t, err, process.ErrNilTxProcessor) + + session, err = NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: &stateMock.AccountsStub{}, + TransactionsProcessor: &testscommon.TxProcessorStub{}, + }) + require.NoError(t, err) + require.NotNil(t, session) +} + +func TestSelectionSession_GetAccountState(t *testing.T) { + t.Parallel() + + accounts := &stateMock.AccountsStub{} + processor := &testscommon.TxProcessorStub{} + + accounts.GetExistingAccountCalled = func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, []byte("alice")) { + return &stateMock.UserAccountStub{ + Address: []byte("alice"), + Nonce: 42, + }, nil + } + + if bytes.Equal(address, []byte("bob")) { + return &stateMock.UserAccountStub{ + Address: []byte("bob"), + Nonce: 7, + IsGuardedCalled: func() bool { + return true + }, + }, nil + } + + return nil, fmt.Errorf("account not found: %s", address) + } + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: accounts, + TransactionsProcessor: processor, + }) + require.NoError(t, err) + require.NotNil(t, session) + + state, err := session.GetAccountState([]byte("alice")) + require.NoError(t, err) + require.Equal(t, uint64(42), state.Nonce) + + state, err = session.GetAccountState([]byte("bob")) + require.NoError(t, err) + require.Equal(t, uint64(7), state.Nonce) + + state, err = session.GetAccountState([]byte("carol")) + require.ErrorContains(t, err, "account not found: carol") + require.Nil(t, state) +} + +func TestSelectionSession_IsIncorrectlyGuarded(t *testing.T) { + t.Parallel() + + accounts := &stateMock.AccountsStub{} + processor := &testscommon.TxProcessorStub{} + + accounts.GetExistingAccountCalled = func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, []byte("bob")) { + return &stateMock.BaseAccountMock{}, nil + } + + return &stateMock.UserAccountStub{}, nil + } + + processor.VerifyGuardianCalled = func(tx *transaction.Transaction, account state.UserAccountHandler) error { + if tx.Nonce == 43 { + return process.ErrTransactionNotExecutable + } + if tx.Nonce == 44 { + return fmt.Errorf("arbitrary processing error") + } + + return nil + } + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: accounts, + TransactionsProcessor: processor, + }) + require.NoError(t, err) + require.NotNil(t, session) + + isIncorrectlyGuarded := session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 42, SndAddr: []byte("alice")}) + require.False(t, isIncorrectlyGuarded) + + isIncorrectlyGuarded = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 43, SndAddr: []byte("alice")}) + require.True(t, isIncorrectlyGuarded) + + isIncorrectlyGuarded = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 44, SndAddr: []byte("alice")}) + require.False(t, isIncorrectlyGuarded) + + isIncorrectlyGuarded = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 45, SndAddr: []byte("bob")}) + require.True(t, isIncorrectlyGuarded) +} + +func TestSelectionSession_ephemeralAccountsCache_IsSharedAmongCalls(t *testing.T) { + t.Parallel() + + accounts := &stateMock.AccountsStub{} + processor := &testscommon.TxProcessorStub{} + + numCallsGetExistingAccount := 0 + + accounts.GetExistingAccountCalled = func(_ []byte) (vmcommon.AccountHandler, error) { + numCallsGetExistingAccount++ + return &stateMock.UserAccountStub{}, nil + } + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: accounts, + TransactionsProcessor: processor, + }) + require.NoError(t, err) + require.NotNil(t, session) + + _, _ = session.GetAccountState([]byte("alice")) + require.Equal(t, 1, numCallsGetExistingAccount) + + _, _ = session.GetAccountState([]byte("alice")) + require.Equal(t, 1, numCallsGetExistingAccount) + + _ = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 42, SndAddr: []byte("alice")}) + require.Equal(t, 1, numCallsGetExistingAccount) + + _, _ = session.GetAccountState([]byte("bob")) + require.Equal(t, 2, numCallsGetExistingAccount) + + _, _ = session.GetAccountState([]byte("bob")) + require.Equal(t, 2, numCallsGetExistingAccount) + + _ = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 42, SndAddr: []byte("bob")}) + require.Equal(t, 2, numCallsGetExistingAccount) +} diff --git a/process/block/preprocess/sortedTransactionsProvider.go b/process/block/preprocess/sortedTransactionsProvider.go index 8c2613b7aa7..f30cb912892 100644 --- a/process/block/preprocess/sortedTransactionsProvider.go +++ b/process/block/preprocess/sortedTransactionsProvider.go @@ -32,16 +32,11 @@ func newAdapterTxCacheToSortedTransactionsProvider(txCache TxCache) *adapterTxCa } // GetSortedTransactions gets the transactions from the cache -func (adapter *adapterTxCacheToSortedTransactionsProvider) GetSortedTransactions() []*txcache.WrappedTransaction { - txs := adapter.txCache.SelectTransactionsWithBandwidth(process.MaxNumOfTxsToSelect, process.NumTxPerSenderBatchForFillingMiniblock, process.MaxGasBandwidthPerBatchPerSender) +func (adapter *adapterTxCacheToSortedTransactionsProvider) GetSortedTransactions(session txcache.SelectionSession) []*txcache.WrappedTransaction { + txs, _ := adapter.txCache.SelectTransactions(session, process.TxCacheSelectionGasRequested, process.TxCacheSelectionMaxNumTxs, process.TxCacheSelectionLoopMaximumDuration) return txs } -// NotifyAccountNonce notifies the cache about the current nonce of an account -func (adapter *adapterTxCacheToSortedTransactionsProvider) NotifyAccountNonce(accountKey []byte, nonce uint64) { - adapter.txCache.NotifyAccountNonce(accountKey, nonce) -} - // IsInterfaceNil returns true if there is no value under the interface func (adapter *adapterTxCacheToSortedTransactionsProvider) IsInterfaceNil() bool { return adapter == nil @@ -52,14 +47,10 @@ type disabledSortedTransactionsProvider struct { } // GetSortedTransactions returns an empty slice -func (adapter *disabledSortedTransactionsProvider) GetSortedTransactions() []*txcache.WrappedTransaction { +func (adapter *disabledSortedTransactionsProvider) GetSortedTransactions(_ txcache.SelectionSession) []*txcache.WrappedTransaction { return make([]*txcache.WrappedTransaction, 0) } -// NotifyAccountNonce does nothing -func (adapter *disabledSortedTransactionsProvider) NotifyAccountNonce(_ []byte, _ uint64) { -} - // IsInterfaceNil returns true if there is no value under the interface func (adapter *disabledSortedTransactionsProvider) IsInterfaceNil() bool { return adapter == nil diff --git a/process/block/preprocess/transactions.go b/process/block/preprocess/transactions.go index eb24585a55b..2f3fc290ef6 100644 --- a/process/block/preprocess/transactions.go +++ b/process/block/preprocess/transactions.go @@ -16,7 +16,6 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" logger "github.com/multiversx/mx-chain-logger-go" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -34,15 +33,10 @@ var _ process.PreProcessor = (*transactions)(nil) var log = logger.GetOrCreate("process/block/preprocess") // 200% bandwidth to allow 100% overshooting estimations -const selectionGasBandwidthIncreasePercent = 200 +const selectionGasBandwidthIncreasePercent = 400 // 130% to allow 30% overshooting estimations for scheduled SC calls -const selectionGasBandwidthIncreaseScheduledPercent = 130 - -type accountTxsShards struct { - accountsInfo map[string]*txShardInfo - sync.RWMutex -} +const selectionGasBandwidthIncreaseScheduledPercent = 260 // TODO: increase code coverage with unit test @@ -59,7 +53,6 @@ type transactions struct { mutOrderedTxs sync.RWMutex blockTracker BlockTracker blockType block.Type - accountTxsShards accountTxsShards emptyAddress []byte txTypeHandler process.TxTypeHandler scheduledTxsExecutionHandler process.ScheduledTxsExecutionHandler @@ -196,7 +189,6 @@ func NewTransactionPreprocessor( txs.txsForCurrBlock.txHashAndInfo = make(map[string]*txInfo) txs.orderedTxs = make(map[string][]data.TransactionHandler) txs.orderedTxHashes = make(map[string][][]byte) - txs.accountTxsShards.accountsInfo = make(map[string]*txShardInfo) txs.emptyAddress = make([]byte, txs.pubkeyConverter.Len()) @@ -719,7 +711,6 @@ func (txs *transactions) createAndProcessScheduledMiniBlocksFromMeAsValidator( mapSCTxs map[string]struct{}, randomness []byte, ) (block.MiniBlockSlice, error) { - if !txs.enableEpochsHandler.IsFlagEnabled(common.ScheduledMiniBlocksFlag) { return make(block.MiniBlockSlice, 0), nil } @@ -798,10 +789,6 @@ func (txs *transactions) CreateBlockStarted() { txs.orderedTxHashes = make(map[string][][]byte) txs.mutOrderedTxs.Unlock() - txs.accountTxsShards.Lock() - txs.accountTxsShards.accountsInfo = make(map[string]*txShardInfo) - txs.accountTxsShards.Unlock() - txs.scheduledTxsExecutionHandler.Init() } @@ -917,41 +904,6 @@ func (txs *transactions) processAndRemoveBadTransaction( return err } -func (txs *transactions) notifyTransactionProviderIfNeeded() { - txs.accountTxsShards.RLock() - for senderAddress, txShardInfoValue := range txs.accountTxsShards.accountsInfo { - if txShardInfoValue.senderShardID != txs.shardCoordinator.SelfId() { - continue - } - - account, err := txs.getAccountForAddress([]byte(senderAddress)) - if err != nil { - log.Debug("notifyTransactionProviderIfNeeded.getAccountForAddress", "error", err) - continue - } - - strCache := process.ShardCacherIdentifier(txShardInfoValue.senderShardID, txShardInfoValue.receiverShardID) - txShardPool := txs.txPool.ShardDataStore(strCache) - if check.IfNil(txShardPool) { - log.Trace("notifyTransactionProviderIfNeeded", "error", process.ErrNilTxDataPool) - continue - } - - sortedTransactionsProvider := createSortedTransactionsProvider(txShardPool) - sortedTransactionsProvider.NotifyAccountNonce([]byte(senderAddress), account.GetNonce()) - } - txs.accountTxsShards.RUnlock() -} - -func (txs *transactions) getAccountForAddress(address []byte) (vmcommon.AccountHandler, error) { - account, err := txs.accounts.GetExistingAccount(address) - if err != nil { - return nil, err - } - - return account, nil -} - // RequestTransactionsForMiniBlock requests missing transactions for a certain miniblock func (txs *transactions) RequestTransactionsForMiniBlock(miniBlock *block.MiniBlock) int { if miniBlock == nil { @@ -1183,7 +1135,6 @@ func (txs *transactions) createAndProcessMiniBlocksFromMeV1( args := miniBlocksBuilderArgs{ gasTracker: txs.gasTracker, accounts: txs.accounts, - accountTxsShards: &txs.accountTxsShards, balanceComputationHandler: txs.balanceComputation, blockSizeComputation: txs.blockSizeComputation, haveTime: haveTime, @@ -1199,10 +1150,6 @@ func (txs *transactions) createAndProcessMiniBlocksFromMeV1( return nil, nil, err } - defer func() { - go txs.notifyTransactionProviderIfNeeded() - }() - remainingTxs := make([]*txcache.WrappedTransaction, 0) for idx, wtx := range sortedTxs { actions, tx := mbBuilder.checkAddTransaction(wtx) @@ -1286,7 +1233,6 @@ func (txs *transactions) processMiniBlockBuilderTx( ) elapsedTime := time.Since(startTime) mb.stats.totalProcessingTime += elapsedTime - mb.updateAccountShardsInfo(tx, wtx) if err != nil && !errors.Is(err, process.ErrFailedTransaction) { txs.handleBadTransaction(err, wtx, tx, mb, snapshot) @@ -1464,7 +1410,16 @@ func (txs *transactions) computeSortedTxs( sortedTransactionsProvider := createSortedTransactionsProvider(txShardPool) log.Debug("computeSortedTxs.GetSortedTransactions") - sortedTxs := sortedTransactionsProvider.GetSortedTransactions() + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: txs.accounts, + TransactionsProcessor: txs.txProcessor, + }) + if err != nil { + return nil, nil, err + } + + sortedTxs := sortedTransactionsProvider.GetSortedTransactions(session) // TODO: this could be moved to SortedTransactionsProvider selectedTxs, remainingTxs := txs.preFilterTransactionsWithMoveBalancePriority(sortedTxs, gasBandwidth) diff --git a/process/block/preprocess/transactionsV2.go b/process/block/preprocess/transactionsV2.go index 654ff4231a8..8d5bbbc320a 100644 --- a/process/block/preprocess/transactionsV2.go +++ b/process/block/preprocess/transactionsV2.go @@ -25,10 +25,6 @@ func (txs *transactions) createAndProcessMiniBlocksFromMeV2( log.Debug("createAndProcessMiniBlocksFromMeV2", "totalGasConsumedInSelfShard", mbInfo.gasInfo.totalGasConsumedInSelfShard) - defer func() { - go txs.notifyTransactionProviderIfNeeded() - }() - remainingTxs := make([]*txcache.WrappedTransaction, 0) for index := range sortedTxs { if !haveTime() { @@ -175,10 +171,6 @@ func (txs *transactions) processTransaction( elapsedTime = time.Since(startTime) mbInfo.processingInfo.totalTimeUsedForProcess += elapsedTime - txs.accountTxsShards.Lock() - txs.accountTxsShards.accountsInfo[string(tx.GetSndAddr())] = &txShardInfo{senderShardID: senderShardID, receiverShardID: receiverShardID} - txs.accountTxsShards.Unlock() - if err != nil && !errors.Is(err, process.ErrFailedTransaction) { if errors.Is(err, process.ErrHigherNonceInTransaction) { mbInfo.senderAddressToSkip = tx.GetSndAddr() @@ -218,7 +210,7 @@ func (txs *transactions) processTransaction( } if errors.Is(err, process.ErrFailedTransaction) { - log.Debug("transactions.processTransaction", + log.Trace("transactions.processTransaction", "txHash", txHash, "nonce", tx.Nonce, "value", tx.Value, @@ -379,10 +371,6 @@ func (txs *transactions) verifyTransaction( elapsedTime = time.Since(startTime) mbInfo.schedulingInfo.totalTimeUsedForScheduledVerify += elapsedTime - txs.accountTxsShards.Lock() - txs.accountTxsShards.accountsInfo[string(tx.GetSndAddr())] = &txShardInfo{senderShardID: senderShardID, receiverShardID: receiverShardID} - txs.accountTxsShards.Unlock() - if err != nil { isTxTargetedForDeletion := errors.Is(err, process.ErrLowerNonceInTransaction) || errors.Is(err, process.ErrInsufficientFee) || errors.Is(err, process.ErrTransactionNotExecutable) if isTxTargetedForDeletion { @@ -561,7 +549,7 @@ func (txs *transactions) getTxAndMbInfo( } numNewTxs := 1 - _, txTypeDstShard := txs.txTypeHandler.ComputeTransactionType(tx) + _, txTypeDstShard, _ := txs.txTypeHandler.ComputeTransactionType(tx) isReceiverSmartContractAddress := txTypeDstShard == process.SCDeployment || txTypeDstShard == process.SCInvoking isCrossShardScCallOrSpecialTx := receiverShardID != txs.shardCoordinator.SelfId() && (isReceiverSmartContractAddress || len(tx.RcvUserName) > 0) @@ -695,7 +683,7 @@ func (txs *transactions) shouldContinueProcessingScheduledTx( mbInfo.senderAddressToSkip = tx.GetSndAddr() - _, txTypeDstShard := txs.txTypeHandler.ComputeTransactionType(tx) + _, txTypeDstShard, _ := txs.txTypeHandler.ComputeTransactionType(tx) isReceiverSmartContractAddress := txTypeDstShard == process.SCDeployment || txTypeDstShard == process.SCInvoking if !isReceiverSmartContractAddress { return nil, nil, false diff --git a/process/block/preprocess/transactionsV2_test.go b/process/block/preprocess/transactionsV2_test.go index 9d4fb1cf686..1c86454ddda 100644 --- a/process/block/preprocess/transactionsV2_test.go +++ b/process/block/preprocess/transactionsV2_test.go @@ -66,11 +66,11 @@ func createTransactionPreprocessor() *transactions { }, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, TxTypeHandler: &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if bytes.Equal(tx.GetRcvAddr(), []byte("smart contract address")) { - return process.MoveBalance, process.SCInvoking + return process.MoveBalance, process.SCInvoking, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }, }, ScheduledTxsExecutionHandler: &testscommon.ScheduledTxsExecutionStub{}, diff --git a/process/block/preprocess/transactions_test.go b/process/block/preprocess/transactions_test.go index ba1f0dd8601..53e3dc369b4 100644 --- a/process/block/preprocess/transactions_test.go +++ b/process/block/preprocess/transactions_test.go @@ -448,6 +448,16 @@ func TestTxsPreprocessor_NewTransactionPreprocessorNilProcessedMiniBlocksTracker assert.Equal(t, process.ErrNilProcessedMiniBlocksTracker, err) } +func TestTxsPreprocessor_NewTransactionPreprocessorNilTxExecutionOrderHandler(t *testing.T) { + t.Parallel() + + args := createDefaultTransactionsProcessorArgs() + args.TxExecutionOrderHandler = nil + txs, err := NewTransactionPreprocessor(args) + assert.Nil(t, txs) + assert.Equal(t, process.ErrNilTxExecutionOrderHandler, err) +} + func TestTxsPreprocessor_NewTransactionPreprocessorOkValsShouldWork(t *testing.T) { t.Parallel() @@ -662,6 +672,14 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAll(t *testi return 0 }, } + args.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } txs, _ := NewTransactionPreprocessor(args) assert.NotNil(t, txs) @@ -672,7 +690,7 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAll(t *testi addedTxs := make([]*transaction.Transaction, 0) for i := 0; i < 10; i++ { - newTx := &transaction.Transaction{GasLimit: uint64(i)} + newTx := &transaction.Transaction{GasLimit: uint64(i), Nonce: 42 + uint64(i)} txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, newTx) args.TxDataPool.AddData(txHash, newTx, newTx.Size(), strCache) @@ -716,6 +734,14 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAllAsNoSCCal return 0 }, } + args.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } args.TxDataPool, _ = dataRetrieverMock.CreateTxPool(2, 0) txs, _ := NewTransactionPreprocessor(args) assert.NotNil(t, txs) @@ -728,7 +754,7 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAllAsNoSCCal addedTxs := make([]*transaction.Transaction, 0) for i := 0; i < 10; i++ { - newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: []byte("012345678910")} + newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: []byte("012345678910"), Nonce: 42 + uint64(i)} txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, newTx) args.TxDataPool.AddData(txHash, newTx, newTx.Size(), strCache) @@ -781,6 +807,14 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddOnly5asSCCal RemoveGasRefundedCalled: func(hashes [][]byte) { }, } + args.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } txs, _ := NewTransactionPreprocessor(args) @@ -792,7 +826,7 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddOnly5asSCCal scAddress, _ := hex.DecodeString("000000000000000000005fed9c659422cd8429ce92f8973bba2a9fb51e0eb3a1") for i := 0; i < 10; i++ { - newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: scAddress} + newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: scAddress, Nonce: 42 + uint64(i)} txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, newTx) args.TxDataPool.AddData(txHash, newTx, newTx.Size(), strCache) diff --git a/process/block/shardblock.go b/process/block/shardblock.go index b58afe1f70e..dc2835a8db2 100644 --- a/process/block/shardblock.go +++ b/process/block/shardblock.go @@ -305,8 +305,9 @@ func (sp *shardProcessor) ProcessBlock( continue } - if !sp.proofsPool.HasProof(core.MetachainShardId, metaBlockHash) && header.GetNonce() > 1 { - return fmt.Errorf("%w for header hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(metaBlockHash)) + err = sp.checkProofRequestingNextHeaderBlockingIfMissing(core.MetachainShardId, metaBlockHash, hInfo.hdr.GetNonce()) + if err != nil { + return err } } } diff --git a/process/block/shardblock_test.go b/process/block/shardblock_test.go index 39797f8db0c..d029b44e65b 100644 --- a/process/block/shardblock_test.go +++ b/process/block/shardblock_test.go @@ -3053,6 +3053,12 @@ func TestShardProcessor_CreateMiniBlocksShouldWorkWithIntraShardTxs(t *testing.T JournalLenCalled: func() int { return 0 }, + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 45, + Balance: big.NewInt(1000000000000000000), + }, nil + }, } totalGasProvided := uint64(0) diff --git a/process/common.go b/process/common.go index f06e0d00091..0bc06bab14e 100644 --- a/process/common.go +++ b/process/common.go @@ -18,10 +18,13 @@ import ( "github.com/multiversx/mx-chain-core-go/data/typeConverters" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" - "github.com/multiversx/mx-chain-go/dataRetriever" - "github.com/multiversx/mx-chain-go/state" logger "github.com/multiversx/mx-chain-logger-go" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/storage" ) var log = logger.GetOrCreate("process") @@ -680,6 +683,10 @@ func DisplayProcessTxDetails( txHash []byte, addressPubkeyConverter core.PubkeyConverter, ) { + if log.GetLevel() > logger.LogTrace { + return + } + if !check.IfNil(accountHandler) { account, ok := accountHandler.(state.UserAccountHandler) if ok { @@ -770,6 +777,27 @@ func GetSortedStorageUpdates(account *vmcommon.OutputAccount) []*vmcommon.Storag return storageUpdates } +// GetHeader tries to get the header from pool first and if not found, searches for it through storer +func GetHeader( + headerHash []byte, + headersPool common.HeadersPool, + headersStorer storage.Storer, + marshaller marshal.Marshalizer, + shardID uint32, +) (data.HeaderHandler, error) { + header, err := headersPool.GetHeaderByHash(headerHash) + if err == nil { + return header, nil + } + + headerBytes, err := headersStorer.SearchFirst(headerHash) + if err != nil { + return nil, err + } + + return UnmarshalHeader(shardID, marshaller, headerBytes) +} + // UnmarshalHeader unmarshalls a block header func UnmarshalHeader(shardId uint32, marshalizer marshal.Marshalizer, headerBuffer []byte) (data.HeaderHandler, error) { if shardId == core.MetachainShardId { diff --git a/process/constants.go b/process/constants.go index f75e7b882ee..997e0a2a458 100644 --- a/process/constants.go +++ b/process/constants.go @@ -2,6 +2,7 @@ package process import ( "fmt" + "time" ) // BlockHeaderState specifies which is the state of the block header received @@ -79,11 +80,6 @@ const EpochChangeGracePeriod = 1 // in one round, when node processes a received block const MaxHeaderRequestsAllowed = 20 -// NumTxPerSenderBatchForFillingMiniblock defines the number of transactions to be drawn -// from the transactions pool, for a specific sender, in a single pass. -// Drawing transactions for a miniblock happens in multiple passes, until "MaxItemsInBlock" are drawn. -const NumTxPerSenderBatchForFillingMiniblock = 10 - // NonceDifferenceWhenSynced defines the difference between probable highest nonce seen from network and node's last // committed block nonce, after which, node is considered himself not synced const NonceDifferenceWhenSynced = 0 @@ -135,12 +131,6 @@ const MaxShardHeadersAllowedInOneMetaBlock = 60 // which would be included in one meta block if they are available const MinShardHeadersFromSameShardInOneMetaBlock = 10 -// MaxNumOfTxsToSelect defines the maximum number of transactions that should be selected from the cache -const MaxNumOfTxsToSelect = 30000 - -// MaxGasBandwidthPerBatchPerSender defines the maximum gas bandwidth that should be selected for a sender per batch from the cache -const MaxGasBandwidthPerBatchPerSender = 5000000 - // MaxHeadersToWhitelistInAdvance defines the maximum number of headers whose miniblocks will be whitelisted in advance const MaxHeadersToWhitelistInAdvance = 300 @@ -148,3 +138,12 @@ const MaxHeadersToWhitelistInAdvance = 300 // the real gas used, after which the transaction will be considered an attack and all the gas will be consumed and // nothing will be refunded to the sender const MaxGasFeeHigherFactorAccepted = 10 + +// TxCacheSelectionGasRequested defines the maximum total gas for transactions that should be selected from the cache. +const TxCacheSelectionGasRequested = 10_000_000_000 + +// TxCacheSelectionMaxNumTxs defines the maximum number of transactions that should be selected from the cache. +const TxCacheSelectionMaxNumTxs = 30_000 + +// TxCacheSelectionLoopMaximumDuration defines the maximum duration for the loop that selects transactions from the cache. +const TxCacheSelectionLoopMaximumDuration = 250 * time.Millisecond diff --git a/process/coordinator/process.go b/process/coordinator/process.go index 8a50d9f0b21..dbc68612649 100644 --- a/process/coordinator/process.go +++ b/process/coordinator/process.go @@ -1622,7 +1622,7 @@ func (tc *transactionCoordinator) checkGasProvidedByMiniBlockInReceiverShard( return process.ErrMissingTransaction } - _, txTypeDstShard := tc.txTypeHandler.ComputeTransactionType(txHandler) + _, txTypeDstShard, _ := tc.txTypeHandler.ComputeTransactionType(txHandler) moveBalanceGasLimit := tc.economicsFee.ComputeGasLimit(txHandler) if txTypeDstShard == process.MoveBalance { gasProvidedByTxInReceiverShard = moveBalanceGasLimit diff --git a/process/coordinator/process_test.go b/process/coordinator/process_test.go index 80e26980e81..f18c6e0fc23 100644 --- a/process/coordinator/process_test.go +++ b/process/coordinator/process_test.go @@ -32,6 +32,7 @@ import ( "github.com/multiversx/mx-chain-go/process/factory" "github.com/multiversx/mx-chain-go/process/factory/shard" "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/database" "github.com/multiversx/mx-chain-go/storage/storageunit" @@ -587,6 +588,7 @@ func createInterimProcessorContainer() process.IntermediateProcessorContainer { func createPreProcessorContainerWithDataPool( dataPool dataRetriever.PoolsHolder, feeHandler process.FeeHandler, + accounts state.AccountsAdapter, ) process.PreProcessorsContainer { totalGasProvided := uint64(0) @@ -597,7 +599,7 @@ func createPreProcessorContainerWithDataPool( &hashingMocks.HasherMock{}, dataPool, createMockPubkeyConverter(), - &stateMock.AccountsStub{}, + accounts, &testscommon.RequestHandlerStub{}, &testscommon.TxProcessorMock{ ProcessTransactionCalled: func(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) { @@ -1248,7 +1250,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeNoTime(t *t tdp := initDataPool(txHash) argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1267,7 +1269,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeNoSpace(t * tdp := initDataPool(txHash) argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.GasHandler = &testscommon.GasHandlerStub{ TotalGasProvidedCalled: func() uint64 { return totalGasProvided @@ -1296,9 +1298,18 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMe(t *testing } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) + tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1311,7 +1322,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMe(t *testing hasher := &hashingMocks.HasherMock{} for shId := uint32(0); shId < nrShards; shId++ { strCache := process.ShardCacherIdentifier(0, shId) - newTx := &transaction.Transaction{GasLimit: uint64(shId)} + newTx := &transaction.Transaction{GasLimit: uint64(shId), Nonce: 42 + uint64(shId)} computedTxHash, _ := core.CalculateHash(marshalizer, hasher, newTx) txPool.AddData(computedTxHash, newTx, newTx.Size(), strCache) @@ -1334,9 +1345,17 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeMultipleMin } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 0, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1387,6 +1406,14 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeMultipleMin } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 0, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool( @@ -1404,7 +1431,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeMultipleMin ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { return gasLimit / uint64(numMiniBlocks) }, - }) + }, argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1450,6 +1477,14 @@ func TestTransactionCoordinator_CompactAndExpandMiniblocksShouldWork(t *testing. } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 0, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool( @@ -1467,7 +1502,7 @@ func TestTransactionCoordinator_CompactAndExpandMiniblocksShouldWork(t *testing. ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { return 0 }, - }) + }, argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1514,9 +1549,17 @@ func TestTransactionCoordinator_GetAllCurrentUsedTxs(t *testing.T) { } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.GasHandler = &testscommon.GasHandlerStub{ ComputeGasProvidedByTxCalled: func(txSndShId uint32, txRcvShId uint32, txHandler data.TransactionHandler) (uint64, uint64, error) { return 0, 0, nil @@ -1538,7 +1581,7 @@ func TestTransactionCoordinator_GetAllCurrentUsedTxs(t *testing.T) { hasher := &hashingMocks.HasherMock{} for i := uint32(0); i < nrShards; i++ { strCache := process.ShardCacherIdentifier(0, i) - newTx := &transaction.Transaction{GasLimit: uint64(i)} + newTx := &transaction.Transaction{GasLimit: uint64(i), Nonce: 42 + uint64(i)} computedTxHash, _ := core.CalculateHash(marshalizer, hasher, newTx) txPool.AddData(computedTxHash, newTx, newTx.Size(), strCache) @@ -1559,7 +1602,7 @@ func TestTransactionCoordinator_RequestBlockTransactionsNilBody(t *testing.T) { argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1581,7 +1624,7 @@ func TestTransactionCoordinator_RequestBlockTransactionsRequestOne(t *testing.T) argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1611,7 +1654,7 @@ func TestTransactionCoordinator_IsDataPreparedForProcessing(t *testing.T) { argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1631,7 +1674,7 @@ func TestTransactionCoordinator_SaveTxsToStorage(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1667,7 +1710,7 @@ func TestTransactionCoordinator_RestoreBlockDataFromStorage(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1705,7 +1748,7 @@ func TestTransactionCoordinator_RemoveBlockDataFromPool(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = dataPool.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1812,7 +1855,7 @@ func TestTransactionCoordinator_ProcessBlockTransaction(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = dataPool.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -2367,7 +2410,7 @@ func TestTransactionCoordinator_SaveTxsToStorageCallsSaveIntermediate(t *testing argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.InterProcessors = &mock.InterimProcessorContainerMock{ KeysCalled: func() []block.Type { return []block.Type{block.SmartContractResultBlock} @@ -2405,7 +2448,7 @@ func TestTransactionCoordinator_PreprocessorsHasToBeOrderedRewardsAreLast(t *tes argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = dataPool.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.InterProcessors = createInterimProcessorContainer() tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) @@ -2589,14 +2632,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldReturnWhenEpochIsNo t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2634,14 +2679,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldErrMaxGasLimitPerMi maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2700,14 +2747,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldErrMaxAccumulatedFe maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2777,14 +2826,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldErrMaxDeveloperFees maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2854,14 +2905,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldWork(t *testing.T) maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2930,14 +2983,16 @@ func TestTransactionCoordinator_GetAllTransactionsShouldWork(t *testing.T) { t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3003,14 +3058,16 @@ func TestTransactionCoordinator_VerifyGasLimitShouldErrMaxGasLimitPerMiniBlockIn tx3GasLimit := uint64(300000001) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3096,14 +3153,16 @@ func TestTransactionCoordinator_VerifyGasLimitShouldWork(t *testing.T) { tx3GasLimit := uint64(300) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3185,14 +3244,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3227,14 +3288,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx1GasLimit := uint64(100) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3246,8 +3309,8 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould }, }, TxTypeHandler: &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }, }, TransactionsLogProcessor: &mock.TxLogsProcessorStub{}, @@ -3284,14 +3347,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx2GasLimit := uint64(1) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3303,8 +3368,8 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould }, }, TxTypeHandler: &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }, }, TransactionsLogProcessor: &mock.TxLogsProcessorStub{}, @@ -3346,14 +3411,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx3GasLimit := uint64(300000001) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3413,14 +3480,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx3GasLimit := uint64(300) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3477,14 +3546,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMissingTransaction(t *testing t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3532,14 +3603,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxAccumulatedFeesExceeded(t tx1GasLimit := uint64(100) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3601,14 +3674,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxDeveloperFeesExceeded(t *t tx1GasLimit := uint64(100) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3671,14 +3746,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxAccumulatedFeesExceededWhe enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3755,14 +3832,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxDeveloperFeesExceededWhenS enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3839,14 +3918,16 @@ func TestTransactionCoordinator_VerifyFeesShouldWork(t *testing.T) { enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3926,14 +4007,16 @@ func TestTransactionCoordinator_GetMaxAccumulatedAndDeveloperFeesShouldErr(t *te t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3978,14 +4061,16 @@ func TestTransactionCoordinator_GetMaxAccumulatedAndDeveloperFeesShouldWork(t *t tx3GasLimit := uint64(300) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -4044,14 +4129,16 @@ func TestTransactionCoordinator_RevertIfNeededShouldWork(t *testing.T) { numTxsFeesReverted := 0 dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &mock.GasHandlerMock{ RestoreGasSinceLastResetCalled: func(key []byte) { diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index f1d47aff44c..45097d89c50 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -77,60 +77,61 @@ func NewTxTypeHandler( } // ComputeTransactionType calculates the transaction type -func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { err := tth.checkTxValidity(tx) if err != nil { - return process.InvalidTransaction, process.InvalidTransaction + return process.InvalidTransaction, process.InvalidTransaction, false } + isRelayedV3 := common.IsRelayedTxV3(tx) + isEmptyAddress := tth.isDestAddressEmpty(tx) if isEmptyAddress { if len(tx.GetData()) > 0 { - return process.SCDeployment, process.SCDeployment + return process.SCDeployment, process.SCDeployment, isRelayedV3 } - return process.InvalidTransaction, process.InvalidTransaction + return process.InvalidTransaction, process.InvalidTransaction, isRelayedV3 } - if len(tx.GetData()) == 0 { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, isRelayedV3 } funcName, args := tth.getFunctionFromArguments(tx.GetData()) isBuiltInFunction := tth.isBuiltInFunctionCall(funcName) if isBuiltInFunction { if tth.isSCCallAfterBuiltIn(funcName, args, tx) { - return process.BuiltInFunctionCall, process.SCInvoking + return process.BuiltInFunctionCall, process.SCInvoking, isRelayedV3 } - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, isRelayedV3 } if isCallOfType(tx, vm.AsynchronousCallBack) { - return process.SCInvoking, process.SCInvoking + return process.SCInvoking, process.SCInvoking, isRelayedV3 } if len(funcName) == 0 { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, isRelayedV3 } if tth.isRelayedTransactionV1(funcName) { - return process.RelayedTx, process.RelayedTx + return process.RelayedTx, process.RelayedTx, isRelayedV3 // this should never be reached with both relayed v1 and relayed v3 } if tth.isRelayedTransactionV2(funcName) { - return process.RelayedTxV2, process.RelayedTxV2 + return process.RelayedTxV2, process.RelayedTxV2, isRelayedV3 // this should never be reached with both relayed v2 and relayed v3 } isDestInSelfShard := tth.isDestAddressInSelfShard(tx.GetRcvAddr()) if isDestInSelfShard && core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.SCInvoking, process.SCInvoking + return process.SCInvoking, process.SCInvoking, isRelayedV3 } if core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.MoveBalance, process.SCInvoking + return process.MoveBalance, process.SCInvoking, isRelayedV3 } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, isRelayedV3 } func isCallOfType(tx data.TransactionHandler, callType vm.CallType) bool { diff --git a/process/coordinator/transactionType_test.go b/process/coordinator/transactionType_test.go index 918b6069212..ef00e924141 100644 --- a/process/coordinator/transactionType_test.go +++ b/process/coordinator/transactionType_test.go @@ -124,9 +124,10 @@ func TestTxTypeHandler_ComputeTransactionTypeNil(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(nil) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(nil) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeNilTx(t *testing.T) { @@ -145,9 +146,10 @@ func TestTxTypeHandler_ComputeTransactionTypeNilTx(t *testing.T) { tx.Value = big.NewInt(45) tx = nil - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeErrWrongTransaction(t *testing.T) { @@ -165,9 +167,10 @@ func TestTxTypeHandler_ComputeTransactionTypeErrWrongTransaction(t *testing.T) { tx.RcvAddr = nil tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeScDeployment(t *testing.T) { @@ -186,9 +189,10 @@ func TestTxTypeHandler_ComputeTransactionTypeScDeployment(t *testing.T) { tx.Data = []byte("data") tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCDeployment, txTypeIn) assert.Equal(t, process.SCDeployment, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallNftTransfer(t *testing.T) { @@ -221,9 +225,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallNftTransfer(t *t tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.BuiltInFunctionCall, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallEsdtTransfer(t *testing.T) { @@ -250,9 +255,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallEsdtTransfer(t * "@" + hex.EncodeToString(big.NewInt(10).Bytes())) tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.BuiltInFunctionCall, txTypeIn) assert.Equal(t, process.BuiltInFunctionCall, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRecv0AddressWrongTransaction(t *testing.T) { @@ -271,9 +277,10 @@ func TestTxTypeHandler_ComputeTransactionTypeRecv0AddressWrongTransaction(t *tes tx.Data = nil tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeScInvoking(t *testing.T) { @@ -292,9 +299,10 @@ func TestTxTypeHandler_ComputeTransactionTypeScInvoking(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCInvoking, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeMoveBalance(t *testing.T) { @@ -318,9 +326,10 @@ func TestTxTypeHandler_ComputeTransactionTypeMoveBalance(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.MoveBalance, txTypeIn) assert.Equal(t, process.MoveBalance, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunc(t *testing.T) { @@ -347,9 +356,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunc(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.BuiltInFunctionCall, txTypeIn) assert.Equal(t, process.BuiltInFunctionCall, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveMoveBalance(t *testing.T) { @@ -378,9 +388,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveMoveBalance(t * assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.MoveBalance, txTypeIn) assert.Equal(t, process.MoveBalance, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveSCCall(t *testing.T) { @@ -409,9 +420,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveSCCall(t *testi assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCInvoking, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRelayedFunc(t *testing.T) { @@ -435,9 +447,10 @@ func TestTxTypeHandler_ComputeTransactionTypeRelayedFunc(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.RelayedTx, txTypeIn) assert.Equal(t, process.RelayedTx, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRelayedV2Func(t *testing.T) { @@ -461,9 +474,39 @@ func TestTxTypeHandler_ComputeTransactionTypeRelayedV2Func(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.RelayedTxV2, txTypeIn) assert.Equal(t, process.RelayedTxV2, txTypeCross) + assert.False(t, isRelayedV3) +} + +func TestTxTypeHandler_ComputeTransactionTypeRelayedV3(t *testing.T) { + t.Parallel() + + tx := &transaction.Transaction{} + tx.Nonce = 0 + tx.SndAddr = []byte("000") + tx.RcvAddr = []byte("001") + tx.Value = big.NewInt(45) + tx.RelayerAddr = []byte("002") + tx.Signature = []byte("ssig") + tx.RelayerSignature = []byte("rsig") + + arg := createMockArguments() + arg.PubkeyConverter = &testscommon.PubkeyConverterStub{ + LenCalled: func() int { + return len(tx.RcvAddr) + }, + } + tth, err := NewTxTypeHandler(arg) + + assert.NotNil(t, tth) + assert.Nil(t, err) + + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) + assert.Equal(t, process.MoveBalance, txTypeIn) + assert.Equal(t, process.MoveBalance, txTypeCross) + assert.True(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeForSCRCallBack(t *testing.T) { @@ -488,7 +531,8 @@ func TestTxTypeHandler_ComputeTransactionTypeForSCRCallBack(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCInvoking, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index 9c72be1d89a..d043f207ac0 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -5,6 +5,8 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/state" @@ -74,14 +76,27 @@ func (txv *txValidator) CheckTxValidity(interceptedTx process.InterceptedTransac return nil } + // for relayed v3, we allow sender accounts that do not exist + isRelayedV3 := common.IsRelayedTxV3(interceptedTx.Transaction()) + hasValue := hasTxValue(interceptedTx) + shouldAllowMissingSenderAccount := isRelayedV3 && !hasValue accountHandler, err := txv.getSenderAccount(interceptedTx) - if err != nil { + if err != nil && !shouldAllowMissingSenderAccount { return err } return txv.checkAccount(interceptedTx, accountHandler) } +func hasTxValue(interceptedTx process.InterceptedTransactionHandler) bool { + txValue := interceptedTx.Transaction().GetValue() + if check.IfNilReflect(txValue) { + return false + } + + return txValue.Sign() > 0 +} + func (txv *txValidator) checkAccount( interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler, @@ -91,24 +106,41 @@ func (txv *txValidator) checkAccount( return err } - account, err := txv.getSenderUserAccount(interceptedTx, accountHandler) + feePayerAccount, err := txv.getFeePayerAccount(interceptedTx, accountHandler) if err != nil { return err } - return txv.checkBalance(interceptedTx, account) + return txv.checkBalance(interceptedTx, feePayerAccount) } -func (txv *txValidator) getSenderUserAccount( +func (txv *txValidator) getFeePayerAccount( interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler, ) (state.UserAccountHandler, error) { - senderAddress := interceptedTx.SenderAddress() - account, ok := accountHandler.(state.UserAccountHandler) + payerAddress := interceptedTx.SenderAddress() + payerAccount := accountHandler + + tx := interceptedTx.Transaction() + if common.IsRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + payerAddress = relayedTx.GetRelayerAddr() + relayerAccount, err := txv.accounts.GetExistingAccount(payerAddress) + if err != nil { + return nil, fmt.Errorf("%w for address %s and shard %d, err: %s", + process.ErrAccountNotFound, + txv.pubKeyConverter.SilentEncode(payerAddress, log), + txv.shardCoordinator.SelfId(), + err.Error(), + ) + } + payerAccount = relayerAccount + } + account, ok := payerAccount.(state.UserAccountHandler) if !ok { return nil, fmt.Errorf("%w, account is not of type *state.Account, address: %s", process.ErrWrongTypeAssertion, - txv.pubKeyConverter.SilentEncode(senderAddress, log), + txv.pubKeyConverter.SilentEncode(payerAddress, log), ) } return account, nil @@ -118,10 +150,9 @@ func (txv *txValidator) checkBalance(interceptedTx process.InterceptedTransactio accountBalance := account.GetBalance() txFee := interceptedTx.Fee() if accountBalance.Cmp(txFee) < 0 { - senderAddress := interceptedTx.SenderAddress() return fmt.Errorf("%w, for address: %s, wanted %v, have %v", process.ErrInsufficientFunds, - txv.pubKeyConverter.SilentEncode(senderAddress, log), + txv.pubKeyConverter.SilentEncode(account.AddressBytes(), log), txFee, accountBalance, ) @@ -131,7 +162,11 @@ func (txv *txValidator) checkBalance(interceptedTx process.InterceptedTransactio } func (txv *txValidator) checkNonce(interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler) error { - accountNonce := accountHandler.GetNonce() + accountNonce := uint64(0) + if !check.IfNil(accountHandler) { + accountNonce = accountHandler.GetNonce() + } + txNonce := interceptedTx.Nonce() lowerNonceInTx := txNonce < accountNonce veryHighNonceInTx := txNonce > accountNonce+uint64(txv.maxNonceDeltaAllowed) diff --git a/process/dataValidators/txValidator_test.go b/process/dataValidators/txValidator_test.go index 551b18928d1..31fa230ec39 100644 --- a/process/dataValidators/txValidator_test.go +++ b/process/dataValidators/txValidator_test.go @@ -1,6 +1,7 @@ package dataValidators_test import ( + "bytes" "errors" "math/big" "strconv" @@ -269,31 +270,101 @@ func TestTxValidator_CheckTxValidityTxNonceIsTooHigh(t *testing.T) { func TestTxValidator_CheckTxValidityAccountBalanceIsLessThanTxTotalValueShouldReturnFalse(t *testing.T) { t.Parallel() - accountNonce := uint64(0) - txNonce := uint64(1) - fee := big.NewInt(1000) - accountBalance := big.NewInt(10) + t.Run("normal tx should return false for sender", func(t *testing.T) { + t.Parallel() - adb := getAccAdapter(accountNonce, accountBalance) - shardCoordinator := createMockCoordinator("_", 0) - maxNonceDeltaAllowed := 100 - txValidator, err := dataValidators.NewTxValidator( - adb, - shardCoordinator, - &testscommon.WhiteListHandlerStub{}, - testscommon.NewPubkeyConverterMock(32), - &testscommon.TxVersionCheckerStub{}, - maxNonceDeltaAllowed, - ) - assert.Nil(t, err) + accountNonce := uint64(0) + txNonce := uint64(1) + fee := big.NewInt(1000) + accountBalance := big.NewInt(10) - addressMock := []byte("address") - currentShard := uint32(0) - txValidatorHandler := getInterceptedTxHandler(currentShard, currentShard, txNonce, addressMock, fee) + providedSenderAddress := []byte("address") + adb := &stateMock.AccountsStub{} + adb.GetExistingAccountCalled = func(address []byte) (handler vmcommon.AccountHandler, e error) { + require.True(t, bytes.Equal(providedSenderAddress, address)) - result := txValidator.CheckTxValidity(txValidatorHandler) - assert.NotNil(t, result) - assert.True(t, errors.Is(result, process.ErrInsufficientFunds)) + acc, _ := accounts.NewUserAccount(address, &trie.DataTrieTrackerStub{}, &trie.TrieLeafParserStub{}) + acc.Nonce = accountNonce + acc.Balance = accountBalance + + return acc, nil + } + + shardCoordinator := createMockCoordinator("_", 0) + maxNonceDeltaAllowed := 100 + txValidator, err := dataValidators.NewTxValidator( + adb, + shardCoordinator, + &testscommon.WhiteListHandlerStub{}, + testscommon.NewPubkeyConverterMock(32), + &testscommon.TxVersionCheckerStub{}, + maxNonceDeltaAllowed, + ) + assert.Nil(t, err) + + currentShard := uint32(0) + txValidatorHandler := getInterceptedTxHandler(currentShard, currentShard, txNonce, providedSenderAddress, fee) + + result := txValidator.CheckTxValidity(txValidatorHandler) + assert.NotNil(t, result) + assert.True(t, errors.Is(result, process.ErrInsufficientFunds)) + }) + t.Run("relayed tx v3 should return false for relayer", func(t *testing.T) { + t.Parallel() + + accountNonce := uint64(0) + txNonce := uint64(1) + fee := big.NewInt(1000) + accountBalance := big.NewInt(10) + + providedRelayerAddress := []byte("relayer") + providedSenderAddress := []byte("address") + adb := &stateMock.AccountsStub{} + cnt := 0 + adb.GetExistingAccountCalled = func(address []byte) (handler vmcommon.AccountHandler, e error) { + cnt++ + if cnt == 1 { + return nil, errors.New("sender not found") + } + + require.True(t, bytes.Equal(providedRelayerAddress, address)) + + acc, _ := accounts.NewUserAccount(address, &trie.DataTrieTrackerStub{}, &trie.TrieLeafParserStub{}) + acc.Nonce = accountNonce + acc.Balance = accountBalance + + return acc, nil + } + + shardCoordinator := createMockCoordinator("_", 0) + maxNonceDeltaAllowed := 100 + txValidator, err := dataValidators.NewTxValidator( + adb, + shardCoordinator, + &testscommon.WhiteListHandlerStub{}, + testscommon.NewPubkeyConverterMock(32), + &testscommon.TxVersionCheckerStub{}, + maxNonceDeltaAllowed, + ) + assert.Nil(t, err) + + currentShard := uint32(0) + txValidatorHandler := getInterceptedTxHandler(currentShard, currentShard, txNonce, providedSenderAddress, fee) + txValidatorHandlerStub, ok := txValidatorHandler.(*mock.InterceptedTxHandlerStub) + require.True(t, ok) + txValidatorHandlerStub.TransactionCalled = func() data.TransactionHandler { + return &transaction.Transaction{ + SndAddr: providedSenderAddress, + Signature: []byte("address sig"), + RelayerAddr: providedRelayerAddress, + RelayerSignature: []byte("relayer sig"), + Value: big.NewInt(0), + } + } + result := txValidator.CheckTxValidity(txValidatorHandler) + assert.NotNil(t, result) + assert.True(t, errors.Is(result, process.ErrInsufficientFunds)) + }) } func TestTxValidator_CheckTxValidityAccountNotExitsShouldReturnFalse(t *testing.T) { diff --git a/process/disabled/txTypeHandler.go b/process/disabled/txTypeHandler.go new file mode 100644 index 00000000000..dd405edff4d --- /dev/null +++ b/process/disabled/txTypeHandler.go @@ -0,0 +1,28 @@ +package disabled + +import ( + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/process" + logger "github.com/multiversx/mx-chain-logger-go" +) + +var log = logger.GetOrCreate("disabledTxTypeHandler") + +type txTypeHandler struct { +} + +// NewTxTypeHandler returns a new instance of disabled txTypeHandler +func NewTxTypeHandler() *txTypeHandler { + return &txTypeHandler{} +} + +// ComputeTransactionType always returns invalid transaction as it is disabled +func (handler *txTypeHandler) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + log.Warn("disabled txTypeHandler ComputeTransactionType always returns invalid transaction") + return process.InvalidTransaction, process.InvalidTransaction, false +} + +// IsInterfaceNil returns true if there is no value under the interface +func (handler *txTypeHandler) IsInterfaceNil() bool { + return handler == nil +} diff --git a/process/economics/economicsData.go b/process/economics/economicsData.go index 5b7ce045237..84e161ef86f 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -488,15 +488,19 @@ func (ed *economicsData) ComputeGasLimit(tx data.TransactionWithFeeHandler) uint return ed.ComputeGasLimitInEpoch(tx, currentEpoch) } -// ComputeGasLimitInEpoch returns the gas limit need by the provided transaction in order to be executed in a specific epoch +// ComputeGasLimitInEpoch returns the gas limit needed by the provided transaction in order to be executed in a specific epoch func (ed *economicsData) ComputeGasLimitInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) uint64 { gasLimit := ed.getMinGasLimit(epoch) dataLen := uint64(len(tx.GetData())) gasLimit += dataLen * ed.gasPerDataByte txInstance, ok := tx.(*transaction.Transaction) - if ok && ed.txVersionHandler.IsGuardedTransaction(txInstance) { - gasLimit += ed.getExtraGasLimitGuardedTx(epoch) + if ok { + if ed.txVersionHandler.IsGuardedTransaction(txInstance) { + gasLimit += ed.getExtraGasLimitGuardedTx(epoch) + } + + gasLimit += ed.getExtraGasLimitRelayedTx(txInstance, epoch) } return gasLimit @@ -575,6 +579,15 @@ func (ed *economicsData) ComputeGasLimitBasedOnBalance(tx data.TransactionWithFe return ed.ComputeGasLimitBasedOnBalanceInEpoch(tx, balance, currentEpoch) } +// ComputeGasUnitsFromRefundValue will compute the gas unit based on the refund value +func (ed *economicsData) ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 { + gasPrice := ed.GasPriceForProcessingInEpoch(tx, epoch) + refund := big.NewInt(0).Set(refundValue) + gasUnits := refund.Div(refund, big.NewInt(int64(gasPrice))) + + return gasUnits.Uint64() +} + // ComputeGasLimitBasedOnBalanceInEpoch will compute gas limit for the given transaction based on the balance in a specific epoch func (ed *economicsData) ComputeGasLimitBasedOnBalanceInEpoch(tx data.TransactionWithFeeHandler, balance *big.Int, epoch uint32) (uint64, error) { balanceWithoutTransferValue := big.NewInt(0).Sub(balance, tx.GetValue()) @@ -605,6 +618,15 @@ func (ed *economicsData) ComputeGasLimitBasedOnBalanceInEpoch(tx data.Transactio return totalGasLimit, nil } +// getExtraGasLimitRelayedTx returns extra gas limit for relayed tx in a specific epoch +func (ed *economicsData) getExtraGasLimitRelayedTx(txInstance *transaction.Transaction, epoch uint32) uint64 { + if common.IsRelayedTxV3(txInstance) { + return ed.MinGasLimitInEpoch(epoch) + } + + return 0 +} + // IsInterfaceNil returns true if there is no value under the interface func (ed *economicsData) IsInterfaceNil() bool { return ed == nil diff --git a/process/errors.go b/process/errors.go index ded117ecb7e..cba8b0f3de4 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1068,9 +1068,6 @@ var ErrNilIsMaxBlockSizeReachedHandler = errors.New("nil handler for max block s // ErrNilTxMaxTotalCostHandler signals a nil transaction max total cost var ErrNilTxMaxTotalCostHandler = errors.New("nil transaction max total cost") -// ErrNilAccountTxsPerShard signals a nil mapping for account transactions to shard -var ErrNilAccountTxsPerShard = errors.New("nil account transactions per shard mapping") - // ErrScheduledRootHashDoesNotMatch signals that scheduled root hash does not match var ErrScheduledRootHashDoesNotMatch = errors.New("scheduled root hash does not match") @@ -1242,6 +1239,9 @@ var ErrNilSentSignatureTracker = errors.New("nil sent signature tracker") // ErrTransferAndExecuteByUserAddressesAreNil signals that transfer and execute by user addresses are nil var ErrTransferAndExecuteByUserAddressesAreNil = errors.New("transfer and execute by user addresses are nil") +// ErrRelayedTxV3Disabled signals that relayed tx v3 are disabled +var ErrRelayedTxV3Disabled = errors.New("relayed tx v3 are disabled") + // ErrMissingConfigurationForEpochZero signals that the provided configuration doesn't include anything for epoch 0 var ErrMissingConfigurationForEpochZero = errors.New("missing configuration for epoch 0") @@ -1251,6 +1251,15 @@ var ErrEmptyChainParametersConfiguration = errors.New("empty chain parameters co // ErrNoMatchingConfigForProvidedEpoch signals that there is no matching configuration for the provided epoch var ErrNoMatchingConfigForProvidedEpoch = errors.New("no matching configuration") +// ErrGuardedRelayerNotAllowed signals that the provided relayer is guarded +var ErrGuardedRelayerNotAllowed = errors.New("guarded relayer not allowed") + +// ErrRelayedByGuardianNotAllowed signals that the provided guardian is also the relayer +var ErrRelayedByGuardianNotAllowed = errors.New("relayed by guardian not allowed") + +// ErrInvalidRelayedTxV3 signals that an invalid relayed tx v3 has been provided +var ErrInvalidRelayedTxV3 = errors.New("invalid relayed transaction") + // ErrNilHeaderProof signals that a nil header proof has been provided var ErrNilHeaderProof = errors.New("nil header proof") diff --git a/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go b/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go index ec699e5803b..06f589c07df 100644 --- a/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go +++ b/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go @@ -725,7 +725,7 @@ func getArgumentsMeta( WhiteListHandler: &testscommon.WhiteListHandlerStub{}, WhiteListerVerifiedTxs: &testscommon.WhiteListHandlerStub{}, AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - ArgumentsParser: &mock.ArgumentParserMock{}, + ArgumentsParser: &testscommon.ArgumentParserMock{}, PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, RequestHandler: &testscommon.RequestHandlerStub{}, PeerSignatureHandler: &mock.PeerSignatureHandlerStub{}, diff --git a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go index d05099299d5..447388221b7 100644 --- a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go +++ b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go @@ -753,7 +753,7 @@ func getArgumentsShard( AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, WhiteListHandler: &testscommon.WhiteListHandlerStub{}, WhiteListerVerifiedTxs: &testscommon.WhiteListHandlerStub{}, - ArgumentsParser: &mock.ArgumentParserMock{}, + ArgumentsParser: &testscommon.ArgumentParserMock{}, PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, RequestHandler: &testscommon.RequestHandlerStub{}, PeerSignatureHandler: &mock.PeerSignatureHandlerStub{}, diff --git a/process/headerCheck/headerSignatureVerify.go b/process/headerCheck/headerSignatureVerify.go index cce3ef6665b..3041fd22ad5 100644 --- a/process/headerCheck/headerSignatureVerify.go +++ b/process/headerCheck/headerSignatureVerify.go @@ -35,6 +35,7 @@ type ArgsHeaderSigVerifier struct { FallbackHeaderValidator process.FallbackHeaderValidator EnableEpochsHandler common.EnableEpochsHandler HeadersPool dataRetriever.HeadersPool + StorageService dataRetriever.StorageService } // HeaderSigVerifier is component used to check if a header is valid @@ -48,6 +49,7 @@ type HeaderSigVerifier struct { fallbackHeaderValidator process.FallbackHeaderValidator enableEpochsHandler common.EnableEpochsHandler headersPool dataRetriever.HeadersPool + storageService dataRetriever.StorageService } // NewHeaderSigVerifier will create a new instance of HeaderSigVerifier @@ -67,6 +69,7 @@ func NewHeaderSigVerifier(arguments *ArgsHeaderSigVerifier) (*HeaderSigVerifier, fallbackHeaderValidator: arguments.FallbackHeaderValidator, enableEpochsHandler: arguments.EnableEpochsHandler, headersPool: arguments.HeadersPool, + storageService: arguments.StorageService, }, nil } @@ -108,6 +111,9 @@ func checkArgsHeaderSigVerifier(arguments *ArgsHeaderSigVerifier) error { if check.IfNil(arguments.HeadersPool) { return process.ErrNilHeadersDataPool } + if check.IfNil(arguments.StorageService) { + return process.ErrNilStorageService + } return nil } @@ -230,6 +236,9 @@ func (hsv *HeaderSigVerifier) VerifySignature(header data.HeaderHandler) error { if hsv.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, header.GetEpoch()) { return hsv.VerifyHeaderWithProof(header) } + if prevProof := header.GetPreviousProof(); !check.IfNilReflect(prevProof) { + return ErrProofNotExpected + } headerCopy, err := hsv.copyHeaderWithoutSig(header) if err != nil { @@ -306,9 +315,52 @@ func (hsv *HeaderSigVerifier) VerifyHeaderWithProof(header data.HeaderHandler) e } prevProof := header.GetPreviousProof() + if prevProof.GetIsStartOfEpoch() { + return hsv.verifyHeaderProofAtTransition(prevProof) + } + return hsv.VerifyHeaderProof(prevProof) } +func (hsv *HeaderSigVerifier) getHeaderForProof(proof data.HeaderProofHandler) (data.HeaderHandler, error) { + headerUnit := dataRetriever.GetHeadersDataUnit(proof.GetHeaderShardId()) + headersStorer, err := hsv.storageService.GetStorer(headerUnit) + if err != nil { + return nil, err + } + + return process.GetHeader(proof.GetHeaderHash(), hsv.headersPool, headersStorer, hsv.marshalizer, proof.GetHeaderShardId()) +} + +func (hsv *HeaderSigVerifier) verifyHeaderProofAtTransition(prevProof data.HeaderProofHandler) error { + if check.IfNilReflect(prevProof) { + return process.ErrNilHeaderProof + } + header, err := hsv.getHeaderForProof(prevProof) + if err != nil { + return err + } + + consensusPubKeys, err := hsv.getConsensusSigners( + header.GetPrevRandSeed(), + prevProof.GetHeaderShardId(), + prevProof.GetHeaderEpoch(), + prevProof.GetIsStartOfEpoch(), + prevProof.GetHeaderRound(), + prevProof.GetHeaderHash(), + prevProof.GetPubKeysBitmap()) + if err != nil { + return err + } + + multiSigVerifier, err := hsv.multiSigContainer.GetMultiSigner(prevProof.GetHeaderEpoch()) + if err != nil { + return err + } + + return multiSigVerifier.VerifyAggregatedSig(consensusPubKeys, prevProof.GetHeaderHash(), prevProof.GetAggregatedSignature()) +} + // VerifyHeaderProof checks if the proof is correct for the header func (hsv *HeaderSigVerifier) VerifyHeaderProof(proofHandler data.HeaderProofHandler) error { if check.IfNilReflect(proofHandler) { diff --git a/process/headerCheck/headerSignatureVerify_test.go b/process/headerCheck/headerSignatureVerify_test.go index ffa59035043..95aa451a837 100644 --- a/process/headerCheck/headerSignatureVerify_test.go +++ b/process/headerCheck/headerSignatureVerify_test.go @@ -19,6 +19,7 @@ import ( "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/cryptoMocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" + "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" "github.com/multiversx/mx-chain-go/testscommon/shardingMocks" ) @@ -57,6 +58,7 @@ func createHeaderSigVerifierArgs() *ArgsHeaderSigVerifier { }, nil }, }, + StorageService: &genericMocks.ChainStorerMock{}, } } diff --git a/process/interceptors/factory/interceptedMetaHeaderDataFactory_test.go b/process/interceptors/factory/interceptedMetaHeaderDataFactory_test.go index 03859b63cb9..cf3ef7386d1 100644 --- a/process/interceptors/factory/interceptedMetaHeaderDataFactory_test.go +++ b/process/interceptors/factory/interceptedMetaHeaderDataFactory_test.go @@ -102,7 +102,7 @@ func createMockArgument( ValidityAttester: &mock.ValidityAttesterStub{}, HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, EpochStartTrigger: &mock.EpochStartTriggerStub{}, - ArgsParser: &mock.ArgumentParserMock{}, + ArgsParser: &testscommon.ArgumentParserMock{}, PeerSignatureHandler: &processMocks.PeerSignatureHandlerStub{}, SignaturesHandler: &processMocks.SignaturesHandlerStub{}, HeartbeatExpiryTimespanInSec: 30, diff --git a/process/interceptors/factory/interceptedTxDataFactory.go b/process/interceptors/factory/interceptedTxDataFactory.go index 563997c5066..0e1a568ad53 100644 --- a/process/interceptors/factory/interceptedTxDataFactory.go +++ b/process/interceptors/factory/interceptedTxDataFactory.go @@ -130,6 +130,7 @@ func (itdf *interceptedTxDataFactory) Create(buff []byte) (process.InterceptedDa itdf.enableEpochsHandler.IsFlagEnabled(common.TransactionSignedWithTxHashFlag), itdf.txSignHasher, itdf.txVersionChecker, + itdf.enableEpochsHandler, ) } diff --git a/process/interface.go b/process/interface.go index f983e09e1cd..09a7ac3ca30 100644 --- a/process/interface.go +++ b/process/interface.go @@ -40,6 +40,7 @@ import ( type TransactionProcessor interface { ProcessTransaction(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) VerifyTransaction(transaction *transaction.Transaction) error + VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error IsInterfaceNil() bool } @@ -69,7 +70,7 @@ type SmartContractProcessorFacade interface { // TxTypeHandler is an interface to calculate the transaction type type TxTypeHandler interface { - ComputeTransactionType(tx data.TransactionHandler) (TransactionType, TransactionType) + ComputeTransactionType(tx data.TransactionHandler) (TransactionType, TransactionType, bool) IsInterfaceNil() bool } @@ -697,6 +698,7 @@ type feeHandler interface { ComputeGasUsedAndFeeBasedOnRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsed(tx data.TransactionWithFeeHandler, gasUsed uint64) *big.Int ComputeGasLimitBasedOnBalance(tx data.TransactionWithFeeHandler, balance *big.Int) (uint64, error) + ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 ComputeTxFeeInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int ComputeGasLimitInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) uint64 ComputeGasUsedAndFeeBasedOnRefundValueInEpoch(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) (uint64, *big.Int) diff --git a/process/mock/argumentsParserMock.go b/process/mock/argumentsParserMock.go deleted file mode 100644 index 02ce8f408ae..00000000000 --- a/process/mock/argumentsParserMock.go +++ /dev/null @@ -1,60 +0,0 @@ -package mock - -import ( - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/multiversx/mx-chain-vm-common-go/parsers" -) - -// ArgumentParserMock - -type ArgumentParserMock struct { - ParseCallDataCalled func(data string) (string, [][]byte, error) - ParseArgumentsCalled func(data string) ([][]byte, error) - ParseDeployDataCalled func(data string) (*parsers.DeployArgs, error) - CreateDataFromStorageUpdateCalled func(storageUpdates []*vmcommon.StorageUpdate) string - GetStorageUpdatesCalled func(data string) ([]*vmcommon.StorageUpdate, error) -} - -// ParseCallData - -func (ap *ArgumentParserMock) ParseCallData(data string) (string, [][]byte, error) { - if ap.ParseCallDataCalled == nil { - return "", nil, nil - } - return ap.ParseCallDataCalled(data) -} - -// ParseArguments - -func (ap *ArgumentParserMock) ParseArguments(data string) ([][]byte, error) { - if ap.ParseArgumentsCalled == nil { - return [][]byte{}, nil - } - return ap.ParseArgumentsCalled(data) -} - -// ParseDeployData - -func (ap *ArgumentParserMock) ParseDeployData(data string) (*parsers.DeployArgs, error) { - if ap.ParseDeployDataCalled == nil { - return nil, nil - } - return ap.ParseDeployDataCalled(data) -} - -// CreateDataFromStorageUpdate - -func (ap *ArgumentParserMock) CreateDataFromStorageUpdate(storageUpdates []*vmcommon.StorageUpdate) string { - if ap.CreateDataFromStorageUpdateCalled == nil { - return "" - } - return ap.CreateDataFromStorageUpdateCalled(storageUpdates) -} - -// GetStorageUpdates - -func (ap *ArgumentParserMock) GetStorageUpdates(data string) ([]*vmcommon.StorageUpdate, error) { - if ap.GetStorageUpdatesCalled == nil { - return nil, nil - } - return ap.GetStorageUpdatesCalled(data) -} - -// IsInterfaceNil returns true if there is no value under the interface -func (ap *ArgumentParserMock) IsInterfaceNil() bool { - return ap == nil -} diff --git a/process/mock/multipleShardsCoordinatorMock.go b/process/mock/multipleShardsCoordinatorMock.go index bff3e16d090..27cb599cf92 100644 --- a/process/mock/multipleShardsCoordinatorMock.go +++ b/process/mock/multipleShardsCoordinatorMock.go @@ -6,6 +6,7 @@ import ( type multipleShardsCoordinatorMock struct { ComputeIdCalled func(address []byte) uint32 + SameShardCalled func(firstAddress, secondAddress []byte) bool noShards uint32 CurrentShard uint32 } @@ -44,7 +45,10 @@ func (scm *multipleShardsCoordinatorMock) SetSelfId(_ uint32) error { } // SameShard - -func (scm *multipleShardsCoordinatorMock) SameShard(_, _ []byte) bool { +func (scm *multipleShardsCoordinatorMock) SameShard(firstAddress, secondAddress []byte) bool { + if scm.SameShardCalled != nil { + return scm.SameShardCalled(firstAddress, secondAddress) + } return true } diff --git a/process/scToProtocol/stakingToPeer.go b/process/scToProtocol/stakingToPeer.go index e9b166b52ea..363a7975a7a 100644 --- a/process/scToProtocol/stakingToPeer.go +++ b/process/scToProtocol/stakingToPeer.go @@ -11,14 +11,15 @@ import ( "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-logger-go" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/vm" "github.com/multiversx/mx-chain-go/vm/systemSmartContracts" - "github.com/multiversx/mx-chain-logger-go" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) var _ process.SmartContractToProtocolHandler = (*stakingToPeer)(nil) @@ -109,6 +110,7 @@ func checkIfNil(args ArgStakingToPeer) error { return core.CheckHandlerCompatibility(args.EnableEpochsHandler, []core.EnableEpochFlag{ common.StakeFlag, common.ValidatorToDelegationFlag, + common.UnJailCleanupFlag, }) } @@ -341,6 +343,9 @@ func (stp *stakingToPeer) updatePeerState( if account.GetTempRating() < stp.unJailRating { log.Debug("node is unJailed, setting temp rating to start rating", "blsKey", blsPubKey) account.SetTempRating(stp.unJailRating) + if stp.enableEpochsHandler.IsFlagEnabled(common.UnJailCleanupFlag) { + account.SetConsecutiveProposerMisses(0) + } } isNewValidator := !isValidator && stakingData.Staked diff --git a/process/scToProtocol/stakingToPeer_test.go b/process/scToProtocol/stakingToPeer_test.go index f53495e92c9..a6f0d80bc1b 100644 --- a/process/scToProtocol/stakingToPeer_test.go +++ b/process/scToProtocol/stakingToPeer_test.go @@ -40,7 +40,7 @@ func createMockArgumentsNewStakingToPeer() ArgStakingToPeer { Marshalizer: &mock.MarshalizerStub{}, PeerState: &stateMock.AccountsStub{}, BaseState: &stateMock.AccountsStub{}, - ArgParser: &mock.ArgumentParserMock{}, + ArgParser: &testscommon.ArgumentParserMock{}, CurrTxs: &mock.TxForCurrentBlockStub{}, RatingsData: &mock.RatingsInfoMock{}, EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.StakeFlag, common.ValidatorToDelegationFlag), @@ -227,7 +227,7 @@ func TestStakingToPeer_UpdateProtocolCannotGetStorageUpdatesShouldErr(t *testing }, nil } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return nil, testError } @@ -252,7 +252,7 @@ func TestStakingToPeer_UpdateProtocolRemoveAccountShouldReturnNil(t *testing.T) }, nil } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: []byte("aabbcc"), Data: []byte("data1")}, @@ -311,7 +311,7 @@ func TestStakingToPeer_UpdateProtocolCannotSetRewardAddressShouldErr(t *testing. offset = append(offset, 99) } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: offset, Data: []byte("data1")}, @@ -368,7 +368,7 @@ func TestStakingToPeer_UpdateProtocolEmptyDataShouldNotAddToTrie(t *testing.T) { offset = append(offset, 99) } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: offset, Data: []byte("data1")}, @@ -429,7 +429,7 @@ func TestStakingToPeer_UpdateProtocolCannotSaveAccountShouldErr(t *testing.T) { offset = append(offset, 99) } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: offset, Data: []byte("data1")}, @@ -492,7 +492,7 @@ func TestStakingToPeer_UpdateProtocolCannotSaveAccountNonceShouldErr(t *testing. offset = append(offset, 99) } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: offset, Data: []byte("data1")}, @@ -554,7 +554,7 @@ func TestStakingToPeer_UpdateProtocol(t *testing.T) { offset = append(offset, 99) } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: offset, Data: []byte("data1")}, @@ -617,7 +617,7 @@ func TestStakingToPeer_UpdateProtocolCannotSaveUnStakedNonceShouldErr(t *testing offset = append(offset, 99) } - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} argParser.GetStorageUpdatesCalled = func(data string) (updates []*vmcommon.StorageUpdate, e error) { return []*vmcommon.StorageUpdate{ {Offset: offset, Data: []byte("data1")}, diff --git a/process/smartContract/process.go b/process/smartContract/process.go index 25031dcbf4a..8fbabd38df3 100644 --- a/process/smartContract/process.go +++ b/process/smartContract/process.go @@ -955,7 +955,7 @@ func (sc *scProcessor) doExecuteBuiltInFunction( return sc.finishSCExecution(make([]data.TransactionHandler, 0), txHash, tx, vmOutput, 0) } - _, txTypeOnDst := sc.txTypeHandler.ComputeTransactionType(tx) + _, txTypeOnDst, _ := sc.txTypeHandler.ComputeTransactionType(tx) builtInFuncGasUsed, err := sc.computeBuiltInFuncGasUsed(txTypeOnDst, vmInput.Function, vmInput.GasProvided, vmOutput.GasRemaining, check.IfNil(acntSnd)) log.LogIfError(err, "function", "ExecuteBuiltInFunction.computeBuiltInFuncGasUsed") @@ -1473,7 +1473,7 @@ func (sc *scProcessor) processIfErrorWithAddedLogs( Logs: processIfErrorLogs, }, 0) - txType, _ := sc.txTypeHandler.ComputeTransactionType(tx) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(tx) isCrossShardMoveBalance := txType == process.MoveBalance && check.IfNil(acntSnd) if isCrossShardMoveBalance && sc.enableEpochsHandler.IsFlagEnabled(common.SCDeployFlag) { // move balance was already consumed in sender shard @@ -2808,7 +2808,7 @@ func (sc *scProcessor) ProcessSmartContractResult(scr *smartContractResult.Smart gasLocked := sc.getGasLockedFromSCR(scr) - txType, _ := sc.txTypeHandler.ComputeTransactionType(scr) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(scr) switch txType { case process.MoveBalance: err = sc.processSimpleSCR(scr, txHash, dstAcc) diff --git a/process/smartContract/processProxy/processProxy_test.go b/process/smartContract/processProxy/processProxy_test.go index 0b5695386a8..d153615600f 100644 --- a/process/smartContract/processProxy/processProxy_test.go +++ b/process/smartContract/processProxy/processProxy_test.go @@ -39,7 +39,7 @@ func createMockSmartContractProcessorArguments() scrCommon.ArgsNewSmartContractP return scrCommon.ArgsNewSmartContractProcessor{ VmContainer: &mock.VMContainerMock{}, - ArgsParser: &mock.ArgumentParserMock{}, + ArgsParser: &testscommon.ArgumentParserMock{}, Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, AccountsDB: &stateMock.AccountsStub{ diff --git a/process/smartContract/process_test.go b/process/smartContract/process_test.go index eb80ea63322..b6c81113f45 100644 --- a/process/smartContract/process_test.go +++ b/process/smartContract/process_test.go @@ -84,7 +84,7 @@ func createMockSmartContractProcessorArguments() scrCommon.ArgsNewSmartContractP return scrCommon.ArgsNewSmartContractProcessor{ VmContainer: &mock.VMContainerMock{}, - ArgsParser: &mock.ArgumentParserMock{}, + ArgsParser: &testscommon.ArgumentParserMock{}, Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, AccountsDB: &stateMock.AccountsStub{ @@ -458,7 +458,7 @@ func TestGasScheduleChangeShouldWork(t *testing.T) { func TestScProcessor_DeploySmartContractBadParse(t *testing.T) { t.Parallel() - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = &mock.VMContainerMock{} arguments.ArgsParser = argParser @@ -888,7 +888,7 @@ func TestScProcessor_DeploySmartContractWrongTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -910,7 +910,7 @@ func TestScProcessor_DeploySmartContractNilTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -932,7 +932,7 @@ func TestScProcessor_DeploySmartContractNotEmptyDestinationAddress(t *testing.T) t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -955,7 +955,7 @@ func TestScProcessor_DeploySmartContractCalculateHashFails(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -987,7 +987,7 @@ func TestScProcessor_DeploySmartContractEconomicsFeeValidateFails(t *testing.T) expectedError := errors.New("expected error") vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1018,7 +1018,7 @@ func TestScProcessor_DeploySmartContractEconomicsFeeSaveAccountsFails(t *testing expectedError := errors.New("expected error") vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1477,7 +1477,7 @@ func TestScProcessor_ExecuteSmartContractTransactionNilTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1501,7 +1501,7 @@ func TestScProcessor_ExecuteSmartContractTransactionNilAccount(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1534,7 +1534,7 @@ func TestScProcessor_ExecuteSmartContractTransactionBadParser(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1566,7 +1566,7 @@ func TestScProcessor_ExecuteSmartContractTransactionVMRunError(t *testing.T) { t.Parallel() vmContainer := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vmContainer arguments.ArgsParser = argParser @@ -1703,7 +1703,7 @@ func TestScProcessor_ExecuteSmartContractTransaction(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm @@ -1736,7 +1736,7 @@ func TestScProcessor_ExecuteSmartContractTransactionSaveLogCalled(t *testing.T) slCalled := false vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm @@ -1773,7 +1773,7 @@ func TestScProcessor_CreateVMCallInputWrongCode(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1801,7 +1801,7 @@ func TestScProcessor_CreateVMCallInput(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1825,7 +1825,7 @@ func TestScProcessor_CreateVMDeployBadCode(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1852,7 +1852,7 @@ func TestScProcessor_CreateVMDeployInput(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1916,7 +1916,7 @@ func TestScProcessor_CreateVMDeployInputWrongArgument(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1945,7 +1945,7 @@ func TestScProcessor_InitializeVMInputFromTx_ShouldErrNotEnoughGas(t *testing.T) t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1975,7 +1975,7 @@ func TestScProcessor_InitializeVMInputFromTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2012,7 +2012,7 @@ func TestScProcessor_processVMOutputNilSndAcc(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2041,7 +2041,7 @@ func TestScProcessor_processVMOutputNilDstAcc(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm @@ -2085,7 +2085,7 @@ func TestScProcessor_GetAccountFromAddressAccNotFound(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2116,7 +2116,7 @@ func TestScProcessor_GetAccountFromAddrFailedGetExistingAccount(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2148,7 +2148,7 @@ func TestScProcessor_GetAccountFromAddrAccNotInShard(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2181,7 +2181,7 @@ func TestScProcessor_GetAccountFromAddr(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2216,7 +2216,7 @@ func TestScProcessor_DeleteAccountsFailedAtRemove(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2251,7 +2251,7 @@ func TestScProcessor_DeleteAccountsNotInShard(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2290,7 +2290,7 @@ func TestScProcessor_DeleteAccountsInShard(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -3196,8 +3196,8 @@ func TestScProcessor_ProcessSmartContractResultDeploySCShouldError(t *testing.T) arguments.AccountsDB = accountsDB arguments.ShardCoordinator = shardCoordinator arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCDeployment, process.SCDeployment + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCDeployment, process.SCDeployment, false }, } sc, err := NewSmartContractProcessor(arguments) @@ -3257,8 +3257,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSC(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } sc, err := NewSmartContractProcessor(arguments) @@ -3320,8 +3320,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSCIfMetaAndBuiltIn(t *test }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.SCDeployFlag) @@ -3394,8 +3394,8 @@ func TestScProcessor_ProcessRelayedSCRValueBackToRelayer(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } sc, err := NewSmartContractProcessor(arguments) @@ -4396,7 +4396,7 @@ func TestScProcessor_CheckBuiltinFunctionIsExecutable(t *testing.T) { }) t.Run("", func(t *testing.T) { argsCopy := arguments - argsCopy.ArgsParser = &mock.ArgumentParserMock{ + argsCopy.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "", nil, expectedErr }, @@ -4407,7 +4407,7 @@ func TestScProcessor_CheckBuiltinFunctionIsExecutable(t *testing.T) { }) t.Run("expected builtin function different than the parsed function name should return error", func(t *testing.T) { argsCopy := arguments - argsCopy.ArgsParser = &mock.ArgumentParserMock{ + argsCopy.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "differentFunction", nil, nil }, @@ -4418,7 +4418,7 @@ func TestScProcessor_CheckBuiltinFunctionIsExecutable(t *testing.T) { }) t.Run("prepare gas provided with error should error", func(t *testing.T) { argsCopy := arguments - argsCopy.ArgsParser = &mock.ArgumentParserMock{ + argsCopy.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "SetGuardian", nil, nil }, @@ -4436,7 +4436,7 @@ func TestScProcessor_CheckBuiltinFunctionIsExecutable(t *testing.T) { }) t.Run("builtin function not found should error", func(t *testing.T) { argsCopy := arguments - argsCopy.ArgsParser = &mock.ArgumentParserMock{ + argsCopy.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "SetGuardian", nil, nil }, @@ -4457,7 +4457,7 @@ func TestScProcessor_CheckBuiltinFunctionIsExecutable(t *testing.T) { }) t.Run("builtin function not supporting executable check should error", func(t *testing.T) { argsCopy := arguments - argsCopy.ArgsParser = &mock.ArgumentParserMock{ + argsCopy.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "SetGuardian", nil, nil }, @@ -4477,7 +4477,7 @@ func TestScProcessor_CheckBuiltinFunctionIsExecutable(t *testing.T) { }) t.Run("OK", func(t *testing.T) { argsCopy := arguments - argsCopy.ArgsParser = &mock.ArgumentParserMock{ + argsCopy.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "SetGuardian", nil, nil }, diff --git a/process/smartContract/processorV2/processV2.go b/process/smartContract/processorV2/processV2.go index 55aff2b72a0..e0d88916a52 100644 --- a/process/smartContract/processorV2/processV2.go +++ b/process/smartContract/processorV2/processV2.go @@ -805,15 +805,17 @@ func (sc *scProcessor) deleteSCRsWithValueZeroGoingToMeta(scrs []data.Transactio } func (sc *scProcessor) saveAccounts(acntSnd, acntDst vmcommon.AccountHandler) error { - if !check.IfNil(acntSnd) { - err := sc.accounts.SaveAccount(acntSnd) - if err != nil { - return err - } + err := sc.saveAccount(acntSnd) + if err != nil { + return err } - if !check.IfNil(acntDst) { - err := sc.accounts.SaveAccount(acntDst) + return sc.saveAccount(acntDst) +} + +func (sc *scProcessor) saveAccount(account vmcommon.AccountHandler) error { + if !check.IfNil(account) { + err := sc.accounts.SaveAccount(account) if err != nil { return err } @@ -931,7 +933,7 @@ func (sc *scProcessor) doExecuteBuiltInFunctionWithoutFailureProcessing( return sc.finishSCExecution(make([]data.TransactionHandler, 0), txHash, tx, vmOutput, 0) } - _, txTypeOnDst := sc.txTypeHandler.ComputeTransactionType(tx) + _, txTypeOnDst, _ := sc.txTypeHandler.ComputeTransactionType(tx) builtInFuncGasUsed, err := sc.computeBuiltInFuncGasUsed(txTypeOnDst, vmInput.Function, vmInput.GasProvided, vmOutput.GasRemaining, check.IfNil(acntSnd)) log.LogIfError(err, "function", "ExecuteBuiltInFunction.computeBuiltInFuncGasUsed") @@ -1406,8 +1408,9 @@ func (sc *scProcessor) isCrossShardESDTTransfer(sender []byte, receiver []byte, func (sc *scProcessor) getOriginalTxHashIfIntraShardRelayedSCR( tx data.TransactionHandler, - txHash []byte) []byte { - relayedSCR, isRelayed := isRelayedTx(tx) + txHash []byte, +) []byte { + relayedSCR, isRelayed := isRelayedSCR(tx) if !isRelayed { return txHash } @@ -1511,10 +1514,10 @@ func (sc *scProcessor) processIfErrorWithAddedLogs(acntSnd state.UserAccountHand logsTxHash := sc.getOriginalTxHashIfIntraShardRelayedSCR(tx, failureContext.txHash) ignorableError := sc.txLogsProcessor.SaveLog(logsTxHash, tx, processIfErrorLogs) if ignorableError != nil { - log.Debug("scProcessor.ProcessIfError() txLogsProcessor.SaveLog()", "error", ignorableError.Error()) + log.Debug("scProcessor.ProcessIfError() save log", "error", ignorableError.Error()) } - txType, _ := sc.txTypeHandler.ComputeTransactionType(tx) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(tx) isCrossShardMoveBalance := txType == process.MoveBalance && check.IfNil(acntSnd) if isCrossShardMoveBalance { // move balance was already consumed in sender shard @@ -1577,7 +1580,7 @@ func (sc *scProcessor) processForRelayerWhenError( txHash []byte, returnMessage []byte, ) (*vmcommon.LogEntry, error) { - relayedSCR, isRelayed := isRelayedTx(originalTx) + relayedSCR, isRelayed := isRelayedSCR(originalTx) if !isRelayed { return nil, nil } @@ -1663,7 +1666,7 @@ func createNewLogFromSCRIfError(txHandler data.TransactionHandler) *vmcommon.Log } // transaction must be of type SCR and relayed address to be set with relayed value higher than 0 -func isRelayedTx(tx data.TransactionHandler) (*smartContractResult.SmartContractResult, bool) { +func isRelayedSCR(tx data.TransactionHandler) (*smartContractResult.SmartContractResult, bool) { relayedSCR, ok := tx.(*smartContractResult.SmartContractResult) if !ok { return nil, false @@ -1676,6 +1679,20 @@ func isRelayedTx(tx data.TransactionHandler) (*smartContractResult.SmartContract return nil, false } +func getRelayedValues(tx data.TransactionHandler) ([]byte, *big.Int) { + relayedTx, isRelayed := isRelayedSCR(tx) + if isRelayed { + return relayedTx.RelayerAddr, big.NewInt(0) + } + + if common.IsRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + return relayedTx.GetRelayerAddr(), big.NewInt(0) + } + + return nil, nil +} + // refunds the transaction values minus the relayed value to the sender account // in case of failed smart contract execution - gas is consumed, value is sent back func (sc *scProcessor) addBackTxValues( @@ -1685,7 +1702,7 @@ func (sc *scProcessor) addBackTxValues( ) error { valueForSnd := big.NewInt(0).Set(scrIfError.Value) - relayedSCR, isRelayed := isRelayedTx(originalTx) + relayedSCR, isRelayed := isRelayedSCR(originalTx) if isRelayed { valueForSnd.Sub(valueForSnd, relayedSCR.RelayedValue) if valueForSnd.Cmp(zero) < 0 { @@ -1920,14 +1937,25 @@ func (sc *scProcessor) processSCPayment(tx data.TransactionHandler, acntSnd stat return err } - cost := sc.economicsFee.ComputeTxFee(tx) - cost = cost.Add(cost, tx.GetValue()) + feePayer, err := sc.getFeePayer(tx, acntSnd) + if err != nil { + return err + } + + fee := sc.economicsFee.ComputeTxFee(tx) + if !check.IfNil(feePayer) { + err = feePayer.SubFromBalance(fee) + if err != nil { + return err + } - if cost.Cmp(big.NewInt(0)) == 0 { - return nil + err = sc.saveAccount(feePayer) + if err != nil { + return err + } } - err = acntSnd.SubFromBalance(cost) + err = acntSnd.SubFromBalance(tx.GetValue()) if err != nil { return err } @@ -1935,6 +1963,29 @@ func (sc *scProcessor) processSCPayment(tx data.TransactionHandler, acntSnd stat return nil } +func (sc *scProcessor) getFeePayer(tx data.TransactionHandler, acntSnd state.UserAccountHandler) (state.UserAccountHandler, error) { + if !common.IsRelayedTxV3(tx) { + return acntSnd, nil + } + + relayedTx, ok := tx.(data.RelayedTransactionHandler) + if !ok { + return acntSnd, nil + } + + relayerIsSender := bytes.Equal(relayedTx.GetRelayerAddr(), tx.GetSndAddr()) + if relayerIsSender { + return acntSnd, nil // do not load the same account twice + } + + account, err := sc.getAccountFromAddress(relayedTx.GetRelayerAddr()) + if err != nil { + return nil, err + } + + return account, nil +} + func (sc *scProcessor) processVMOutput( vmInput *vmcommon.VMInput, vmOutput *vmcommon.VMOutput, @@ -2256,11 +2307,7 @@ func createBaseSCR( result.CallType = vmData.DirectCall setOriginalTxHash(result, txHash, tx) - relayedTx, isRelayed := isRelayedTx(tx) - if isRelayed { - result.RelayedValue = big.NewInt(0) - result.RelayerAddr = relayedTx.RelayerAddr - } + result.RelayerAddr, result.RelayedValue = getRelayedValues(tx) return result } @@ -2298,11 +2345,8 @@ func (sc *scProcessor) createAsyncCallBackSCRFromVMOutput( OriginalSender: origScr.GetOriginalSender(), } setOriginalTxHash(scr, txHash, tx) - relayedTx, isRelayed := isRelayedTx(tx) - if isRelayed { - scr.RelayedValue = big.NewInt(0) - scr.RelayerAddr = relayedTx.RelayerAddr - } + + scr.RelayerAddr, scr.RelayedValue = getRelayedValues(tx) sc.addVMOutputResultsToSCR(vmOutput, scr) @@ -2567,29 +2611,7 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( rcvAddress = tx.GetRcvAddr() } - var refundGasToRelayerSCR *smartContractResult.SmartContractResult - relayedSCR, isRelayed := isRelayedTx(tx) - shouldRefundGasToRelayerSCR := isRelayed && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 - if shouldRefundGasToRelayerSCR { - senderForRelayerRefund := tx.GetRcvAddr() - if !sc.isSelfShard(tx.GetRcvAddr()) { - senderForRelayerRefund = tx.GetSndAddr() - } - - refundGasToRelayerSCR = &smartContractResult.SmartContractResult{ - Nonce: relayedSCR.Nonce + 1, - Value: big.NewInt(0).Set(gasRefund), - RcvAddr: relayedSCR.RelayerAddr, - SndAddr: senderForRelayerRefund, - PrevTxHash: txHash, - OriginalTxHash: relayedSCR.OriginalTxHash, - GasPrice: tx.GetGasPrice(), - CallType: vmData.DirectCall, - ReturnMessage: []byte("gas refund for relayer"), - OriginalSender: relayedSCR.OriginalSender, - } - gasRemaining = 0 - } + refundGasToRelayerSCR := sc.createRefundGasToRelayerSCRIfNeeded(tx, txHash, callType, gasRefund) scTx := &smartContractResult.SmartContractResult{} scTx.Value = big.NewInt(0).Set(storageFreeRefund) @@ -2620,6 +2642,71 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( return scTx, refundGasToRelayerSCR } +func (sc *scProcessor) createRefundGasToRelayerSCRIfNeeded( + tx data.TransactionHandler, + txHash []byte, + callType vmData.CallType, + gasRefund *big.Int, +) *smartContractResult.SmartContractResult { + relayedSCR, isRelayed := isRelayedSCR(tx) + shouldRefundGasToRelayerSCR := isRelayed && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 + if shouldRefundGasToRelayerSCR { + return sc.createRefundGasToRelayerSCR( + tx, + relayedSCR.Nonce+1, + relayedSCR.RelayerAddr, + relayedSCR.OriginalSender, + txHash, + relayedSCR.OriginalTxHash, + gasRefund) + } + + isRelayedV3 := common.IsRelayedTxV3(tx) + shouldRefundGasToRelayerSCR = isRelayedV3 && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 + if shouldRefundGasToRelayerSCR { + relayedTx := tx.(data.RelayedTransactionHandler) + + return sc.createRefundGasToRelayerSCR( + tx, + tx.GetNonce()+1, + relayedTx.GetRelayerAddr(), + tx.GetSndAddr(), + txHash, + txHash, + gasRefund) + } + + return nil +} + +func (sc *scProcessor) createRefundGasToRelayerSCR( + tx data.TransactionHandler, + nonce uint64, + relayerAddr []byte, + originalSender []byte, + prevTxHash []byte, + originalTxHash []byte, + refund *big.Int, +) *smartContractResult.SmartContractResult { + senderForRelayerRefund := tx.GetRcvAddr() + if !sc.isSelfShard(tx.GetRcvAddr()) { + senderForRelayerRefund = tx.GetSndAddr() + } + + return &smartContractResult.SmartContractResult{ + Nonce: nonce, + Value: big.NewInt(0).Set(refund), + RcvAddr: relayerAddr, + SndAddr: senderForRelayerRefund, + PrevTxHash: prevTxHash, + OriginalTxHash: originalTxHash, + GasPrice: tx.GetGasPrice(), + CallType: vmData.DirectCall, + ReturnMessage: []byte("gas refund for relayer"), + OriginalSender: originalSender, + } +} + func addReturnDataToSCR(vmOutput *vmcommon.VMOutput, scTx *smartContractResult.SmartContractResult) { for _, retData := range vmOutput.ReturnData { scTx.Data = append(scTx.Data, []byte("@"+hex.EncodeToString(retData))...) @@ -2719,7 +2806,7 @@ func (sc *scProcessor) ProcessSmartContractResult(scr *smartContractResult.Smart gasLocked := sc.getGasLockedFromSCR(scr) - txType, _ := sc.txTypeHandler.ComputeTransactionType(scr) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(scr) switch txType { case process.MoveBalance: err = sc.processSimpleSCR(scr, txHash, dstAcc) diff --git a/process/smartContract/processorV2/process_test.go b/process/smartContract/processorV2/process_test.go index 8919006995f..638e096005e 100644 --- a/process/smartContract/processorV2/process_test.go +++ b/process/smartContract/processorV2/process_test.go @@ -94,7 +94,7 @@ func createMockSmartContractProcessorArguments() scrCommon.ArgsNewSmartContractP return scrCommon.ArgsNewSmartContractProcessor{ VmContainer: &mock.VMContainerMock{}, - ArgsParser: &mock.ArgumentParserMock{}, + ArgsParser: &testscommon.ArgumentParserMock{}, Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, AccountsDB: &stateMock.AccountsStub{ @@ -439,7 +439,7 @@ func createTxLogsProcessor() process.TransactionLogProcessor { func TestScProcessor_DeploySmartContractBadParse(t *testing.T) { t.Parallel() - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = &mock.VMContainerMock{} arguments.ArgsParser = argParser @@ -909,7 +909,7 @@ func TestScProcessor_DeploySmartContractWrongTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -931,7 +931,7 @@ func TestScProcessor_DeploySmartContractNilTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -953,7 +953,7 @@ func TestScProcessor_DeploySmartContractNotEmptyDestinationAddress(t *testing.T) t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -976,7 +976,7 @@ func TestScProcessor_DeploySmartContractCalculateHashFails(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1008,7 +1008,7 @@ func TestScProcessor_DeploySmartContractEconomicsFeeValidateFails(t *testing.T) expectedError := errors.New("expected error") vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1039,7 +1039,7 @@ func TestScProcessor_DeploySmartContractEconomicsFeeSaveAccountsFails(t *testing expectedError := errors.New("expected error") vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1498,7 +1498,7 @@ func TestScProcessor_ExecuteSmartContractTransactionNilTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1522,7 +1522,7 @@ func TestScProcessor_ExecuteSmartContractTransactionNilAccount(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1555,7 +1555,7 @@ func TestScProcessor_ExecuteSmartContractTransactionBadParser(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1587,7 +1587,7 @@ func TestScProcessor_ExecuteSmartContractTransactionVMRunError(t *testing.T) { t.Parallel() vmContainer := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vmContainer arguments.ArgsParser = argParser @@ -1724,7 +1724,7 @@ func TestScProcessor_ExecuteSmartContractTransaction(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm @@ -1757,7 +1757,7 @@ func TestScProcessor_ExecuteSmartContractTransactionSaveLogCalled(t *testing.T) slCalled := false vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm @@ -1794,7 +1794,7 @@ func TestScProcessor_CreateVMCallInputWrongCode(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1822,7 +1822,7 @@ func TestScProcessor_CreateVMCallInput(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1846,7 +1846,7 @@ func TestScProcessor_CreateVMDeployBadCode(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1873,7 +1873,7 @@ func TestScProcessor_CreateVMCallInputBadAsync(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1903,7 +1903,7 @@ func TestScProcessor_CreateVMDeployInput(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1967,7 +1967,7 @@ func TestScProcessor_CreateVMDeployInputWrongArgument(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -1996,7 +1996,7 @@ func TestScProcessor_InitializeVMInputFromTx_ShouldErrNotEnoughGas(t *testing.T) t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2026,7 +2026,7 @@ func TestScProcessor_InitializeVMInputFromTx(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2063,7 +2063,7 @@ func TestScProcessor_processVMOutputNilSndAcc(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2092,7 +2092,7 @@ func TestScProcessor_processVMOutputNilDstAcc(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm @@ -2136,7 +2136,7 @@ func TestScProcessor_GetAccountFromAddressAccNotFound(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2167,7 +2167,7 @@ func TestScProcessor_GetAccountFromAddrFailedGetExistingAccount(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2199,7 +2199,7 @@ func TestScProcessor_GetAccountFromAddrAccNotInShard(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2232,7 +2232,7 @@ func TestScProcessor_GetAccountFromAddr(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2267,7 +2267,7 @@ func TestScProcessor_DeleteAccountsFailedAtRemove(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2302,7 +2302,7 @@ func TestScProcessor_DeleteAccountsNotInShard(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -2342,7 +2342,7 @@ func TestScProcessor_DeleteAccountsInShard(t *testing.T) { } vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm arguments.ArgsParser = argParser @@ -3129,8 +3129,8 @@ func TestScProcessor_ProcessSmartContractResultDeploySCShouldError(t *testing.T) arguments.AccountsDB = accountsDB arguments.ShardCoordinator = shardCoordinator arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCDeployment, process.SCDeployment + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCDeployment, process.SCDeployment, false }, } sc, err := NewSmartContractProcessorV2(arguments) @@ -3190,8 +3190,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSC(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } sc, err := NewSmartContractProcessorV2(arguments) @@ -3253,8 +3253,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSCIfMetaAndBuiltIn(t *test }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() @@ -3327,8 +3327,15 @@ func TestScProcessor_ProcessRelayedSCRValueBackToRelayer(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false + }, + } + wasSaveLogsCalled := false + arguments.TxLogsProcessor = &mock.TxLogsProcessorStub{ + SaveLogCalled: func(txHash []byte, tx data.TransactionHandler, logs []*vmcommon.LogEntry) error { + wasSaveLogsCalled = true + return nil }, } sc, err := NewSmartContractProcessorV2(arguments) @@ -3353,6 +3360,7 @@ func TestScProcessor_ProcessRelayedSCRValueBackToRelayer(t *testing.T) { userFinalValue := baseValue.Sub(baseValue, scr.Value) userFinalValue.Add(userFinalValue, userReturnValue) require.True(t, userAcc.GetBalance().Cmp(userFinalValue) == 0) + require.True(t, wasSaveLogsCalled) } func TestScProcessor_checkUpgradePermission(t *testing.T) { @@ -4344,7 +4352,7 @@ func TestSCProcessor_PrependAsyncParamsToData(t *testing.T) { func TestScProcessor_ForbidMultiLevelAsync(t *testing.T) { t.Parallel() vm := &mock.VMContainerMock{} - argParser := &mock.ArgumentParserMock{} + argParser := &testscommon.ArgumentParserMock{} accntState := &stateMock.AccountsStub{} arguments := createMockSmartContractProcessorArguments() arguments.VmContainer = vm diff --git a/process/smartContract/processorV2/testScProcessor.go b/process/smartContract/processorV2/testScProcessor.go index 0e8e643605f..52a63c6c308 100644 --- a/process/smartContract/processorV2/testScProcessor.go +++ b/process/smartContract/processorV2/testScProcessor.go @@ -2,6 +2,7 @@ package processorV2 import ( "encoding/hex" + "errors" "fmt" "strings" @@ -79,7 +80,7 @@ func (tsp *TestScProcessor) GetCompositeTestError() error { func wrapErrorIfNotContains(originalError error, msg string) error { if originalError == nil { - return fmt.Errorf(msg) + return errors.New(msg) } alreadyContainsMessage := strings.Contains(originalError.Error(), msg) diff --git a/process/smartContract/processorV2/vmInputV2.go b/process/smartContract/processorV2/vmInputV2.go index 35e68776907..81dd1f9360c 100644 --- a/process/smartContract/processorV2/vmInputV2.go +++ b/process/smartContract/processorV2/vmInputV2.go @@ -39,6 +39,9 @@ func (sc *scProcessor) initializeVMInputFromTx(vmInput *vmcommon.VMInput, tx dat vmInput.CallerAddr = tx.GetSndAddr() vmInput.CallValue = new(big.Int).Set(tx.GetValue()) vmInput.GasPrice = tx.GetGasPrice() + + vmInput.RelayerAddr, _ = getRelayedValues(tx) + vmInput.GasProvided, err = sc.prepareGasProvided(tx) if err != nil { return err diff --git a/process/smartContract/scrCommon/common.go b/process/smartContract/scrCommon/common.go index 957abe5800b..8cd8efd6484 100644 --- a/process/smartContract/scrCommon/common.go +++ b/process/smartContract/scrCommon/common.go @@ -1,6 +1,8 @@ package scrCommon import ( + "math/big" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/hashing" @@ -12,7 +14,6 @@ import ( "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/storage" vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "math/big" ) // TestSmartContractProcessor is a SmartContractProcessor used in integration tests diff --git a/process/smartContract/testScProcessor.go b/process/smartContract/testScProcessor.go index a13419ab621..d602619c61e 100644 --- a/process/smartContract/testScProcessor.go +++ b/process/smartContract/testScProcessor.go @@ -2,6 +2,7 @@ package smartContract import ( "encoding/hex" + "errors" "fmt" "strings" @@ -83,7 +84,7 @@ func (tsp *TestScProcessor) GetCompositeTestError() error { func wrapErrorIfNotContains(originalError error, msg string) error { if originalError == nil { - return fmt.Errorf(msg) + return errors.New(msg) } alreadyContainsMessage := strings.Contains(originalError.Error(), msg) diff --git a/process/sync/baseForkDetector.go b/process/sync/baseForkDetector.go index 77f8c8841f4..3fe907d954b 100644 --- a/process/sync/baseForkDetector.go +++ b/process/sync/baseForkDetector.go @@ -760,11 +760,13 @@ func (bfd *baseForkDetector) processReceivedBlock( bfd.setHighestNonceReceived(header.GetNonce()) if state == process.BHProposed || !hasProof { + log.Trace("forkDetector.processReceivedBlock: block is proposed or has no proof", "state", state, "has proof", hasProof) return } isHeaderReceivedTooLate := bfd.isHeaderReceivedTooLate(header, state, process.BlockFinality) if isHeaderReceivedTooLate { + log.Trace("forkDetector.processReceivedBlock: block is received too late", "initial state", state) state = process.BHReceivedTooLate } @@ -778,6 +780,7 @@ func (bfd *baseForkDetector) processReceivedBlock( } if !bfd.append(hInfo) { + log.Trace("forkDetector.processReceivedBlock: header not appended", "nonce", hInfo.nonce, "hash", hInfo.hash) return } diff --git a/process/sync/metaForkDetector.go b/process/sync/metaForkDetector.go index f6a285fb3bc..cb45f2fdadc 100644 --- a/process/sync/metaForkDetector.go +++ b/process/sync/metaForkDetector.go @@ -108,7 +108,11 @@ func (mfd *metaForkDetector) doJobOnBHProcessed( _ [][]byte, ) { mfd.setFinalCheckpoint(mfd.lastCheckpoint()) - mfd.addCheckpoint(&checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash}) + newCheckpoint := &checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash} + mfd.addCheckpoint(newCheckpoint) + if mfd.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, header.GetEpoch()) { + mfd.setFinalCheckpoint(newCheckpoint) + } mfd.removePastOrInvalidRecords() } diff --git a/process/sync/shardForkDetector.go b/process/sync/shardForkDetector.go index d688c548a2e..499e232782e 100644 --- a/process/sync/shardForkDetector.go +++ b/process/sync/shardForkDetector.go @@ -112,7 +112,13 @@ func (sfd *shardForkDetector) doJobOnBHProcessed( ) { _ = sfd.appendSelfNotarizedHeaders(selfNotarizedHeaders, selfNotarizedHeadersHashes, core.MetachainShardId) sfd.computeFinalCheckpoint() - sfd.addCheckpoint(&checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash}) + newCheckpoint := &checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash} + sfd.addCheckpoint(newCheckpoint) + // first shard block with proof does not have increased consensus + // so instant finality will only be set after the first block with increased consensus + if common.ShouldBlockHavePrevProof(header, sfd.enableEpochsHandler, common.EquivalentMessagesFlag) { + sfd.setFinalCheckpoint(newCheckpoint) + } sfd.removePastOrInvalidRecords() } diff --git a/process/track/baseBlockTrack.go b/process/track/baseBlockTrack.go index bd3a288ca3c..7e3325dae20 100644 --- a/process/track/baseBlockTrack.go +++ b/process/track/baseBlockTrack.go @@ -131,7 +131,7 @@ func (bbt *baseBlockTrack) receivedProof(proof data.HeaderProofHandler) { } headerHash := proof.GetHeaderHash() - header, err := bbt.headersPool.GetHeaderByHash(headerHash) + header, err := bbt.getHeaderForProof(proof) if err != nil { log.Debug("baseBlockTrack.receivedProof with missing header", "headerHash", headerHash) return @@ -147,6 +147,16 @@ func (bbt *baseBlockTrack) receivedProof(proof data.HeaderProofHandler) { bbt.receivedHeader(header, headerHash) } +func (bbt *baseBlockTrack) getHeaderForProof(proof data.HeaderProofHandler) (data.HeaderHandler, error) { + headerUnit := dataRetriever.GetHeadersDataUnit(proof.GetHeaderShardId()) + headersStorer, err := bbt.store.GetStorer(headerUnit) + if err != nil { + return nil, err + } + + return process.GetHeader(proof.GetHeaderHash(), bbt.headersPool, headersStorer, bbt.marshalizer, proof.GetHeaderShardId()) +} + func (bbt *baseBlockTrack) receivedHeader(headerHandler data.HeaderHandler, headerHash []byte) { if bbt.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, headerHandler.GetEpoch()) { if !bbt.proofsPool.HasProof(headerHandler.GetShardID(), headerHash) { diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 45e9f2aac13..0433f8a0f50 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -29,6 +29,7 @@ type baseTxProcessor struct { enableEpochsHandler common.EnableEpochsHandler txVersionChecker process.TxVersionCheckerHandler guardianChecker process.GuardianChecker + txTypeHandler process.TxTypeHandler } func (txProc *baseTxProcessor) getAccounts( @@ -118,7 +119,17 @@ func (txProc *baseTxProcessor) checkTxValues( acntSnd, acntDst state.UserAccountHandler, isUserTxOfRelayed bool, ) error { - err := txProc.verifyGuardian(tx, acntSnd) + + if common.IsRelayedTxV3(tx) { + relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return err + } + + return txProc.checkUserTxOfRelayedV3Values(tx, acntSnd, acntDst, relayerAccount) + } + + err := txProc.VerifyGuardian(tx, acntSnd) if err != nil { return err } @@ -145,7 +156,8 @@ func (txProc *baseTxProcessor) checkTxValues( if tx.GasLimit < txProc.economicsFee.ComputeGasLimit(tx) { return process.ErrNotEnoughGasInUserTx } - txFee = txProc.economicsFee.ComputeFeeForProcessing(tx, tx.GasLimit) + + txFee = txProc.computeInnerTxFee(tx) } else { txFee = txProc.economicsFee.ComputeTxFee(tx) } @@ -172,6 +184,109 @@ func (txProc *baseTxProcessor) checkTxValues( return nil } +func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( + tx *transaction.Transaction, + senderAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, + relayerAccount state.UserAccountHandler, +) error { + err := txProc.VerifyGuardian(tx, senderAccount) + if err != nil { + return err + } + err = txProc.checkUserNames(tx, senderAccount, destinationAccount) + if err != nil { + return err + } + if check.IfNil(senderAccount) { + return nil + } + if senderAccount.GetNonce() < tx.Nonce { + return process.ErrHigherNonceInTransaction + } + if senderAccount.GetNonce() > tx.Nonce { + return process.ErrLowerNonceInTransaction + } + err = txProc.economicsFee.CheckValidityTxValues(tx) + if err != nil { + return err + } + + if tx.GasLimit < txProc.economicsFee.ComputeGasLimit(tx) { + return process.ErrNotEnoughGas + } + + if check.IfNil(relayerAccount) { + return nil + } + + txFee := txProc.economicsFee.ComputeTxFee(tx) + + if relayerAccount.GetBalance().Cmp(txFee) < 0 { + return fmt.Errorf("%w, has: %s, wanted: %s", + process.ErrInsufficientFee, + relayerAccount.GetBalance().String(), + txFee.String(), + ) + } + + if senderAccount.GetBalance().Cmp(tx.Value) < 0 { + return process.ErrInsufficientFunds + } + + return nil +} + +func (txProc *baseTxProcessor) getFeePayer( + tx *transaction.Transaction, + senderAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, +) (state.UserAccountHandler, bool, error) { + if !common.IsRelayedTxV3(tx) { + return senderAccount, false, nil + } + + relayerIsSender := bytes.Equal(tx.RelayerAddr, tx.SndAddr) + if relayerIsSender { + return senderAccount, true, nil // do not load the same account twice + } + + relayerIsDestination := bytes.Equal(tx.RelayerAddr, tx.RcvAddr) + if relayerIsDestination { + return destinationAccount, true, nil // do not load the same account twice + } + + acntRelayer, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return nil, true, err + } + + return acntRelayer, true, nil +} + +func (txProc *baseTxProcessor) computeInnerTxFee(tx *transaction.Transaction) *big.Int { + if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { + return txProc.computeInnerTxFeeAfterBaseCostFix(tx) + } + + return txProc.economicsFee.ComputeFeeForProcessing(tx, tx.GasLimit) +} + +func (txProc *baseTxProcessor) computeInnerTxFeeAfterBaseCostFix(tx *transaction.Transaction) *big.Int { + _, dstShardTxType, _ := txProc.txTypeHandler.ComputeTransactionType(tx) + if dstShardTxType == process.MoveBalance { + return txProc.economicsFee.ComputeMoveBalanceFee(tx) + } + + moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(tx) + gasToUse := tx.GetGasLimit() - moveBalanceGasLimit + moveBalanceUserFee := txProc.economicsFee.ComputeMoveBalanceFee(tx) + processingUserFee := txProc.economicsFee.ComputeFeeForProcessing(tx, gasToUse) + txFee := big.NewInt(0).Add(moveBalanceUserFee, processingUserFee) + + return txFee +} + func (txProc *baseTxProcessor) checkUserNames(tx *transaction.Transaction, acntSnd, acntDst state.UserAccountHandler) error { isUserNameWrong := len(tx.SndUserName) > 0 && !check.IfNil(acntSnd) && !bytes.Equal(tx.SndUserName, acntSnd.GetUserName()) @@ -263,7 +378,8 @@ func (txProc *baseTxProcessor) checkGuardedAccountUnguardedTxPermission(tx *tran return nil } -func (txProc *baseTxProcessor) verifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { +// VerifyGuardian does the guardian verification +func (txProc *baseTxProcessor) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { if check.IfNil(account) { return nil } diff --git a/process/transaction/baseProcess_test.go b/process/transaction/baseProcess_test.go index 3527748a72e..7d4605239a9 100644 --- a/process/transaction/baseProcess_test.go +++ b/process/transaction/baseProcess_test.go @@ -44,6 +44,7 @@ func createMockBaseTxProcessor() *baseTxProcessor { enableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.PenalizedTooMuchGasFlag), txVersionChecker: &testscommon.TxVersionCheckerStub{}, guardianChecker: &guardianMocks.GuardedAccountHandlerStub{}, + txTypeHandler: &testscommon.TxTypeHandlerMock{}, } return &baseProc @@ -212,6 +213,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { enableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.PenalizedTooMuchGasFlag), txVersionChecker: &testscommon.TxVersionCheckerStub{}, guardianChecker: &guardianMocks.GuardedAccountHandlerStub{}, + txTypeHandler: &testscommon.TxTypeHandlerMock{}, } notGuardedAccount := &stateMock.UserAccountStub{} @@ -229,7 +231,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { t.Parallel() localBaseProc := baseProc - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, nil) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, nil) assert.Nil(t, err) }) t.Run("guarded account with a not guarded transaction should error", func(t *testing.T) { @@ -242,7 +244,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, guardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, guardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), "not allowed to bypass guardian") }) @@ -256,7 +258,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, notGuardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, notGuardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), process.ErrGuardedTransactionNotExpected.Error()) }) @@ -270,7 +272,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, notGuardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, notGuardedAccount) assert.Nil(t, err) }) t.Run("get active guardian fails should error", func(t *testing.T) { @@ -288,7 +290,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, guardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, guardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), expectedErr.Error()) }) @@ -307,7 +309,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(tx, guardedAccount) + err := localBaseProc.VerifyGuardian(tx, guardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), process.ErrTransactionAndAccountGuardianMismatch.Error()) }) @@ -326,7 +328,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(tx, guardedAccount) + err := localBaseProc.VerifyGuardian(tx, guardedAccount) assert.Nil(t, err) }) } diff --git a/process/transaction/export_test.go b/process/transaction/export_test.go index a10b1e2e50c..c6ff2791b45 100644 --- a/process/transaction/export_test.go +++ b/process/transaction/export_test.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -13,19 +14,23 @@ import ( type TxProcessor *txProcessor +// GetAccounts calls the un-exported method getAccounts func (txProc *txProcessor) GetAccounts(adrSrc, adrDst []byte, ) (acntSrc, acntDst state.UserAccountHandler, err error) { return txProc.getAccounts(adrSrc, adrDst) } +// CheckTxValues calls the un-exported method checkTxValues func (txProc *txProcessor) CheckTxValues(tx *transaction.Transaction, acntSnd, acntDst state.UserAccountHandler, isUserTxOfRelayed bool) error { return txProc.checkTxValues(tx, acntSnd, acntDst, isUserTxOfRelayed) } +// IncreaseNonce calls IncreaseNonce on the provided account func (txProc *txProcessor) IncreaseNonce(acntSrc state.UserAccountHandler) { acntSrc.IncreaseNonce(1) } +// ProcessTxFee calls the un-exported method processTxFee func (txProc *txProcessor) ProcessTxFee( tx *transaction.Transaction, acntSnd, acntDst state.UserAccountHandler, @@ -35,24 +40,35 @@ func (txProc *txProcessor) ProcessTxFee( return txProc.processTxFee(tx, acntSnd, acntDst, txType, isUserTxOfRelayed) } +// SetWhitelistHandler sets the un-exported field whiteListerVerifiedTxs func (inTx *InterceptedTransaction) SetWhitelistHandler(handler process.WhiteListHandler) { inTx.whiteListerVerifiedTxs = handler } +// IsCrossTxFromMe calls the un-exported method isCrossTxFromMe func (txProc *baseTxProcessor) IsCrossTxFromMe(adrSrc, adrDst []byte) bool { return txProc.isCrossTxFromMe(adrSrc, adrDst) } +// ProcessUserTx calls the un-exported method processUserTx func (txProc *txProcessor) ProcessUserTx( originalTx *transaction.Transaction, userTx *transaction.Transaction, relayedTxValue *big.Int, relayedNonce uint64, - txHash []byte, + relayerAddr []byte, + originalTxHash []byte, ) (vmcommon.ReturnCode, error) { - return txProc.processUserTx(originalTx, userTx, relayedTxValue, relayedNonce, txHash) + return txProc.processUserTx( + originalTx, + userTx, + relayedTxValue, + relayedNonce, + relayerAddr, + originalTxHash) } +// ProcessMoveBalanceCostRelayedUserTx calls the un-exported method processMoveBalanceCostRelayedUserTx func (txProc *txProcessor) ProcessMoveBalanceCostRelayedUserTx( userTx *transaction.Transaction, userScr *smartContractResult.SmartContractResult, @@ -62,6 +78,7 @@ func (txProc *txProcessor) ProcessMoveBalanceCostRelayedUserTx( return txProc.processMoveBalanceCostRelayedUserTx(userTx, userScr, userAcc, originalTxHash) } +// ExecuteFailedRelayedTransaction calls the un-exported method executeFailedRelayedUserTx func (txProc *txProcessor) ExecuteFailedRelayedTransaction( userTx *transaction.Transaction, relayerAdr []byte, @@ -81,20 +98,22 @@ func (txProc *txProcessor) ExecuteFailedRelayedTransaction( errorMsg) } +// CheckMaxGasPrice calls the un-exported method checkMaxGasPrice func (inTx *InterceptedTransaction) CheckMaxGasPrice() error { return inTx.checkMaxGasPrice() } -func (txProc *txProcessor) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { - return txProc.verifyGuardian(tx, account) +// SetEnableEpochsHandler sets the internal enable epochs handler +func (inTx *InterceptedTransaction) SetEnableEpochsHandler(handler common.EnableEpochsHandler) { + inTx.enableEpochsHandler = handler } -// ShouldIncreaseNonce - +// ShouldIncreaseNonce calls the un-exported method shouldIncreaseNonce func (txProc *txProcessor) ShouldIncreaseNonce(executionErr error) bool { return txProc.shouldIncreaseNonce(executionErr) } -// AddNonExecutableLog - +// AddNonExecutableLog calls the un-exported method addNonExecutableLog func (txProc *txProcessor) AddNonExecutableLog(executionErr error, originalTxHash []byte, originalTx data.TransactionHandler) error { return txProc.addNonExecutableLog(executionErr, originalTxHash, originalTx) } diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 0aedf837d09..53722cad96f 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -13,6 +13,7 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-crypto-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" logger "github.com/multiversx/mx-chain-logger-go" @@ -42,6 +43,7 @@ type InterceptedTransaction struct { sndShard uint32 isForCurrentShard bool enableSignedTxWithHash bool + enableEpochsHandler common.EnableEpochsHandler } // NewInterceptedTransaction returns a new instance of InterceptedTransaction @@ -61,6 +63,7 @@ func NewInterceptedTransaction( enableSignedTxWithHash bool, txSignHasher hashing.Hasher, txVersionChecker process.TxVersionCheckerHandler, + enableEpochsHandler common.EnableEpochsHandler, ) (*InterceptedTransaction, error) { if txBuff == nil { @@ -105,6 +108,9 @@ func NewInterceptedTransaction( if check.IfNil(txVersionChecker) { return nil, process.ErrNilTransactionVersionChecker } + if check.IfNil(enableEpochsHandler) { + return nil, process.ErrNilEnableEpochsHandler + } tx, err := createTx(protoMarshalizer, txBuff) if err != nil { @@ -127,6 +133,7 @@ func NewInterceptedTransaction( enableSignedTxWithHash: enableSignedTxWithHash, txVersionChecker: txVersionChecker, txSignHasher: txSignHasher, + enableEpochsHandler: enableEpochsHandler, } err = inTx.processFields(txBuff) @@ -189,6 +196,11 @@ func (inTx *InterceptedTransaction) CheckValidity() error { return err } + err = inTx.verifyIfRelayedTxV3(inTx.tx) + if err != nil { + return err + } + err = inTx.verifyIfRelayedTx(inTx.tx) if err != nil { return err @@ -205,45 +217,84 @@ func (inTx *InterceptedTransaction) CheckValidity() error { return nil } -func isRelayedTx(funcName string) bool { - return core.RelayedTransaction == funcName || core.RelayedTransactionV2 == funcName -} +func (inTx *InterceptedTransaction) checkRecursiveRelayed(userTx *transaction.Transaction) error { + if common.IsValidRelayedTxV3(userTx) { + return process.ErrRecursiveRelayedTxIsNotAllowed + } -func (inTx *InterceptedTransaction) verifyIfRelayedTxV2(tx *transaction.Transaction) error { - funcName, userTxArgs, err := inTx.argsParser.ParseCallData(string(tx.Data)) + funcName, _, err := inTx.argsParser.ParseCallData(string(userTx.Data)) if err != nil { return nil } - if core.RelayedTransactionV2 != funcName { + + if isRelayedTx(funcName) { + return process.ErrRecursiveRelayedTxIsNotAllowed + } + + return nil +} + +func isRelayedTx(funcName string) bool { + return core.RelayedTransaction == funcName || + core.RelayedTransactionV2 == funcName +} + +func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transaction) error { + if !common.IsRelayedTxV3(tx) { return nil } - userTx, err := createRelayedV2(tx, userTxArgs) + if !inTx.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { + return process.ErrRelayedTxV3Disabled + } + + if !common.IsValidRelayedTxV3(tx) { + return process.ErrInvalidRelayedTxV3 + } + + err := inTx.integrity(tx) if err != nil { return err } - err = inTx.verifySig(userTx) + if !inTx.coordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { + return process.ErrShardIdMissmatch + } + + if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { + return process.ErrRelayedByGuardianNotAllowed + } + + userTx := *tx + userTx.RelayerSignature = make([]byte, 0) // temporary removed signature for recursive relayed checks + err = inTx.verifyUserTx(&userTx) if err != nil { return fmt.Errorf("inner transaction: %w", err) } - err = inTx.VerifyGuardianSig(userTx) + err = inTx.verifyRelayerSig(tx) if err != nil { - return fmt.Errorf("inner transaction: %w", err) + return err } - funcName, _, err = inTx.argsParser.ParseCallData(string(userTx.Data)) + return nil +} + +func (inTx *InterceptedTransaction) verifyIfRelayedTxV2(tx *transaction.Transaction) error { + funcName, userTxArgs, err := inTx.argsParser.ParseCallData(string(tx.Data)) if err != nil { return nil } + if core.RelayedTransactionV2 != funcName { + return nil + } - // recursive relayed transactions are not allowed - if isRelayedTx(funcName) { - return process.ErrRecursiveRelayedTxIsNotAllowed + userTx, err := createRelayedV2(tx, userTxArgs) + if err != nil { + return err } - return nil + return inTx.verifyUserTx(userTx) } func (inTx *InterceptedTransaction) verifyIfRelayedTx(tx *transaction.Transaction) error { @@ -273,28 +324,23 @@ func (inTx *InterceptedTransaction) verifyIfRelayedTx(tx *transaction.Transactio return fmt.Errorf("inner transaction: %w", err) } - err = inTx.verifySig(userTx) + return inTx.verifyUserTx(userTx) +} + +func (inTx *InterceptedTransaction) verifyUserTx(userTx *transaction.Transaction) error { + // recursive relayed transactions are not allowed + err := inTx.checkRecursiveRelayed(userTx) if err != nil { return fmt.Errorf("inner transaction: %w", err) } - - err = inTx.VerifyGuardianSig(userTx) + err = inTx.verifySig(userTx) if err != nil { return fmt.Errorf("inner transaction: %w", err) } - if len(userTx.Data) == 0 { - return nil - } - - funcName, _, err = inTx.argsParser.ParseCallData(string(userTx.Data)) + err = inTx.VerifyGuardianSig(userTx) if err != nil { - return nil - } - - // recursive relayed transactions are not allowed - if isRelayedTx(funcName) { - return process.ErrRecursiveRelayedTxIsNotAllowed + return fmt.Errorf("inner transaction: %w", err) } return nil @@ -381,6 +427,21 @@ func (inTx *InterceptedTransaction) verifySig(tx *transaction.Transaction) error return inTx.singleSigner.Verify(senderPubKey, txMessageForSigVerification, tx.Signature) } +// verifyRelayerSig checks if the tx is correctly signed by relayer +func (inTx *InterceptedTransaction) verifyRelayerSig(tx *transaction.Transaction) error { + txMessageForSigVerification, err := inTx.getTxMessageForGivenTx(tx) + if err != nil { + return err + } + + relayerPubKey, err := inTx.keyGen.PublicKeyFromByteArray(tx.RelayerAddr) + if err != nil { + return err + } + + return inTx.singleSigner.Verify(relayerPubKey, txMessageForSigVerification, tx.RelayerSignature) +} + // VerifyGuardianSig verifies if the guardian signature is valid func (inTx *InterceptedTransaction) VerifyGuardianSig(tx *transaction.Transaction) error { txMessageForSigVerification, err := inTx.getTxMessageForGivenTx(tx) diff --git a/process/transaction/interceptedTransaction_test.go b/process/transaction/interceptedTransaction_test.go index 1312f5cba4f..a0de364ddff 100644 --- a/process/transaction/interceptedTransaction_test.go +++ b/process/transaction/interceptedTransaction_test.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "math/big" + "strings" "testing" "github.com/multiversx/mx-chain-core-go/core" @@ -18,6 +19,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/interceptors" "github.com/multiversx/mx-chain-go/process/mock" @@ -26,6 +28,7 @@ import ( "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/cache" "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" + "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" ) @@ -37,6 +40,7 @@ var senderShard = uint32(2) var recvShard = uint32(3) var senderAddress = []byte("12345678901234567890123456789012") var recvAddress = []byte("23456789012345678901234567890123") +var relayerAddress = []byte("34567890123456789012345678901234") var sigBad = []byte("bad-signature") var sigOk = []byte("signature") @@ -110,11 +114,12 @@ func createInterceptedTxWithTxFeeHandlerAndVersionChecker(tx *dataTransaction.Tr shardCoordinator, txFeeHandler, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("T"), false, &hashingMocks.HasherMock{}, txVerChecker, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) } @@ -153,11 +158,12 @@ func createInterceptedTxFromPlainTx(tx *dataTransaction.Transaction, txFeeHandle shardCoordinator, txFeeHandler, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, chainID, false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) } @@ -171,7 +177,8 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction shardCoordinator := mock.NewMultipleShardsCoordinatorMock() shardCoordinator.CurrentShard = 0 shardCoordinator.ComputeIdCalled = func(address []byte) uint32 { - if bytes.Equal(address, senderAddress) { + if bytes.Equal(address, senderAddress) || + bytes.Equal(address, relayerAddress) { return senderShard } if bytes.Equal(address, recvAddress) { @@ -180,6 +187,10 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction return shardCoordinator.CurrentShard } + shardCoordinator.SameShardCalled = func(firstAddress, secondAddress []byte) bool { + return string(firstAddress) == string(relayerAddress) && + string(secondAddress) == string(senderAddress) + } return transaction.NewInterceptedTransaction( txBuff, @@ -201,6 +212,7 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(tx.Version), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), ) } @@ -220,11 +232,12 @@ func TestNewInterceptedTransaction_NilBufferShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -250,6 +263,7 @@ func TestNewInterceptedTransaction_NilArgsParser(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -270,11 +284,12 @@ func TestNewInterceptedTransaction_NilVersionChecker(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, nil, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -295,11 +310,12 @@ func TestNewInterceptedTransaction_NilMarshalizerShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -320,11 +336,12 @@ func TestNewInterceptedTransaction_NilSignMarshalizerShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -345,11 +362,12 @@ func TestNewInterceptedTransaction_NilHasherShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -370,11 +388,12 @@ func TestNewInterceptedTransaction_NilKeyGenShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -395,11 +414,12 @@ func TestNewInterceptedTransaction_NilSignerShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -420,11 +440,12 @@ func TestNewInterceptedTransaction_NilPubkeyConverterShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -445,11 +466,12 @@ func TestNewInterceptedTransaction_NilCoordinatorShouldErr(t *testing.T) { nil, &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -470,11 +492,12 @@ func TestNewInterceptedTransaction_NilFeeHandlerShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), nil, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -495,11 +518,12 @@ func TestNewInterceptedTransaction_NilWhiteListerVerifiedTxsShouldErr(t *testing mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, nil, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -520,11 +544,12 @@ func TestNewInterceptedTransaction_InvalidChainIDShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, nil, false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -545,17 +570,44 @@ func TestNewInterceptedTransaction_NilTxSignHasherShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, nil, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) assert.Equal(t, process.ErrNilHasher, err) } +func TestNewInterceptedTransaction_NilEnableEpochsHandlerShouldErr(t *testing.T) { + t.Parallel() + + txi, err := transaction.NewInterceptedTransaction( + make([]byte, 0), + &mock.MarshalizerMock{}, + &mock.MarshalizerMock{}, + &hashingMocks.HasherMock{}, + &mock.SingleSignKeyGenMock{}, + &mock.SignerMock{}, + createMockPubKeyConverter(), + mock.NewOneShardCoordinatorMock(), + &economicsmocks.EconomicsHandlerStub{}, + &testscommon.WhiteListHandlerStub{}, + &testscommon.ArgumentParserMock{}, + []byte("chainID"), + false, + &hashingMocks.HasherMock{}, + versioning.NewTxVersionChecker(1), + nil, + ) + + assert.Nil(t, txi) + assert.Equal(t, process.ErrNilEnableEpochsHandler, err) +} + func TestNewInterceptedTransaction_UnmarshalingTxFailsShouldErr(t *testing.T) { t.Parallel() @@ -576,11 +628,12 @@ func TestNewInterceptedTransaction_UnmarshalingTxFailsShouldErr(t *testing.T) { mock.NewOneShardCoordinatorMock(), &economicsmocks.EconomicsHandlerStub{}, &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("chainID"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -1046,11 +1099,12 @@ func TestInterceptedTransaction_CheckValiditySignedWithHashButNotEnabled(t *test shardCoordinator, createFreeTxFeeHandler(), &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, chainID, false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) err := txi.CheckValidity() @@ -1106,11 +1160,12 @@ func TestInterceptedTransaction_CheckValiditySignedWithHashShouldWork(t *testing shardCoordinator, createFreeTxFeeHandler(), &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, chainID, true, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) err := txi.CheckValidity() @@ -1191,11 +1246,12 @@ func TestInterceptedTransaction_ScTxDeployRecvShardIdShouldBeSendersShardId(t *t shardCoordinator, createFreeTxFeeHandler(), &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, chainID, false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, err) @@ -1330,11 +1386,12 @@ func TestInterceptedTransaction_CheckValiditySecondTimeDoesNotVerifySig(t *testi shardCoordinator, createFreeTxFeeHandler(), whiteListerVerifiedTxs, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, chainID, false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) require.Nil(t, err) @@ -1428,7 +1485,8 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTx(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxData)) txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) err = txi.CheckValidity() - assert.Equal(t, process.ErrRecursiveRelayedTxIsNotAllowed, err) + assert.True(t, strings.Contains(err.Error(), process.ErrRecursiveRelayedTxIsNotAllowed.Error())) + assert.Contains(t, err.Error(), "inner transaction") } func TestInterceptedTransaction_CheckValidityOfRelayedTxV2(t *testing.T) { @@ -1489,7 +1547,8 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTxV2(t *testing.T) { tx.Data = []byte(core.RelayedTransactionV2 + "@" + hex.EncodeToString(userTx.RcvAddr) + "@" + hex.EncodeToString(big.NewInt(0).SetUint64(userTx.Nonce).Bytes()) + "@" + hex.EncodeToString([]byte(core.RelayedTransaction)) + "@" + hex.EncodeToString(userTx.Signature)) txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) err = txi.CheckValidity() - assert.Equal(t, process.ErrRecursiveRelayedTxIsNotAllowed, err) + assert.True(t, strings.Contains(err.Error(), process.ErrRecursiveRelayedTxIsNotAllowed.Error())) + assert.Contains(t, err.Error(), "inner transaction") userTx.Signature = sigOk userTx.SndAddr = []byte("otherAddress") @@ -1499,6 +1558,82 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTxV2(t *testing.T) { assert.Nil(t, err) } +func TestInterceptedTransaction_CheckValidityOfRelayedTxV3(t *testing.T) { + t.Parallel() + + minTxVersion := uint32(1) + chainID := []byte("chain") + tx := &dataTransaction.Transaction{ + Nonce: 1, + Value: big.NewInt(2), + GasLimit: 3, + GasPrice: 4, + RcvAddr: recvAddress, + SndAddr: senderAddress, + ChainID: chainID, + Version: minTxVersion, + RelayerAddr: relayerAddress, + } + txi, _ := createInterceptedTxFromPlainTxWithArgParser(tx) + err := txi.CheckValidity() + assert.Equal(t, err, process.ErrNilSignature) + + tx.Signature = sigOk + tx.RelayerSignature = sigOk + + // flag not active should error + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + txi.SetEnableEpochsHandler(enableEpochsHandlerMock.NewEnableEpochsHandlerStub()) + err = txi.CheckValidity() + assert.Equal(t, process.ErrRelayedTxV3Disabled, err) + + // invalid relayed v3 should error + tx.RelayerSignature = nil + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Equal(t, process.ErrInvalidRelayedTxV3, err) + + // sender in different shard than relayer should fail + tx.RelayerSignature = sigOk + tx.RelayerAddr = bytes.Repeat([]byte("a"), len(relayerAddress)) + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Equal(t, process.ErrShardIdMissmatch, err) + + // relayer == guardian should fail + tx.Version = 2 + tx.Options = 2 + tx.RelayerAddr = relayerAddress + tx.GuardianAddr = tx.RelayerAddr + tx.GuardianSignature = sigOk + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Equal(t, process.ErrRelayedByGuardianNotAllowed, err) + + // recursive relayed txs + tx.Version = minTxVersion + tx.Options = 0 + tx.GuardianAddr = nil + tx.GuardianSignature = nil + tx.Data = []byte(core.RelayedTransactionV2 + "@" + hex.EncodeToString(recvAddress) + "@" + hex.EncodeToString(big.NewInt(0).SetUint64(0).Bytes()) + "@" + hex.EncodeToString([]byte("some method")) + "@" + hex.EncodeToString(sigOk)) + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.True(t, errors.Is(err, process.ErrRecursiveRelayedTxIsNotAllowed)) + + // invalid relayer signature + tx.Data = nil + tx.RelayerSignature = bytes.Repeat([]byte("a"), len(sigOk)) // same length but invalid relayer sig + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.NotNil(t, err) + + // should work + tx.RelayerSignature = sigOk + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Nil(t, err) +} + // ------- IsInterfaceNil func TestInterceptedTransaction_IsInterfaceNil(t *testing.T) { t.Parallel() @@ -1623,11 +1758,12 @@ func TestInterceptedTransaction_Fee(t *testing.T) { shardCoordinator, createFreeTxFeeHandler(), &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("T"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(0), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Equal(t, big.NewInt(0), txin.Fee()) @@ -1666,11 +1802,12 @@ func TestInterceptedTransaction_String(t *testing.T) { shardCoordinator, createFreeTxFeeHandler(), &testscommon.WhiteListHandlerStub{}, - &mock.ArgumentParserMock{}, + &testscommon.ArgumentParserMock{}, []byte("T"), false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(0), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) expectedFormat := fmt.Sprintf( diff --git a/process/transaction/metaProcess.go b/process/transaction/metaProcess.go index d1b88a012d4..090d4ad89e4 100644 --- a/process/transaction/metaProcess.go +++ b/process/transaction/metaProcess.go @@ -20,7 +20,6 @@ var _ process.TransactionProcessor = (*metaTxProcessor)(nil) // txProcessor implements TransactionProcessor interface and can modify account states according to a transaction type metaTxProcessor struct { *baseTxProcessor - txTypeHandler process.TxTypeHandler enableEpochsHandler common.EnableEpochsHandler } @@ -66,6 +65,7 @@ func NewMetaTxProcessor(args ArgsNewMetaTxProcessor) (*metaTxProcessor, error) { err := core.CheckHandlerCompatibility(args.EnableEpochsHandler, []core.EnableEpochFlag{ common.PenalizedTooMuchGasFlag, common.ESDTFlag, + common.FixRelayedBaseCostFlag, }) if err != nil { return nil, err @@ -88,11 +88,11 @@ func NewMetaTxProcessor(args ArgsNewMetaTxProcessor) (*metaTxProcessor, error) { enableEpochsHandler: args.EnableEpochsHandler, txVersionChecker: args.TxVersionChecker, guardianChecker: args.GuardianChecker, + txTypeHandler: args.TxTypeHandler, } txProc := &metaTxProcessor{ baseTxProcessor: baseTxProcess, - txTypeHandler: args.TxTypeHandler, enableEpochsHandler: args.EnableEpochsHandler, } @@ -135,7 +135,7 @@ func (txProc *metaTxProcessor) ProcessTransaction(tx *transaction.Transaction) ( return 0, err } - txType, _ := txProc.txTypeHandler.ComputeTransactionType(tx) + txType, _, _ := txProc.txTypeHandler.ComputeTransactionType(tx) switch txType { case process.SCDeployment: return txProc.processSCDeployment(tx, tx.SndAddr) diff --git a/process/transaction/metaProcess_test.go b/process/transaction/metaProcess_test.go index eaaa1382d2e..42a04260077 100644 --- a/process/transaction/metaProcess_test.go +++ b/process/transaction/metaProcess_test.go @@ -277,8 +277,8 @@ func TestMetaTxProcessor_ProcessTransactionScTxShouldWork(t *testing.T) { args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } txProc, _ := txproc.NewMetaTxProcessor(args) @@ -323,8 +323,8 @@ func TestMetaTxProcessor_ProcessTransactionScTxShouldReturnErrWhenExecutionFails args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } txProc, _ := txproc.NewMetaTxProcessor(args) @@ -439,8 +439,8 @@ func TestMetaTxProcessor_ProcessTransactionBuiltInCallTxShouldWork(t *testing.T) args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ESDTFlag) diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 95de88df395..a9b4d4d68b8 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -39,7 +39,6 @@ type relayedFees struct { type txProcessor struct { *baseTxProcessor txFeeHandler process.TransactionFeeHandler - txTypeHandler process.TxTypeHandler receiptForwarder process.IntermediateTransactionHandler badTxForwarder process.IntermediateTransactionHandler argsParser process.ArgumentsParser @@ -129,6 +128,7 @@ func NewTxProcessor(args ArgsNewTxProcessor) (*txProcessor, error) { common.RelayedTransactionsFlag, common.RelayedTransactionsV2Flag, common.RelayedNonceFixFlag, + common.FixRelayedBaseCostFlag, }) if err != nil { return nil, err @@ -154,12 +154,12 @@ func NewTxProcessor(args ArgsNewTxProcessor) (*txProcessor, error) { enableEpochsHandler: args.EnableEpochsHandler, txVersionChecker: args.TxVersionChecker, guardianChecker: args.GuardianChecker, + txTypeHandler: args.TxTypeHandler, } txProc := &txProcessor{ baseTxProcessor: baseTxProcess, txFeeHandler: args.TxFeeHandler, - txTypeHandler: args.TxTypeHandler, receiptForwarder: args.ReceiptForwarder, badTxForwarder: args.BadTxForwarder, argsParser: args.ArgsParser, @@ -196,18 +196,18 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco txProc.pubkeyConv, ) - txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(tx) + txType, dstShardTxType, isRelayedV3 := txProc.txTypeHandler.ComputeTransactionType(tx) err = txProc.checkTxValues(tx, acntSnd, acntDst, false) if err != nil { if errors.Is(err, process.ErrInsufficientFunds) { - receiptErr := txProc.executingFailedTransaction(tx, acntSnd, err) + receiptErr := txProc.executingFailedTransaction(tx, acntSnd, acntDst, err) if receiptErr != nil { return 0, receiptErr } } if errors.Is(err, process.ErrUserNameDoesNotMatch) && txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsFlag) { - receiptErr := txProc.executingFailedTransaction(tx, acntSnd, err) + receiptErr := txProc.executingFailedTransaction(tx, acntSnd, acntDst, err) if receiptErr != nil { return vmcommon.UserError, receiptErr } @@ -223,6 +223,13 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco return vmcommon.UserError, err } + if isRelayedV3 { + err = txProc.verifyRelayedTxV3(tx) + if err != nil { + return vmcommon.UserError, err + } + } + switch txType { case process.MoveBalance: err = txProc.processMoveBalance(tx, acntSnd, acntDst, dstShardTxType, nil, false) @@ -242,7 +249,7 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco return txProc.processRelayedTxV2(tx, acntSnd, acntDst) } - return vmcommon.UserError, txProc.executingFailedTransaction(tx, acntSnd, process.ErrWrongTransaction) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, acntSnd, acntDst, process.ErrWrongTransaction) } func (txProc *txProcessor) executeAfterFailedMoveBalanceTransaction( @@ -289,14 +296,20 @@ func (txProc *txProcessor) executeAfterFailedMoveBalanceTransaction( func (txProc *txProcessor) executingFailedTransaction( tx *transaction.Transaction, acntSnd state.UserAccountHandler, + acntDst state.UserAccountHandler, txError error, ) error { if check.IfNil(acntSnd) { return nil } + feePayer, isRelayedV3, err := txProc.getFeePayer(tx, acntSnd, acntDst) + if err != nil { + return err + } + txFee := txProc.economicsFee.ComputeTxFee(tx) - err := acntSnd.SubFromBalance(txFee) + err = feePayer.SubFromBalance(txFee) if err != nil { return err } @@ -328,24 +341,32 @@ func (txProc *txProcessor) executingFailedTransaction( txProc.txFeeHandler.ProcessTransactionFee(txFee, big.NewInt(0), txHash) - err = txProc.accounts.SaveAccount(acntSnd) + err = txProc.accounts.SaveAccount(feePayer) if err != nil { return err } + if isRelayedV3 { + // for relayed v3, the nonce was increased for sender, but fees consumed from relayer + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return err + } + } + return process.ErrFailedTransaction } func (txProc *txProcessor) createReceiptWithReturnedGas( txHash []byte, tx *transaction.Transaction, - acntSnd state.UserAccountHandler, + feePayer state.UserAccountHandler, moveBalanceCost *big.Int, totalProvided *big.Int, destShardTxType process.TransactionType, isUserTxOfRelayed bool, ) error { - if check.IfNil(acntSnd) || isUserTxOfRelayed { + if check.IfNil(feePayer) || isUserTxOfRelayed { return nil } shouldCreateReceiptBackwardCompatible := !txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && core.IsSmartContractAddress(tx.RcvAddr) @@ -362,32 +383,33 @@ func (txProc *txProcessor) createReceiptWithReturnedGas( rpt := &receipt.Receipt{ Value: big.NewInt(0).Set(refundValue), - SndAddr: tx.SndAddr, + SndAddr: feePayer.AddressBytes(), Data: []byte(RefundGasMessage), TxHash: txHash, } - err := txProc.receiptForwarder.AddIntermediateTransactions([]data.TransactionHandler{rpt}, txHash) - if err != nil { - return err - } - - return nil + return txProc.receiptForwarder.AddIntermediateTransactions([]data.TransactionHandler{rpt}, txHash) } func (txProc *txProcessor) processTxFee( tx *transaction.Transaction, - acntSnd, acntDst state.UserAccountHandler, + feePayer, acntDst state.UserAccountHandler, dstShardTxType process.TransactionType, isUserTxOfRelayed bool, ) (*big.Int, *big.Int, error) { - if check.IfNil(acntSnd) { + if check.IfNil(feePayer) { return big.NewInt(0), big.NewInt(0), nil } if isUserTxOfRelayed { - totalCost := txProc.economicsFee.ComputeFeeForProcessing(tx, tx.GasLimit) - err := acntSnd.SubFromBalance(totalCost) + totalCost := txProc.computeInnerTxFee(tx) + + err := feePayer.SubFromBalance(totalCost) + if err != nil { + return nil, nil, err + } + + err = txProc.accounts.SaveAccount(feePayer) if err != nil { return nil, nil, err } @@ -398,6 +420,10 @@ func (txProc *txProcessor) processTxFee( moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(tx) currentShardFee := txProc.economicsFee.ComputeFeeForProcessing(tx, moveBalanceGasLimit) + if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { + currentShardFee = txProc.economicsFee.ComputeMoveBalanceFee(tx) + } + return currentShardFee, totalCost, nil } @@ -412,26 +438,28 @@ func (txProc *txProcessor) processTxFee( if dstShardTxType != process.MoveBalance || (!txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && isCrossShardSCCall) { - err := acntSnd.SubFromBalance(totalCost) + err := feePayer.SubFromBalance(totalCost) if err != nil { return nil, nil, err } } else { - err := acntSnd.SubFromBalance(moveBalanceFee) + err := feePayer.SubFromBalance(moveBalanceFee) if err != nil { return nil, nil, err } } + err := txProc.accounts.SaveAccount(feePayer) + if err != nil { + return nil, nil, err + } + return moveBalanceFee, totalCost, nil } -func (txProc *txProcessor) checkIfValidTxToMetaChain( - tx *transaction.Transaction, - adrDst []byte, -) error { +func (txProc *txProcessor) checkIfValidTxToMetaChain(tx *transaction.Transaction) error { - destShardId := txProc.shardCoordinator.ComputeId(adrDst) + destShardId := txProc.shardCoordinator.ComputeId(tx.RcvAddr) if destShardId != core.MetachainShardId { return nil } @@ -459,7 +487,11 @@ func (txProc *txProcessor) processMoveBalance( isUserTxOfRelayed bool, ) error { - moveBalanceCost, totalCost, err := txProc.processTxFee(tx, acntSrc, acntDst, destShardTxType, isUserTxOfRelayed) + feePayer, _, err := txProc.getFeePayer(tx, acntSrc, acntDst) + if err != nil { + return nil + } + moveBalanceCost, totalCost, err := txProc.processTxFee(tx, feePayer, acntDst, destShardTxType, isUserTxOfRelayed) if err != nil { return err } @@ -480,14 +512,28 @@ func (txProc *txProcessor) processMoveBalance( isPayable, err := txProc.scProcessor.IsPayable(tx.SndAddr, tx.RcvAddr) if err != nil { + errRefund := txProc.revertConsumedValueFromSender(tx, acntSrc, isUserTxOfRelayed) + if errRefund != nil { + log.Error("failed to return funds to sender after check if receiver is payable", "error", errRefund) + } return err } if !isPayable { + err = txProc.revertConsumedValueFromSender(tx, acntSrc, isUserTxOfRelayed) + if err != nil { + log.Error("failed to return funds to sender while transferring to non payable sc", "error", err) + } + return process.ErrAccountNotPayable } - err = txProc.checkIfValidTxToMetaChain(tx, tx.RcvAddr) + err = txProc.checkIfValidTxToMetaChain(tx) if err != nil { + errLocal := txProc.revertConsumedValueFromSender(tx, acntSrc, isUserTxOfRelayed) + if errLocal != nil { + log.Error("failed to return funds to sender while sending invalid tx to metachain", "error", errLocal) + } + return err } @@ -509,7 +555,7 @@ func (txProc *txProcessor) processMoveBalance( return err } - err = txProc.createReceiptWithReturnedGas(txHash, tx, acntSrc, moveBalanceCost, totalCost, destShardTxType, isUserTxOfRelayed) + err = txProc.createReceiptWithReturnedGas(txHash, tx, feePayer, moveBalanceCost, totalCost, destShardTxType, isUserTxOfRelayed) if err != nil { return err } @@ -523,6 +569,31 @@ func (txProc *txProcessor) processMoveBalance( return nil } +func (txProc *txProcessor) revertConsumedValueFromSender( + tx *transaction.Transaction, + acntSrc state.UserAccountHandler, + isUserTxOfRelayed bool, +) error { + if !isUserTxOfRelayed { + return nil + } + + if !txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedMoveBalanceToNonPayableSCFlag) { + return nil + } + + if check.IfNil(acntSrc) { + return nil + } + + err := acntSrc.AddToBalance(tx.Value) + if err != nil { + return err + } + + return txProc.accounts.SaveAccount(acntSrc) +} + func (txProc *txProcessor) processSCDeployment( tx *transaction.Transaction, acntSrc state.UserAccountHandler, @@ -559,8 +630,13 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( tx *transaction.Transaction, userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { - computedFees := txProc.computeRelayedTxFees(tx) - txHash, err := txProc.processTxAtRelayer(relayerAcnt, computedFees.totalFee, computedFees.relayerFee, tx) + computedFees := txProc.computeRelayedTxFees(tx, userTx) + txHash, err := txProc.processTxAtRelayer( + relayerAcnt, + computedFees.totalFee, + computedFees.relayerFee, + tx, + tx.Value) if err != nil { return 0, err } @@ -569,12 +645,12 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( return vmcommon.Ok, nil } - err = txProc.addFeeAndValueToDest(acntDst, tx, computedFees.remainingFee) + err = txProc.addFeeAndValueToDest(acntDst, tx.Value, computedFees.remainingFee) if err != nil { return 0, err } - return txProc.processUserTx(tx, userTx, tx.Value, tx.Nonce, txHash) + return txProc.processUserTx(tx, userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) } func (txProc *txProcessor) processTxAtRelayer( @@ -582,6 +658,7 @@ func (txProc *txProcessor) processTxAtRelayer( totalFee *big.Int, relayerFee *big.Int, tx *transaction.Transaction, + valueToSubFromRelayer *big.Int, ) ([]byte, error) { txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) if err != nil { @@ -589,7 +666,7 @@ func (txProc *txProcessor) processTxAtRelayer( } if !check.IfNil(relayerAcnt) { - err = relayerAcnt.SubFromBalance(tx.GetValue()) + err = relayerAcnt.SubFromBalance(valueToSubFromRelayer) if err != nil { return nil, err } @@ -611,8 +688,12 @@ func (txProc *txProcessor) processTxAtRelayer( return txHash, nil } -func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler, tx *transaction.Transaction, remainingFee *big.Int) error { - err := acntDst.AddToBalance(tx.GetValue()) +func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler, txValue *big.Int, remainingFee *big.Int) error { + if check.IfNil(acntDst) { + return nil + } + + err := acntDst.AddToBalance(txValue) if err != nil { return err } @@ -625,23 +706,48 @@ func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler return txProc.accounts.SaveAccount(acntDst) } +func (txProc *txProcessor) verifyRelayedTxV3(tx *transaction.Transaction) error { + if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrRelayedTxV3Disabled) + } + + if !txProc.shardCoordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrShardIdMissmatch) + } + + if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrRelayedByGuardianNotAllowed) + } + + relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, err) + } + + if !check.IfNil(relayerAccount) && relayerAccount.IsGuarded() { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrGuardedRelayerNotAllowed) + } + + return nil +} + func (txProc *txProcessor) processRelayedTxV2( tx *transaction.Transaction, relayerAcnt, acntDst state.UserAccountHandler, ) (vmcommon.ReturnCode, error) { if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV2Flag) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxV2Disabled) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxV2Disabled) } if tx.GetValue().Cmp(big.NewInt(0)) != 0 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxV2ZeroVal) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxV2ZeroVal) } _, args, err := txProc.argsParser.ParseCallData(string(tx.GetData())) if err != nil { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, err) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, err) } if len(args) != 4 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrInvalidArguments) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrInvalidArguments) } userTx := makeUserTxFromRelayedTxV2Args(args) @@ -662,39 +768,44 @@ func (txProc *txProcessor) processRelayedTx( return 0, err } if len(args) != 1 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrInvalidArguments) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrInvalidArguments) } if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsFlag) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxDisabled) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxDisabled) } userTx := &transaction.Transaction{} err = txProc.signMarshalizer.Unmarshal(userTx, args[0]) if err != nil { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, err) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, err) } if !bytes.Equal(userTx.SndAddr, tx.RcvAddr) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxBeneficiaryDoesNotMatchReceiver) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxBeneficiaryDoesNotMatchReceiver) } if userTx.Value.Cmp(tx.Value) < 0 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxValueHigherThenUserTxValue) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxValueHigherThenUserTxValue) } if userTx.GasPrice != tx.GasPrice { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedGasPriceMissmatch) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedGasPriceMissmatch) } remainingGasLimit := tx.GasLimit - txProc.economicsFee.ComputeGasLimit(tx) if userTx.GasLimit != remainingGasLimit { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxGasLimitMissmatch) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxGasLimitMissmatch) } return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx) } -func (txProc *txProcessor) computeRelayedTxFees(tx *transaction.Transaction) relayedFees { +func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transaction) relayedFees { relayerFee := txProc.economicsFee.ComputeMoveBalanceFee(tx) totalFee := txProc.economicsFee.ComputeTxFee(tx) + if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { + userFee := txProc.computeInnerTxFeeAfterBaseCostFix(userTx) + + totalFee = totalFee.Add(relayerFee, userFee) + } remainingFee := big.NewInt(0).Sub(totalFee, relayerFee) computedFees := relayedFees{ @@ -725,7 +836,8 @@ func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( return err } - consumedFee := txProc.economicsFee.ComputeFeeForProcessing(userTx, userTx.GasLimit) + consumedFee := txProc.computeInnerTxFee(userTx) + err = userAcnt.SubFromBalance(consumedFee) if err != nil { return err @@ -759,6 +871,7 @@ func (txProc *txProcessor) addNonExecutableLog(executionErr error, originalTxHas } return txProc.txLogsProcessor.SaveLog(originalTxHash, originalTx, []*vmcommon.LogEntry{logEntry}) + } func (txProc *txProcessor) processMoveBalanceCostRelayedUserTx( @@ -769,6 +882,9 @@ func (txProc *txProcessor) processMoveBalanceCostRelayedUserTx( ) error { moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(userTx) moveBalanceUserFee := txProc.economicsFee.ComputeFeeForProcessing(userTx, moveBalanceGasLimit) + if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { + moveBalanceUserFee = txProc.economicsFee.ComputeMoveBalanceFee(userTx) + } userScrHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, userScr) if err != nil { @@ -784,22 +900,27 @@ func (txProc *txProcessor) processUserTx( userTx *transaction.Transaction, relayedTxValue *big.Int, relayedNonce uint64, - txHash []byte, + relayerAddr []byte, + originalTxHash []byte, ) (vmcommon.ReturnCode, error) { acntSnd, acntDst, err := txProc.getAccounts(userTx.SndAddr, userTx.RcvAddr) if err != nil { - return 0, err - } - - var originalTxHash []byte - originalTxHash, err = core.CalculateHash(txProc.marshalizer, txProc.hasher, originalTx) - if err != nil { - return 0, err + errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue, originalTxHash, originalTx, err) + if errRemove != nil { + return vmcommon.UserError, errRemove + } + return vmcommon.UserError, txProc.executeFailedRelayedUserTx( + userTx, + relayerAddr, + relayedTxValue, + relayedNonce, + originalTx, + originalTxHash, + err.Error()) } - relayerAdr := originalTx.SndAddr - txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) + txType, dstShardTxType, _ := txProc.txTypeHandler.ComputeTransactionType(userTx) err = txProc.checkTxValues(userTx, acntSnd, acntDst, true) if err != nil { errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue, originalTxHash, originalTx, err) @@ -808,15 +929,15 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, - txHash, + originalTxHash, err.Error()) } - scrFromTx, err := txProc.makeSCRFromUserTx(userTx, relayerAdr, relayedTxValue, txHash) + scrFromTx, err := txProc.makeSCRFromUserTx(userTx, relayerAddr, relayedTxValue, originalTxHash) if err != nil { return 0, err } @@ -854,22 +975,22 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, - txHash, + originalTxHash, err.Error()) } if errors.Is(err, process.ErrInvalidMetaTransaction) || errors.Is(err, process.ErrAccountNotPayable) { return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, - txHash, + originalTxHash, err.Error()) } @@ -891,7 +1012,7 @@ func (txProc *txProcessor) processUserTx( return returnCode, nil } - err = txProc.scrForwarder.AddIntermediateTransactions([]data.TransactionHandler{scrFromTx}, txHash) + err = txProc.scrForwarder.AddIntermediateTransactions([]data.TransactionHandler{scrFromTx}, originalTxHash) if err != nil { return 0, err } @@ -949,6 +1070,7 @@ func (txProc *txProcessor) executeFailedRelayedUserTx( originalTxHash []byte, errorMsg string, ) error { + scrForRelayer := &smartContractResult.SmartContractResult{ Nonce: relayedNonce, Value: big.NewInt(0).Set(relayedTxValue), @@ -970,11 +1092,22 @@ func (txProc *txProcessor) executeFailedRelayedUserTx( } totalFee := txProc.economicsFee.ComputeFeeForProcessing(userTx, userTx.GasLimit) + moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(userTx) + gasToUse := userTx.GetGasLimit() - moveBalanceGasLimit + processingUserFee := txProc.economicsFee.ComputeFeeForProcessing(userTx, gasToUse) + if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { + moveBalanceUserFee := txProc.economicsFee.ComputeMoveBalanceFee(userTx) + totalFee = big.NewInt(0).Add(moveBalanceUserFee, processingUserFee) + } + senderShardID := txProc.shardCoordinator.ComputeId(userTx.SndAddr) if senderShardID != txProc.shardCoordinator.SelfId() { - moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(userTx) - moveBalanceUserFee := txProc.economicsFee.ComputeFeeForProcessing(userTx, moveBalanceGasLimit) - totalFee.Sub(totalFee, moveBalanceUserFee) + if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { + totalFee.Sub(totalFee, processingUserFee) + } else { + moveBalanceUserFee := txProc.economicsFee.ComputeFeeForProcessing(userTx, moveBalanceGasLimit) + totalFee.Sub(totalFee, moveBalanceUserFee) + } } txProc.txFeeHandler.ProcessTransactionFee(totalFee, big.NewInt(0), originalTxHash) diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index 7c90dbad75f..75adbf3f3b9 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -7,16 +7,19 @@ import ( "errors" "fmt" "math/big" + "strings" "testing" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" "github.com/multiversx/mx-chain-vm-common-go/parsers" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" @@ -35,6 +38,8 @@ import ( "github.com/multiversx/mx-chain-go/vm" ) +var txHash = []byte("hash") + func generateRandomByteSlice(size int) []byte { buff := make([]byte, size) _, _ = rand.Reader.Read(buff) @@ -87,9 +92,9 @@ func createArgsForTxProcessor() txproc.ArgsNewTxProcessor { EconomicsFee: feeHandlerMock(), ReceiptForwarder: &mock.IntermediateTransactionHandlerMock{}, BadTxForwarder: &mock.IntermediateTransactionHandlerMock{}, - ArgsParser: &mock.ArgumentParserMock{}, + ArgsParser: &testscommon.ArgumentParserMock{}, ScrForwarder: &mock.IntermediateTransactionHandlerMock{}, - EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.PenalizedTooMuchGasFlag), + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.PenalizedTooMuchGasFlag, common.FixRelayedBaseCostFlag), GuardianChecker: &guardianMocks.GuardedAccountHandlerStub{}, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, TxLogsProcessor: &mock.TxLogsProcessorStub{}, @@ -303,6 +308,28 @@ func TestNewTxProcessor_NilEnableRoundsHandlerShouldErr(t *testing.T) { assert.Nil(t, txProc) } +func TestNewTxProcessor_NilTxVersionCheckerShouldErr(t *testing.T) { + t.Parallel() + + args := createArgsForTxProcessor() + args.TxVersionChecker = nil + txProc, err := txproc.NewTxProcessor(args) + + assert.Equal(t, process.ErrNilTransactionVersionChecker, err) + assert.Nil(t, txProc) +} + +func TestNewTxProcessor_NilGuardianCheckerShouldErr(t *testing.T) { + t.Parallel() + + args := createArgsForTxProcessor() + args.GuardianChecker = nil + txProc, err := txproc.NewTxProcessor(args) + + assert.Equal(t, process.ErrNilGuardianChecker, err) + assert.Nil(t, txProc) +} + func TestNewTxProcessor_OkValsShouldWork(t *testing.T) { t.Parallel() @@ -897,7 +924,7 @@ func TestTxProcessor_ProcessMoveBalanceToSmartPayableContract(t *testing.T) { _, err := execTx.ProcessTransaction(&tx) assert.Nil(t, err) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func testProcessCheck(t *testing.T, nonce uint64, value *big.Int) { @@ -963,7 +990,7 @@ func TestTxProcessor_ProcessMoveBalancesShouldWork(t *testing.T) { _, err := execTx.ProcessTransaction(&tx) assert.Nil(t, err) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func TestTxProcessor_ProcessOkValsShouldWork(t *testing.T) { @@ -999,7 +1026,7 @@ func TestTxProcessor_ProcessOkValsShouldWork(t *testing.T) { assert.Equal(t, uint64(5), acntSrc.GetNonce()) assert.Equal(t, big.NewInt(29), acntSrc.GetBalance()) assert.Equal(t, big.NewInt(71), acntDst.GetBalance()) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func TestTxProcessor_MoveBalanceWithFeesShouldWork(t *testing.T) { @@ -1046,7 +1073,7 @@ func TestTxProcessor_MoveBalanceWithFeesShouldWork(t *testing.T) { assert.Equal(t, uint64(5), acntSrc.GetNonce()) assert.Equal(t, big.NewInt(13), acntSrc.GetBalance()) assert.Equal(t, big.NewInt(71), acntDst.GetBalance()) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func TestTxProcessor_ProcessTransactionScDeployTxShouldWork(t *testing.T) { @@ -1085,8 +1112,8 @@ func TestTxProcessor_ProcessTransactionScDeployTxShouldWork(t *testing.T) { args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType process.TransactionType, destinationTransactionType process.TransactionType) { - return process.SCDeployment, process.SCDeployment + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType process.TransactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.SCDeployment, process.SCDeployment, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1133,8 +1160,8 @@ func TestTxProcessor_ProcessTransactionBuiltInFunctionCallShouldWork(t *testing. args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1181,8 +1208,8 @@ func TestTxProcessor_ProcessTransactionScTxShouldWork(t *testing.T) { args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1227,8 +1254,8 @@ func TestTxProcessor_ProcessTransactionScTxShouldReturnErrWhenExecutionFails(t * args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1298,7 +1325,7 @@ func TestTxProcessor_ProcessTransactionScTxShouldNotBeCalledWhenAdrDstIsNotInNod _, err := execTx.ProcessTransaction(&tx) assert.Nil(t, err) assert.False(t, wasCalled) - assert.Equal(t, 1, saveAccountCalled) + assert.Equal(t, 2, saveAccountCalled) } func TestTxProcessor_ProcessTxFeeIntraShard(t *testing.T) { @@ -1465,8 +1492,8 @@ func TestTxProcessor_ProcessTxFeeMoveBalanceUserTx(t *testing.T) { cost, totalCost, err := execTx.ProcessTxFee(tx, acntSnd, nil, process.MoveBalance, true) assert.Nil(t, err) - assert.True(t, cost.Cmp(processingFee) == 0) - assert.True(t, totalCost.Cmp(processingFee) == 0) + assert.True(t, cost.Cmp(moveBalanceFee) == 0) + assert.True(t, totalCost.Cmp(moveBalanceFee) == 0) } func TestTxProcessor_ProcessTxFeeSCInvokeUserTx(t *testing.T) { @@ -1477,6 +1504,7 @@ func TestTxProcessor_ProcessTxFeeSCInvokeUserTx(t *testing.T) { negMoveBalanceFee := big.NewInt(0).Neg(moveBalanceFee) gasPerByte := uint64(1) args := createArgsForTxProcessor() + args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.PenalizedTooMuchGasFlag) args.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ ComputeMoveBalanceFeeCalled: func(tx data.TransactionWithFeeHandler) *big.Int { return moveBalanceFee @@ -1546,8 +1574,8 @@ func TestTxProcessor_ProcessTransactionShouldReturnErrForInvalidMetaTx(t *testin }, } args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.MoveBalance + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.MoveBalance, false }, } args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.MetaProtectionFlag) @@ -1594,8 +1622,8 @@ func TestTxProcessor_ProcessTransactionShouldTreatAsInvalidTxIfTxTypeIsWrong(t * }, } args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.InvalidTransaction, process.InvalidTransaction + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.InvalidTransaction, process.InvalidTransaction, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1835,7 +1863,7 @@ func TestTxProcessor_ProcessRelayedTransactionV2ArgsParserShouldErr(t *testing.T parseError := errors.New("parse error") args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "", nil, parseError }} @@ -2127,7 +2155,7 @@ func TestTxProcessor_ProcessRelayedTransactionArgsParserErrorShouldError(t *test parseError := errors.New("parse error") args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return "", nil, parseError }} @@ -2154,8 +2182,8 @@ func TestTxProcessor_ProcessRelayedTransactionArgsParserErrorShouldError(t *test return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2190,7 +2218,7 @@ func TestTxProcessor_ProcessRelayedTransactionMultipleArgumentsShouldError(t *te tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{[]byte("0"), []byte("1")}, nil }} @@ -2217,8 +2245,8 @@ func TestTxProcessor_ProcessRelayedTransactionMultipleArgumentsShouldError(t *te return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2253,7 +2281,7 @@ func TestTxProcessor_ProcessRelayedTransactionFailUnMarshalInnerShouldError(t *t tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{[]byte("0")}, nil }} @@ -2280,8 +2308,8 @@ func TestTxProcessor_ProcessRelayedTransactionFailUnMarshalInnerShouldError(t *t return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2316,7 +2344,7 @@ func TestTxProcessor_ProcessRelayedTransactionDifferentSenderInInnerTxThanReceiv tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2343,8 +2371,8 @@ func TestTxProcessor_ProcessRelayedTransactionDifferentSenderInInnerTxThanReceiv return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2379,7 +2407,7 @@ func TestTxProcessor_ProcessRelayedTransactionSmallerValueInnerTxShouldError(t * tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2406,8 +2434,8 @@ func TestTxProcessor_ProcessRelayedTransactionSmallerValueInnerTxShouldError(t * return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2442,7 +2470,7 @@ func TestTxProcessor_ProcessRelayedTransactionGasPriceMismatchShouldError(t *tes tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2469,8 +2497,8 @@ func TestTxProcessor_ProcessRelayedTransactionGasPriceMismatchShouldError(t *tes return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2505,7 +2533,7 @@ func TestTxProcessor_ProcessRelayedTransactionGasLimitMismatchShouldError(t *tes tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2532,8 +2560,8 @@ func TestTxProcessor_ProcessRelayedTransactionGasLimitMismatchShouldError(t *tes return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2626,17 +2654,179 @@ func TestTxProcessor_ProcessRelayedTransactionDisabled(t *testing.T) { assert.True(t, called) } +func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { + t.Parallel() + + pubKeyConverter := testscommon.NewPubkeyConverterMock(4) + + userAddr := []byte("user") + tx := &transaction.Transaction{} + tx.Nonce = 0 + tx.SndAddr = []byte("sSRC") + tx.RcvAddr = userAddr + tx.Value = big.NewInt(45) + tx.GasPrice = 1 + tx.GasLimit = 1 + tx.RelayerAddr = []byte("rela") + tx.Signature = []byte("ssig") + tx.RelayerSignature = []byte("rsig") + + acntSrc := createUserAcc(tx.SndAddr) + _ = acntSrc.AddToBalance(big.NewInt(100)) + acntDst := createUserAcc(tx.RcvAddr) + _ = acntDst.AddToBalance(big.NewInt(10)) + acntRelayer := createUserAcc(tx.RelayerAddr) + _ = acntRelayer.AddToBalance(big.NewInt(10)) + + adb := &stateMock.AccountsStub{} + adb.LoadAccountCalled = func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, tx.SndAddr) { + return acntSrc, nil + } + if bytes.Equal(address, tx.RcvAddr) { + return acntDst, nil + } + if bytes.Equal(address, tx.RelayerAddr) { + return acntRelayer, nil + } + + return nil, errors.New("failure") + } + scProcessorMock := &testscommon.SCProcessorMock{} + shardC, _ := sharding.NewMultiShardCoordinator(1, 0) + + esdtTransferParser, _ := parsers.NewESDTTransferParser(&mock.MarshalizerMock{}) + argTxTypeHandler := coordinator.ArgNewTxTypeHandler{ + PubkeyConverter: pubKeyConverter, + ShardCoordinator: shardC, + BuiltInFunctions: builtInFunctions.NewBuiltInFunctionContainer(), + ArgumentParser: parsers.NewCallArgsParser(), + ESDTTransferParser: esdtTransferParser, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), + } + txTypeHandler, _ := coordinator.NewTxTypeHandler(argTxTypeHandler) + + args := createArgsForTxProcessor() + args.Accounts = adb + args.ScProcessor = scProcessorMock + args.ShardCoordinator = shardC + args.TxTypeHandler = txTypeHandler + args.PubkeyConv = pubKeyConverter + args.ArgsParser = smartContract.NewArgumentParser() + args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag) + txProc, _ := txproc.NewTxProcessor(args) + require.NotNil(t, txProc) + + t.Run("should work", func(t *testing.T) { + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProc.ProcessTransaction(&txCopy) + assert.NoError(t, err) + assert.Equal(t, vmcommon.Ok, returnCode) + }) + t.Run("flag not active should error", func(t *testing.T) { + argsCopy := args + argsCopy.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrRelayedTxV3Disabled.Error())) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("relayer not in the same shard with the sender should error", func(t *testing.T) { + argsCopy := args + argsCopy.ShardCoordinator = &mock.ShardCoordinatorStub{ + SameShardCalled: func(firstAddress, secondAddress []byte) bool { + return false + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrShardIdMissmatch.Error())) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("guarded relayer account should error", func(t *testing.T) { + acntRelayerCopy := acntRelayer + acntRelayerCopy.IsGuarded() + argsCopy := args + argsCopy.Accounts = &stateMock.AccountsStub{ + LoadAccountCalled: func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, tx.SndAddr) { + return acntSrc, nil + } + if bytes.Equal(address, tx.RcvAddr) { + return acntDst, nil + } + if bytes.Equal(address, tx.RelayerAddr) { + return &stateMock.UserAccountStub{ + IsGuardedCalled: func() bool { + return true + }, + Balance: big.NewInt(1), + }, nil + } + + return nil, errors.New("failure") + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrGuardedRelayerNotAllowed.Error())) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("same guardian and relayer should error", func(t *testing.T) { + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + txCopy.GuardianAddr = txCopy.RelayerAddr + returnCode, err := txProc.ProcessTransaction(&txCopy) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrRelayedByGuardianNotAllowed.Error())) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("insufficient gas limit should error", func(t *testing.T) { + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + argsCopy := args + argsCopy.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ + ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { + return txCopy.GasLimit + 1 + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.Equal(t, process.ErrNotEnoughGas, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) +} + func TestTxProcessor_ConsumeMoveBalanceWithUserTx(t *testing.T) { t.Parallel() args := createArgsForTxProcessor() + args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub() args.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ - ComputeFeeForProcessingCalled: func(tx data.TransactionWithFeeHandler, gasToUse uint64) *big.Int { - return big.NewInt(1) - }, ComputeTxFeeCalled: func(tx data.TransactionWithFeeHandler) *big.Int { return big.NewInt(150) }, + ComputeFeeForProcessingCalled: func(tx data.TransactionWithFeeHandler, gasToUse uint64) *big.Int { + return big.NewInt(1) + }, } args.TxFeeHandler = &mock.FeeAccumulatorStub{ ProcessTransactionFeeCalled: func(cost *big.Int, devFee *big.Int, hash []byte) { @@ -2658,7 +2848,7 @@ func TestTxProcessor_ConsumeMoveBalanceWithUserTx(t *testing.T) { err := execTx.ProcessMoveBalanceCostRelayedUserTx(userTx, &smartContractResult.SmartContractResult{}, acntSrc, originalTxHash) assert.Nil(t, err) - assert.Equal(t, acntSrc.GetBalance(), big.NewInt(99)) + assert.Equal(t, big.NewInt(99), acntSrc.GetBalance()) } func TestTxProcessor_IsCrossTxFromMeShouldWork(t *testing.T) { @@ -2700,7 +2890,7 @@ func TestTxProcessor_ProcessUserTxOfTypeRelayedShouldError(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2727,14 +2917,13 @@ func TestTxProcessor_ProcessUserTxOfTypeRelayedShouldError(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.UserError, returnCode) } @@ -2764,7 +2953,7 @@ func TestTxProcessor_ProcessUserTxOfTypeMoveBalanceShouldWork(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2791,14 +2980,13 @@ func TestTxProcessor_ProcessUserTxOfTypeMoveBalanceShouldWork(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.MoveBalance, process.MoveBalance + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.MoveBalance, process.MoveBalance, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -2828,7 +3016,7 @@ func TestTxProcessor_ProcessUserTxOfTypeSCDeploymentShouldWork(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2855,14 +3043,13 @@ func TestTxProcessor_ProcessUserTxOfTypeSCDeploymentShouldWork(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.SCDeployment, process.SCDeployment + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.SCDeployment, process.SCDeployment, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -2892,7 +3079,7 @@ func TestTxProcessor_ProcessUserTxOfTypeSCInvokingShouldWork(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2919,14 +3106,13 @@ func TestTxProcessor_ProcessUserTxOfTypeSCInvokingShouldWork(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.SCInvoking, process.SCInvoking + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.SCInvoking, process.SCInvoking, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -2956,7 +3142,7 @@ func TestTxProcessor_ProcessUserTxOfTypeBuiltInFunctionCallShouldWork(t *testing tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -2983,14 +3169,13 @@ func TestTxProcessor_ProcessUserTxOfTypeBuiltInFunctionCallShouldWork(t *testing return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -3020,7 +3205,7 @@ func TestTxProcessor_ProcessUserTxErrNotPayableShouldFailRelayTx(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -3051,14 +3236,13 @@ func TestTxProcessor_ProcessUserTxErrNotPayableShouldFailRelayTx(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.MoveBalance, process.MoveBalance + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.MoveBalance, process.MoveBalance, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.UserError, returnCode) } @@ -3088,7 +3272,7 @@ func TestTxProcessor_ProcessUserTxFailedBuiltInFunctionCall(t *testing.T) { tx.Data = []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxMarshalled)) args := createArgsForTxProcessor() - args.ArgsParser = &mock.ArgumentParserMock{ + args.ArgsParser = &testscommon.ArgumentParserMock{ ParseCallDataCalled: func(data string) (string, [][]byte, error) { return core.RelayedTransaction, [][]byte{userTxMarshalled}, nil }} @@ -3121,14 +3305,13 @@ func TestTxProcessor_ProcessUserTxFailedBuiltInFunctionCall(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }} execTx, _ := txproc.NewTxProcessor(args) - txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, tx) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.ExecutionFailed, returnCode) } @@ -3213,7 +3396,7 @@ func TestTxProcessor_shouldIncreaseNonce(t *testing.T) { t.Run("fix not enabled, should return true", func(t *testing.T) { args := createArgsForTxProcessor() - args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedNonceFixFlag) txProc, _ := txproc.NewTxProcessor(args) assert.True(t, txProc.ShouldIncreaseNonce(nil)) @@ -3306,3 +3489,85 @@ func TestTxProcessor_AddNonExecutableLog(t *testing.T) { assert.Equal(t, 3, numLogsSaved) }) } + +func TestTxProcessor_ProcessMoveBalanceToNonPayableContract(t *testing.T) { + t.Parallel() + + shardCoordinator := mock.NewOneShardCoordinatorMock() + marshaller := marshal.JsonMarshalizer{} + + innerTx := transaction.Transaction{} + innerTx.Nonce = 1 + innerTx.SndAddr = []byte("SRC") + innerTx.RcvAddr = make([]byte, 32) + innerTx.Value = big.NewInt(100) + + tx := transaction.Transaction{} + tx.Nonce = 0 + tx.SndAddr = []byte("SRC") + tx.RcvAddr = []byte("SRC") + tx.Value = big.NewInt(0) + marshalledData, _ := marshaller.Marshal(&innerTx) + tx.Data = []byte("relayedTx@" + hex.EncodeToString(marshalledData)) + + shardCoordinator.ComputeIdCalled = func(address []byte) uint32 { + return 0 + } + + acntSrc := createUserAcc(innerTx.SndAddr) + initialBalance := big.NewInt(100) + _ = acntSrc.AddToBalance(initialBalance) + acntDst := createUserAcc(innerTx.RcvAddr) + + adb := createAccountStub(innerTx.SndAddr, innerTx.RcvAddr, acntSrc, acntDst) + + args := createArgsForTxProcessor() + args.Accounts = adb + args.ShardCoordinator = shardCoordinator + args.SignMarshalizer = &marshaller + cnt := 0 + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + cnt++ + if cnt == 1 { + return process.RelayedTx, process.RelayedTx, false + } + + return process.MoveBalance, process.MoveBalance, false + }, + } + args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub( + common.RelayedTransactionsFlag, + common.FixRelayedBaseCostFlag, + common.FixRelayedMoveBalanceToNonPayableSCFlag, + ) + args.ScProcessor = &testscommon.SCProcessorMock{ + IsPayableCalled: func(sndAddress, recvAddress []byte) (bool, error) { + return false, nil + }, + } + args.ArgsParser = &testscommon.ArgumentParserMock{ + ParseCallDataCalled: func(data string) (string, [][]byte, error) { + return core.RelayedTransaction, [][]byte{marshalledData}, nil + }, + } + execTx, _ := txproc.NewTxProcessor(args) + + _, err := execTx.ProcessTransaction(&tx) + assert.Nil(t, err) + + balance := acntSrc.GetBalance() + require.Equal(t, initialBalance, balance) // disabled economics data, testing only tx.value +} + +func TestTxProcessor_IsInterfaceNil(t *testing.T) { + t.Parallel() + + args := createArgsForTxProcessor() + args.Hasher = nil + proc, _ := txproc.NewTxProcessor(args) + require.True(t, proc.IsInterfaceNil()) + + proc, _ = txproc.NewTxProcessor(createArgsForTxProcessor()) + require.False(t, proc.IsInterfaceNil()) +} diff --git a/process/transactionEvaluator/simulationAccountsDB_test.go b/process/transactionEvaluator/simulationAccountsDB_test.go index 13655ba315f..16c9e7effdd 100644 --- a/process/transactionEvaluator/simulationAccountsDB_test.go +++ b/process/transactionEvaluator/simulationAccountsDB_test.go @@ -37,36 +37,36 @@ func TestReadOnlyAccountsDB_WriteOperationsShouldNotCalled(t *testing.T) { failErrMsg := "this function should have not be called" accDb := &stateMock.AccountsStub{ SaveAccountCalled: func(account vmcommon.AccountHandler) error { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) return nil }, RemoveAccountCalled: func(_ []byte) error { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) return nil }, CommitCalled: func() ([]byte, error) { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) return nil, nil }, RevertToSnapshotCalled: func(_ int) error { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) return nil }, RecreateTrieCalled: func(_ common.RootHashHolder) error { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) return nil }, PruneTrieCalled: func(_ []byte, _ state.TriePruningIdentifier, _ state.PruningHandler) { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) }, CancelPruneCalled: func(_ []byte, _ state.TriePruningIdentifier) { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) }, SnapshotStateCalled: func(_ []byte, _ uint32) { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) }, RecreateAllTriesCalled: func(_ []byte) (map[string]common.Trie, error) { - t.Errorf(failErrMsg) + t.Errorf("%s", failErrMsg) return nil, nil }, } diff --git a/process/transactionEvaluator/transactionEvaluator.go b/process/transactionEvaluator/transactionEvaluator.go index 9e61d138419..c2e566490ef 100644 --- a/process/transactionEvaluator/transactionEvaluator.go +++ b/process/transactionEvaluator/transactionEvaluator.go @@ -111,7 +111,7 @@ func (ate *apiTransactionEvaluator) ComputeTransactionGasLimit(tx *transaction.T ate.mutExecution.Unlock() }() - txTypeOnSender, txTypeOnDestination := ate.txTypeHandler.ComputeTransactionType(tx) + txTypeOnSender, txTypeOnDestination, _ := ate.txTypeHandler.ComputeTransactionType(tx) if txTypeOnSender == process.MoveBalance && txTypeOnDestination == process.MoveBalance { return ate.computeMoveBalanceCost(tx), nil } diff --git a/process/transactionEvaluator/transactionEvaluator_test.go b/process/transactionEvaluator/transactionEvaluator_test.go index f36a5388777..bfcbf97f787 100644 --- a/process/transactionEvaluator/transactionEvaluator_test.go +++ b/process/transactionEvaluator/transactionEvaluator_test.go @@ -114,8 +114,8 @@ func TestComputeTransactionGasLimit_MoveBalance(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.MoveBalance + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.MoveBalance, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -153,8 +153,8 @@ func TestComputeTransactionGasLimit_MoveBalanceInvalidNonceShouldStillComputeCos args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.MoveBalance + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.MoveBalance, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -187,8 +187,8 @@ func TestComputeTransactionGasLimit_BuiltInFunction(t *testing.T) { consumedGasUnits := uint64(4000) args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -223,8 +223,8 @@ func TestComputeTransactionGasLimit_BuiltInFunctionShouldErr(t *testing.T) { localErr := errors.New("local err") args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -253,8 +253,8 @@ func TestComputeTransactionGasLimit_BuiltInFunctionShouldErr(t *testing.T) { func TestComputeTransactionGasLimit_NilVMOutput(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -284,8 +284,8 @@ func TestComputeTransactionGasLimit_NilVMOutput(t *testing.T) { func TestComputeTransactionGasLimit_RetCodeNotOk(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -321,8 +321,8 @@ func TestTransactionEvaluator_RelayedTxShouldErr(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.RelayedTx, process.RelayedTx + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.RelayedTx, process.RelayedTx, false }, } tce, _ := NewAPITransactionEvaluator(args) @@ -386,8 +386,8 @@ func TestApiTransactionEvaluator_ComputeTransactionGasLimit(t *testing.T) { _ = args.BlockChain.SetCurrentBlockHeaderAndRootHash(&block.Header{Nonce: expectedNonce}, []byte("test")) args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } args.TxSimulator = &mock.TransactionSimulatorStub{ diff --git a/process/transactionLog/printTxLogProcessor_test.go b/process/transactionLog/printTxLogProcessor_test.go index 5074ec617a4..c442440afb9 100644 --- a/process/transactionLog/printTxLogProcessor_test.go +++ b/process/transactionLog/printTxLogProcessor_test.go @@ -65,9 +65,6 @@ func TestPrintTxLogProcessor_SaveLog(t *testing.T) { err := ptlp.SaveLog([]byte("hash"), &transaction.Transaction{}, txLogEntry) require.Nil(t, err) - err = ptlp.SaveLog([]byte("hash"), &transaction.Transaction{}, nil) - require.Nil(t, err) - require.True(t, strings.Contains(buff.String(), "printTxLogProcessor.SaveLog")) require.True(t, strings.Contains(buff.String(), "printTxLogProcessor.entry")) } diff --git a/process/transactionLog/process.go b/process/transactionLog/process.go index 76a44294cd2..39b74f4b02a 100644 --- a/process/transactionLog/process.go +++ b/process/transactionLog/process.go @@ -36,7 +36,7 @@ type txLogProcessor struct { } // NewTxLogProcessor creates a transaction log processor capable of parsing logs from the VM -// and saving them into the injected storage +// and saving them into the injected storage func NewTxLogProcessor(args ArgTxLogProcessor) (*txLogProcessor, error) { storer := args.Storer if check.IfNil(storer) && args.SaveInStorageEnabled { @@ -179,7 +179,6 @@ func (tlp *txLogProcessor) saveLogToCache(txHash []byte, log *transaction.Log) { }) tlp.logsIndices[string(txHash)] = len(tlp.logs) - 1 tlp.mut.Unlock() - } // For SC deployment transactions, we use the sender address diff --git a/process/transactionLog/process_test.go b/process/transactionLog/process_test.go index f132c865486..c4f58322056 100644 --- a/process/transactionLog/process_test.go +++ b/process/transactionLog/process_test.go @@ -8,6 +8,7 @@ import ( "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/mock" "github.com/multiversx/mx-chain-go/process/transactionLog" + "github.com/multiversx/mx-chain-go/testscommon" storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" @@ -88,7 +89,7 @@ func TestTxLogProcessor_SaveLogsMarshalErr(t *testing.T) { retErr := errors.New("marshal err") txLogProcessor, _ := transactionLog.NewTxLogProcessor(transactionLog.ArgTxLogProcessor{ Storer: &storageStubs.StorerStub{}, - Marshalizer: &mock.MarshalizerStub{ + Marshalizer: &testscommon.MarshallerStub{ MarshalCalled: func(obj interface{}) (bytes []byte, err error) { return nil, retErr }, @@ -111,7 +112,7 @@ func TestTxLogProcessor_SaveLogsStoreErr(t *testing.T) { return retErr }, }, - Marshalizer: &mock.MarshalizerStub{ + Marshalizer: &testscommon.MarshallerStub{ MarshalCalled: func(obj interface{}) (bytes []byte, err error) { return nil, nil }, @@ -138,7 +139,7 @@ func TestTxLogProcessor_SaveLogsCallsPutWithMarshalBuff(t *testing.T) { return nil }, }, - Marshalizer: &mock.MarshalizerStub{ + Marshalizer: &testscommon.MarshallerStub{ MarshalCalled: func(obj interface{}) (bytes []byte, err error) { log, _ := obj.(*transaction.Log) require.Equal(t, expectedLogData[0], log.Events[0].Data) @@ -164,7 +165,7 @@ func TestTxLogProcessor_GetLogErrNotFound(t *testing.T) { return nil, errors.New("storer error") }, }, - Marshalizer: &mock.MarshalizerStub{}, + Marshalizer: &testscommon.MarshallerStub{}, SaveInStorageEnabled: true, }) @@ -181,7 +182,7 @@ func TestTxLogProcessor_GetLogUnmarshalErr(t *testing.T) { return make([]byte, 0), nil }, }, - Marshalizer: &mock.MarshalizerStub{ + Marshalizer: &testscommon.MarshallerStub{ UnmarshalCalled: func(obj interface{}, buff []byte) error { return retErr }, @@ -240,3 +241,19 @@ func TestTxLogProcessor_GetLogFromCacheNotInCacheShouldReturnFromStorage(t *test _, found := txLogProcessor.GetLogFromCache([]byte("txhash")) require.True(t, found) } + +func TestTxLogProcessor_IsInterfaceNil(t *testing.T) { + t.Parallel() + + txLogProcessor, _ := transactionLog.NewTxLogProcessor(transactionLog.ArgTxLogProcessor{ + Storer: &storageStubs.StorerStub{}, + Marshalizer: nil, + }) + require.True(t, txLogProcessor.IsInterfaceNil()) + + txLogProcessor, _ = transactionLog.NewTxLogProcessor(transactionLog.ArgTxLogProcessor{ + Storer: &storageStubs.StorerStub{}, + Marshalizer: &testscommon.MarshallerStub{}, + }) + require.False(t, txLogProcessor.IsInterfaceNil()) +} diff --git a/sharding/errors.go b/sharding/errors.go index 8aa23d74d7a..e6c2c29984c 100644 --- a/sharding/errors.go +++ b/sharding/errors.go @@ -57,3 +57,6 @@ var ErrNoMatchingConfigurationFound = errors.New("no matching configuration foun // ErrNilChainParametersNotifier signals that a nil chain parameters notifier has been provided var ErrNilChainParametersNotifier = errors.New("nil chain parameters notifier") + +// ErrInvalidChainParametersForEpoch signals that an invalid chain parameters for epoch has been provided +var ErrInvalidChainParametersForEpoch = errors.New("invalid chain parameters for epoch") diff --git a/sharding/mock/pubkeyConverterMock.go b/sharding/mock/pubkeyConverterMock.go index e81d21ff4f6..2679da82d02 100644 --- a/sharding/mock/pubkeyConverterMock.go +++ b/sharding/mock/pubkeyConverterMock.go @@ -8,7 +8,8 @@ import ( // PubkeyConverterMock - type PubkeyConverterMock struct { - len int + len int + DecodeCalled func() ([]byte, error) } // NewPubkeyConverterMock - @@ -20,6 +21,9 @@ func NewPubkeyConverterMock(addressLen int) *PubkeyConverterMock { // Decode - func (pcm *PubkeyConverterMock) Decode(humanReadable string) ([]byte, error) { + if pcm.DecodeCalled != nil { + return pcm.DecodeCalled() + } return hex.DecodeString(humanReadable) } diff --git a/sharding/nodesCoordinator/common_test.go b/sharding/nodesCoordinator/common_test.go index 50be55fd1ae..b7902db0c7e 100644 --- a/sharding/nodesCoordinator/common_test.go +++ b/sharding/nodesCoordinator/common_test.go @@ -5,10 +5,13 @@ import ( "encoding/binary" "fmt" "math/big" + "strconv" "testing" - "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" ) func TestComputeStartIndexAndNumAppearancesForValidator(t *testing.T) { @@ -151,3 +154,105 @@ func getExpandedEligibleList(num int) []uint32 { func newValidatorMock(pubKey []byte, chances uint32, index uint32) *validator { return &validator{pubKey: pubKey, index: index, chances: chances} } + +func TestSerializableShardValidatorListToValidatorListShouldErrNilPubKey(t *testing.T) { + t.Parallel() + + listOfSerializableValidators := []*SerializableValidator{ + { + PubKey: nil, + Chances: 1, + Index: 1, + }, + } + + _, err := SerializableShardValidatorListToValidatorList(listOfSerializableValidators) + require.Equal(t, ErrNilPubKey, err) +} + +func TestSerializableShardValidatorListToValidatorListShouldWork(t *testing.T) { + t.Parallel() + + listOfSerializableValidators := []*SerializableValidator{ + { + PubKey: []byte("pubkey"), + Chances: 1, + Index: 1, + }, + } + + expectedListOfValidators := make([]Validator, 1) + v, _ := NewValidator(listOfSerializableValidators[0].PubKey, listOfSerializableValidators[0].Chances, listOfSerializableValidators[0].Index) + require.NotNil(t, v) + expectedListOfValidators[0] = v + + valReturned, err := SerializableShardValidatorListToValidatorList(listOfSerializableValidators) + + require.Nil(t, err) + require.Equal(t, expectedListOfValidators, valReturned) +} + +func TestSerializableValidatorsToValidatorsShouldWork(t *testing.T) { + t.Parallel() + + mapOfSerializableValidators := make(map[string][]*SerializableValidator, 1) + mapOfSerializableValidators["1"] = []*SerializableValidator{ + { + PubKey: []byte("pubkey"), + Chances: 1, + Index: 1, + }, + } + + expectedMapOfValidators := make(map[uint32][]Validator, 1) + + v, _ := NewValidator(mapOfSerializableValidators["1"][0].PubKey, mapOfSerializableValidators["1"][0].Chances, mapOfSerializableValidators["1"][0].Index) + expectedMapOfValidators[uint32(1)] = []Validator{v} + + require.NotNil(t, v) + + valReturned, err := SerializableValidatorsToValidators(mapOfSerializableValidators) + + require.Nil(t, err) + require.Equal(t, expectedMapOfValidators, valReturned) +} + +func TestSerializableValidatorsToValidatorsShouldErrNilPubKey(t *testing.T) { + t.Parallel() + + mapOfSerializableValidators := make(map[string][]*SerializableValidator, 1) + mapOfSerializableValidators["1"] = []*SerializableValidator{ + { + PubKey: nil, + Chances: 1, + Index: 1, + }, + } + + _, err := SerializableValidatorsToValidators(mapOfSerializableValidators) + + require.Equal(t, ErrNilPubKey, err) +} + +func TestSerializableValidatorsToValidatorsShouldErrEmptyString(t *testing.T) { + t.Parallel() + + mapOfSerializableValidators := make(map[string][]*SerializableValidator, 1) + mapOfSerializableValidators[""] = []*SerializableValidator{ + { + PubKey: []byte("pubkey"), + Chances: 1, + Index: 1, + }, + } + + expectedMapOfValidators := make(map[uint32][]Validator, 1) + + v, _ := NewValidator(mapOfSerializableValidators[""][0].PubKey, mapOfSerializableValidators[""][0].Chances, mapOfSerializableValidators[""][0].Index) + require.NotNil(t, v) + expectedMapOfValidators[uint32(1)] = []Validator{v} + + _, err := SerializableValidatorsToValidators(mapOfSerializableValidators) + + require.Equal(t, &strconv.NumError{Func: "ParseUint", Num: "", Err: strconv.ErrSyntax}, err) +} diff --git a/sharding/nodesSetup_test.go b/sharding/nodesSetup_test.go index 23f95c854d4..f5e6de19a8b 100644 --- a/sharding/nodesSetup_test.go +++ b/sharding/nodesSetup_test.go @@ -5,10 +5,11 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/sharding/mock" "github.com/multiversx/mx-chain-go/testscommon/chainParameters" - "github.com/stretchr/testify/require" ) var ( @@ -95,6 +96,35 @@ func createTestNodesSetup(args argsTestNodesSetup) (*NodesSetup, error) { return ns, err } +func createTestNodesSetupWithSpecificMockedComponents(args argsTestNodesSetup, + initialNodes []*config.InitialNodeConfig, + addressPubkeyConverter core.PubkeyConverter, + validatorPubkeyConverter core.PubkeyConverter) (*NodesSetup, error) { + + ns, err := NewNodesSetup( + config.NodesConfig{ + StartTime: 0, + InitialNodes: initialNodes, + }, + &chainParameters.ChainParametersHandlerStub{ + ChainParametersForEpochCalled: func(epoch uint32) (config.ChainParametersByEpochConfig, error) { + return config.ChainParametersByEpochConfig{ + EnableEpoch: 0, + ShardMinNumNodes: args.shardMinNodes, + ShardConsensusGroupSize: args.shardConsensusSize, + MetachainMinNumNodes: args.metaMinNodes, + MetachainConsensusGroupSize: args.metaConsensusSize, + }, nil + }, + }, + addressPubkeyConverter, + validatorPubkeyConverter, + args.genesisMaxShards, + ) + + return ns, err +} + func TestNodesSetup_ProcessConfigNodesWithIncompleteDataShouldErr(t *testing.T) { t.Parallel() @@ -118,6 +148,113 @@ func TestNodesSetup_ProcessConfigNodesWithIncompleteDataShouldErr(t *testing.T) require.Equal(t, ErrCouldNotParsePubKey, err) } +func TestNodesSetup_ProcessConfigNodesShouldErrCouldNotParsePubKeyForString(t *testing.T) { + t.Parallel() + + mockedNodes := make([]*config.InitialNodeConfig, 2) + mockedNodes[0] = &config.InitialNodeConfig{ + PubKey: pubKeys[0], + Address: address[0], + } + + mockedNodes[1] = &config.InitialNodeConfig{ + PubKey: pubKeys[1], + Address: address[1], + } + + addressPubkeyConverterMocked := mock.NewPubkeyConverterMock(32) + validatorPubkeyConverterMocked := &mock.PubkeyConverterMock{ + DecodeCalled: func() ([]byte, error) { + return nil, ErrCouldNotParsePubKey + }, + } + + _, err := createTestNodesSetupWithSpecificMockedComponents(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 2, + genesisMaxShards: 3, + }, + mockedNodes, + addressPubkeyConverterMocked, + validatorPubkeyConverterMocked, + ) + + require.ErrorIs(t, err, ErrCouldNotParsePubKey) +} + +func TestNodesSetup_ProcessConfigNodesShouldErrCouldNotParseAddressForString(t *testing.T) { + t.Parallel() + + mockedNodes := make([]*config.InitialNodeConfig, 2) + mockedNodes[0] = &config.InitialNodeConfig{ + PubKey: pubKeys[0], + Address: address[0], + } + + mockedNodes[1] = &config.InitialNodeConfig{ + PubKey: pubKeys[1], + Address: address[1], + } + + addressPubkeyConverterMocked := &mock.PubkeyConverterMock{ + DecodeCalled: func() ([]byte, error) { + return nil, ErrCouldNotParseAddress + }, + } + validatorPubkeyConverterMocked := mock.NewPubkeyConverterMock(96) + + _, err := createTestNodesSetupWithSpecificMockedComponents(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 2, + genesisMaxShards: 3, + }, + mockedNodes, + addressPubkeyConverterMocked, + validatorPubkeyConverterMocked, + ) + + require.ErrorIs(t, err, ErrCouldNotParseAddress) +} + +func TestNodesSetup_ProcessConfigNodesWithEmptyDataShouldErrCouldNotParseAddress(t *testing.T) { + t.Parallel() + + mockedNodes := make([]*config.InitialNodeConfig, 2) + mockedNodes[0] = &config.InitialNodeConfig{ + PubKey: pubKeys[0], + Address: address[0], + } + + mockedNodes[1] = &config.InitialNodeConfig{ + PubKey: pubKeys[1], + Address: "", + } + + addressPubkeyConverterMocked := mock.NewPubkeyConverterMock(32) + validatorPubkeyConverterMocked := mock.NewPubkeyConverterMock(96) + + _, err := createTestNodesSetupWithSpecificMockedComponents(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 2, + genesisMaxShards: 3, + }, + mockedNodes, + addressPubkeyConverterMocked, + validatorPubkeyConverterMocked, + ) + + require.ErrorIs(t, err, ErrCouldNotParseAddress) +} + func TestNodesSetup_ProcessConfigInvalidConsensusGroupSizeShouldErr(t *testing.T) { t.Parallel() @@ -193,6 +330,21 @@ func TestNodesSetup_ProcessConfigInvalidNumOfNodesSmallerThanMinNodesPerShardSho require.Equal(t, ErrNodesSizeSmallerThanMinNoOfNodes, err) } +func TestNodesSetup_ProcessConfigInvalidNumOfNodesSmallerThanTotalMinNodesShouldErr(t *testing.T) { + t.Parallel() + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 2, + shardMinNodes: 3, + metaConsensusSize: 1, + metaMinNodes: 3, + numInitialNodes: 5, + genesisMaxShards: 3, + }) + require.Nil(t, ns) + require.Equal(t, ErrNodesSizeSmallerThanMinNoOfNodes, err) +} + func TestNodesSetup_InitialNodesPubKeysWithHysteresis(t *testing.T) { t.Parallel() @@ -425,6 +577,66 @@ func TestNewNodesSetup_InvalidMaxNumShardsShouldErr(t *testing.T) { require.Contains(t, err.Error(), ErrInvalidMaximumNumberOfShards.Error()) } +func TestNewNodesSetup_ErrNilPubkeyConverterForAddressPubkeyConverter(t *testing.T) { + t.Parallel() + + _, err := NewNodesSetup( + config.NodesConfig{}, + &chainParameters.ChainParametersHandlerStub{}, + nil, + mock.NewPubkeyConverterMock(96), + 3, + ) + + require.ErrorIs(t, err, ErrNilPubkeyConverter) +} + +func TestNewNodesSetup_ErrNilPubkeyConverterForValidatorPubkeyConverter(t *testing.T) { + t.Parallel() + + _, err := NewNodesSetup( + config.NodesConfig{}, + &chainParameters.ChainParametersHandlerStub{}, + mock.NewPubkeyConverterMock(32), + nil, + 3, + ) + + require.ErrorIs(t, err, ErrNilPubkeyConverter) +} + +func TestNewNodesSetup_ErrNilChainParametersProvider(t *testing.T) { + t.Parallel() + + _, err := NewNodesSetup( + config.NodesConfig{}, + nil, + mock.NewPubkeyConverterMock(32), + mock.NewPubkeyConverterMock(96), + 3, + ) + + require.Equal(t, err, ErrNilChainParametersProvider) +} + +func TestNewNodesSetup_ErrChainParametersForEpoch(t *testing.T) { + t.Parallel() + + _, err := NewNodesSetup( + config.NodesConfig{}, + &chainParameters.ChainParametersHandlerStub{ + ChainParametersForEpochCalled: func(epoch uint32) (config.ChainParametersByEpochConfig, error) { + return config.ChainParametersByEpochConfig{}, ErrInvalidChainParametersForEpoch + }, + }, + mock.NewPubkeyConverterMock(32), + mock.NewPubkeyConverterMock(96), + 3, + ) + + require.ErrorIs(t, err, ErrInvalidChainParametersForEpoch) +} + func TestNodesSetup_IfNodesWithinMaxShardLimitEquivalentDistribution(t *testing.T) { t.Parallel() @@ -514,3 +726,348 @@ func TestNodesSetup_NodesAboveMaxShardLimit(t *testing.T) { minHysteresisNodesMeta = ns.MinMetaHysteresisNodes() require.Equal(t, uint32(80), minHysteresisNodesMeta) } + +func TestNodesSetup_AllInitialNodesShouldWork(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 2 + + var listOfInitialNodes = [2]InitialNode{ + { + PubKey: pubKeys[0], + Address: address[0], + }, + { + PubKey: pubKeys[1], + Address: address[1], + }, + } + + var expectedConvertedPubKeys = make([][]byte, 2) + pubKeyConverter := mock.NewPubkeyConverterMock(96) + + for i, nod := range listOfInitialNodes { + convertedValue, err := pubKeyConverter.Decode(nod.PubKey) + require.Nil(t, err) + require.NotNil(t, convertedValue) + expectedConvertedPubKeys[i] = convertedValue + } + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 2, + genesisMaxShards: 1, + }) + + require.Nil(t, err) + ns.Hysteresis = 0.2 + ns.Adaptivity = false + + ns = createAndAssignNodes(ns, noOfInitialNodes) + + allInitialNodes := ns.AllInitialNodes() + + for i, expectedConvertedKey := range expectedConvertedPubKeys { + require.Equal(t, expectedConvertedKey, allInitialNodes[i].PubKeyBytes()) + } + +} + +func TestNodesSetup_InitialNodesInfoShouldWork(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 3 + + var listOfInitialNodes = [3]InitialNode{ + { + PubKey: pubKeys[0], + Address: address[0], + }, + { + PubKey: pubKeys[1], + Address: address[1], + }, + { + PubKey: pubKeys[2], + Address: address[2], + }, + } + + var listOfExpectedConvertedPubKeysEligibleNodes = make([][]byte, 2) + pubKeyConverter := mock.NewPubkeyConverterMock(96) + + for i := 0; i < 2; i++ { + convertedValue, err := pubKeyConverter.Decode(listOfInitialNodes[i].PubKey) + require.Nil(t, err) + require.NotNil(t, convertedValue) + listOfExpectedConvertedPubKeysEligibleNodes[i] = convertedValue + } + + var listOfExpectedConvertedPubKeysWaitingNode = make([][]byte, 1) + listOfExpectedConvertedPubKeysWaitingNode[0], _ = pubKeyConverter.Decode(listOfInitialNodes[2].PubKey) + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.Nil(t, err) + ns.Hysteresis = 0.2 + ns.Adaptivity = false + + ns = createAndAssignNodes(ns, noOfInitialNodes) + + allEligibleNodes, allWaitingNodes := ns.InitialNodesInfo() + + require.Equal(t, listOfExpectedConvertedPubKeysEligibleNodes[0], allEligibleNodes[(core.MetachainShardId)][0].PubKeyBytes()) + require.Equal(t, listOfExpectedConvertedPubKeysEligibleNodes[1], allEligibleNodes[0][0].PubKeyBytes()) + require.Equal(t, listOfExpectedConvertedPubKeysWaitingNode[0], allWaitingNodes[(core.MetachainShardId)][0].PubKeyBytes()) + +} + +func TestNodesSetup_InitialNodesPubKeysShouldWork(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 3 + + var listOfInitialNodes = [3]InitialNode{ + { + PubKey: pubKeys[0], + Address: address[0], + }, + { + PubKey: pubKeys[1], + Address: address[1], + }, + { + PubKey: pubKeys[2], + Address: address[2], + }, + } + + var listOfExpectedConvertedPubKeysEligibleNodes = make([]string, 2) + pubKeyConverter := mock.NewPubkeyConverterMock(96) + + for i := 0; i < 2; i++ { + convertedValue, err := pubKeyConverter.Decode(listOfInitialNodes[i].PubKey) + require.Nil(t, err) + require.NotNil(t, convertedValue) + listOfExpectedConvertedPubKeysEligibleNodes[i] = string(convertedValue) + } + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.Nil(t, err) + ns.Hysteresis = 0.2 + ns.Adaptivity = false + + ns = createAndAssignNodes(ns, noOfInitialNodes) + + allEligibleNodes := ns.InitialNodesPubKeys() + + require.Equal(t, listOfExpectedConvertedPubKeysEligibleNodes[0], allEligibleNodes[(core.MetachainShardId)][0]) + require.Equal(t, listOfExpectedConvertedPubKeysEligibleNodes[1], allEligibleNodes[0][0]) + +} + +func TestNodesSetup_InitialEligibleNodesPubKeysForShardShouldErrShardIdOutOfRange(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 3 + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.Nil(t, err) + ns.Hysteresis = 0.2 + ns.Adaptivity = false + + ns = createAndAssignNodes(ns, noOfInitialNodes) + + returnedPubKeys, err := ns.InitialEligibleNodesPubKeysForShard(1) + require.Nil(t, returnedPubKeys) + require.Equal(t, ErrShardIdOutOfRange, err) + +} + +func TestNodesSetup_InitialEligibleNodesPubKeysForShardShouldWork(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 3 + + var listOfInitialNodes = [3]InitialNode{ + { + PubKey: pubKeys[0], + Address: address[0], + }, + { + PubKey: pubKeys[1], + Address: address[1], + }, + { + PubKey: pubKeys[2], + Address: address[2], + }, + } + + var listOfExpectedPubKeysEligibleNodes = make([]string, 2) + pubKeyConverter := mock.NewPubkeyConverterMock(96) + + for i := 0; i < 2; i++ { + convertedValue, err := pubKeyConverter.Decode(listOfInitialNodes[i].PubKey) + require.Nil(t, err) + require.NotNil(t, convertedValue) + listOfExpectedPubKeysEligibleNodes[i] = string(convertedValue) + } + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.Nil(t, err) + ns.Hysteresis = 0.2 + ns.Adaptivity = false + + ns = createAndAssignNodes(ns, noOfInitialNodes) + + allEligibleNodes, err := ns.InitialEligibleNodesPubKeysForShard(0) + + require.Nil(t, err) + require.Equal(t, listOfExpectedPubKeysEligibleNodes[1], allEligibleNodes[0]) +} + +func TestNodesSetup_NumberOfShardsShouldWork(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 3 + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.Nil(t, err) + require.NotNil(t, ns) + + ns.Hysteresis = 0.2 + ns.Adaptivity = false + + ns = createAndAssignNodes(ns, noOfInitialNodes) + + require.NotNil(t, ns) + + valReturned := ns.NumberOfShards() + require.Equal(t, uint32(1), valReturned) + + valReturned = ns.MinNumberOfNodesWithHysteresis() + require.Equal(t, uint32(2), valReturned) + + valReturned = ns.MinNumberOfShardNodes() + require.Equal(t, uint32(1), valReturned) + + valReturned = ns.MinNumberOfShardNodes() + require.Equal(t, uint32(1), valReturned) + + shardConsensusGroupSize := ns.GetShardConsensusGroupSize() + require.Equal(t, uint32(1), shardConsensusGroupSize) + + metaConsensusGroupSize := ns.GetMetaConsensusGroupSize() + require.Equal(t, uint32(1), metaConsensusGroupSize) + + ns.Hysteresis = 0.5 + hysteresis := ns.GetHysteresis() + require.Equal(t, float32(0.5), hysteresis) + + ns.Adaptivity = true + adaptivity := ns.GetAdaptivity() + require.True(t, adaptivity) + + ns.StartTime = 2 + startTime := ns.GetStartTime() + require.Equal(t, int64(2), startTime) + + ns.RoundDuration = 2 + roundDuration := ns.GetRoundDuration() + require.Equal(t, uint64(2), roundDuration) + +} + +func TestNodesSetup_ExportNodesConfigShouldWork(t *testing.T) { + t.Parallel() + + noOfInitialNodes := 3 + + ns, err := createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.Nil(t, err) + + ns.Hysteresis = 0.2 + ns.Adaptivity = false + ns.StartTime = 10 + + ns = createAndAssignNodes(ns, noOfInitialNodes) + configNodes := ns.ExportNodesConfig() + + require.Equal(t, int64(10), configNodes.StartTime) + + var expectedNodesConfigs = make([]config.InitialNodeConfig, len(configNodes.InitialNodes)) + var actualNodesConfigs = make([]config.InitialNodeConfig, len(configNodes.InitialNodes)) + + for i, nodeConfig := range configNodes.InitialNodes { + expectedNodesConfigs[i] = config.InitialNodeConfig{PubKey: pubKeys[i], Address: address[i], InitialRating: 0} + actualNodesConfigs[i] = config.InitialNodeConfig{PubKey: nodeConfig.PubKey, Address: nodeConfig.Address, InitialRating: nodeConfig.InitialRating} + + } + + for i := range configNodes.InitialNodes { + require.Equal(t, expectedNodesConfigs[i], actualNodesConfigs[i]) + } + +} + +func TestNodesSetup_IsInterfaceNil(t *testing.T) { + t.Parallel() + + ns, _ := NewNodesSetup(config.NodesConfig{}, nil, nil, nil, 0) + require.True(t, ns.IsInterfaceNil()) + + ns, _ = createTestNodesSetup(argsTestNodesSetup{ + shardConsensusSize: 1, + shardMinNodes: 1, + metaConsensusSize: 1, + metaMinNodes: 1, + numInitialNodes: 3, + genesisMaxShards: 1, + }) + require.False(t, ns.IsInterfaceNil()) +} diff --git a/sharding/oneShardCoordinator_test.go b/sharding/oneShardCoordinator_test.go new file mode 100644 index 00000000000..c2c5d68edfe --- /dev/null +++ b/sharding/oneShardCoordinator_test.go @@ -0,0 +1,33 @@ +package sharding + +import ( + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/stretchr/testify/require" +) + +func TestOneShardCoordinator_NumberOfShardsShouldWork(t *testing.T) { + t.Parallel() + + oneShardCoordinator := OneShardCoordinator{} + + returnedVal := oneShardCoordinator.NumberOfShards() + require.Equal(t, uint32(1), returnedVal) + + returnedVal = oneShardCoordinator.ComputeId([]byte{}) + require.Equal(t, uint32(0), returnedVal) + + returnedVal = oneShardCoordinator.SelfId() + require.Equal(t, uint32(0), returnedVal) + + isShameShard := oneShardCoordinator.SameShard(nil, nil) + require.True(t, isShameShard) + + communicationID := oneShardCoordinator.CommunicationIdentifier(0) + require.Equal(t, core.CommunicationIdentifierBetweenShards(0, 0), communicationID) + + isInterfaceNil := oneShardCoordinator.IsInterfaceNil() + require.False(t, isInterfaceNil) + +} diff --git a/state/accountsDB.go b/state/accountsDB.go index 4274e5138d6..707361ae68d 100644 --- a/state/accountsDB.go +++ b/state/accountsDB.go @@ -482,9 +482,7 @@ func (adb *AccountsDB) saveDataTrie(accountHandler baseAccountHandler) error { } func (adb *AccountsDB) saveAccountToTrie(accountHandler vmcommon.AccountHandler, mainTrie common.Trie) error { - log.Trace("accountsDB.saveAccountToTrie", - "address", hex.EncodeToString(accountHandler.AddressBytes()), - ) + log.Trace("accountsDB.saveAccountToTrie", "address", accountHandler.AddressBytes()) // pass the reference to marshaller, otherwise it will fail marshalling balance buff, err := adb.marshaller.Marshal(accountHandler) @@ -601,9 +599,7 @@ func (adb *AccountsDB) LoadAccount(address []byte) (vmcommon.AccountHandler, err return nil, fmt.Errorf("%w in LoadAccount", ErrNilAddress) } - log.Trace("accountsDB.LoadAccount", - "address", hex.EncodeToString(address), - ) + log.Trace("accountsDB.LoadAccount", "address", address) mainTrie := adb.getMainTrie() acnt, err := adb.getAccount(address, mainTrie) @@ -653,9 +649,7 @@ func (adb *AccountsDB) GetExistingAccount(address []byte) (vmcommon.AccountHandl return nil, fmt.Errorf("%w in GetExistingAccount", ErrNilAddress) } - log.Trace("accountsDB.GetExistingAccount", - "address", hex.EncodeToString(address), - ) + log.Trace("accountsDB.GetExistingAccount", "address", address) mainTrie := adb.getMainTrie() acnt, err := adb.getAccount(address, mainTrie) diff --git a/state/interface.go b/state/interface.go index 0b53a4f5fcd..a9b460957b1 100644 --- a/state/interface.go +++ b/state/interface.go @@ -59,7 +59,7 @@ type PeerAccountHandler interface { GetTempRating() uint32 SetTempRating(uint32) GetConsecutiveProposerMisses() uint32 - SetConsecutiveProposerMisses(uint322 uint32) + SetConsecutiveProposerMisses(consecutiveMisses uint32) ResetAtNewEpoch() SetPreviousList(list string) vmcommon.AccountHandler diff --git a/statusHandler/statusMetricsProvider.go b/statusHandler/statusMetricsProvider.go index b47b6851eae..bc92e511c10 100644 --- a/statusHandler/statusMetricsProvider.go +++ b/statusHandler/statusMetricsProvider.go @@ -246,6 +246,7 @@ func (sm *statusMetrics) ConfigMetrics() (map[string]interface{}, error) { configMetrics[common.MetricMinGasPrice] = sm.uint64Metrics[common.MetricMinGasPrice] configMetrics[common.MetricMinGasLimit] = sm.uint64Metrics[common.MetricMinGasLimit] configMetrics[common.MetricExtraGasLimitGuardedTx] = sm.uint64Metrics[common.MetricExtraGasLimitGuardedTx] + configMetrics[common.MetricExtraGasLimitRelayedTx] = sm.uint64Metrics[common.MetricExtraGasLimitRelayedTx] configMetrics[common.MetricMaxGasPerTransaction] = sm.uint64Metrics[common.MetricMaxGasPerTransaction] configMetrics[common.MetricRoundDuration] = sm.uint64Metrics[common.MetricRoundDuration] configMetrics[common.MetricStartTime] = sm.uint64Metrics[common.MetricStartTime] @@ -377,6 +378,9 @@ func (sm *statusMetrics) EnableEpochsMetrics() (map[string]interface{}, error) { enableEpochsMetrics[common.MetricDynamicESDTEnableEpoch] = sm.uint64Metrics[common.MetricDynamicESDTEnableEpoch] enableEpochsMetrics[common.MetricEGLDInMultiTransferEnableEpoch] = sm.uint64Metrics[common.MetricEGLDInMultiTransferEnableEpoch] enableEpochsMetrics[common.MetricCryptoOpcodesV2EnableEpoch] = sm.uint64Metrics[common.MetricCryptoOpcodesV2EnableEpoch] + enableEpochsMetrics[common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch] = sm.uint64Metrics[common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch] + enableEpochsMetrics[common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch] = sm.uint64Metrics[common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch] + enableEpochsMetrics[common.MetricRelayedTransactionsV3EnableEpoch] = sm.uint64Metrics[common.MetricRelayedTransactionsV3EnableEpoch] numNodesChangeConfig := sm.uint64Metrics[common.MetricMaxNodesChangeEnableEpoch+"_count"] diff --git a/statusHandler/statusMetricsProvider_test.go b/statusHandler/statusMetricsProvider_test.go index 2eecf8cd598..40303970ce5 100644 --- a/statusHandler/statusMetricsProvider_test.go +++ b/statusHandler/statusMetricsProvider_test.go @@ -179,6 +179,7 @@ func TestStatusMetrics_NetworkConfig(t *testing.T) { sm.SetUInt64Value(common.MetricMinGasPrice, 1000) sm.SetUInt64Value(common.MetricMinGasLimit, 50000) sm.SetUInt64Value(common.MetricExtraGasLimitGuardedTx, 50000) + sm.SetUInt64Value(common.MetricExtraGasLimitRelayedTx, 50000) sm.SetStringValue(common.MetricRewardsTopUpGradientPoint, "12345") sm.SetUInt64Value(common.MetricGasPerDataByte, 1500) sm.SetStringValue(common.MetricChainId, "local-id") @@ -202,6 +203,7 @@ func TestStatusMetrics_NetworkConfig(t *testing.T) { "erd_meta_consensus_group_size": uint64(25), "erd_min_gas_limit": uint64(50000), "erd_extra_gas_limit_guarded_tx": uint64(50000), + "erd_extra_gas_limit_relayed_tx": uint64(50000), "erd_min_gas_price": uint64(1000), "erd_min_transaction_version": uint64(2), "erd_num_metachain_nodes": uint64(50), @@ -400,6 +402,9 @@ func TestStatusMetrics_EnableEpochMetrics(t *testing.T) { sm.SetUInt64Value(common.MetricDynamicESDTEnableEpoch, uint64(4)) sm.SetUInt64Value(common.MetricEGLDInMultiTransferEnableEpoch, uint64(4)) sm.SetUInt64Value(common.MetricCryptoOpcodesV2EnableEpoch, uint64(4)) + sm.SetUInt64Value(common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch, uint64(4)) + sm.SetUInt64Value(common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch, uint64(4)) + sm.SetUInt64Value(common.MetricRelayedTransactionsV3EnableEpoch, uint64(4)) maxNodesChangeConfig := []map[string]uint64{ { @@ -529,6 +534,9 @@ func TestStatusMetrics_EnableEpochMetrics(t *testing.T) { common.MetricDynamicESDTEnableEpoch: uint64(4), common.MetricEGLDInMultiTransferEnableEpoch: uint64(4), common.MetricCryptoOpcodesV2EnableEpoch: uint64(4), + common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch: uint64(4), + common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch: uint64(4), + common.MetricRelayedTransactionsV3EnableEpoch: uint64(4), common.MetricMaxNodesChangeEnableEpoch: []map[string]interface{}{ { diff --git a/storage/constants.go b/storage/constants.go index 9cd37571521..4924cbf06dd 100644 --- a/storage/constants.go +++ b/storage/constants.go @@ -19,9 +19,6 @@ const PathEpochPlaceholder = "[E]" // PathIdentifierPlaceholder represents the placeholder for the identifier in paths const PathIdentifierPlaceholder = "[I]" -// TxPoolNumTxsToPreemptivelyEvict instructs tx pool eviction algorithm to remove this many transactions when eviction takes place -const TxPoolNumTxsToPreemptivelyEvict = uint32(1000) - // DefaultDBPath is the default path for nodes databases const DefaultDBPath = "db" @@ -33,3 +30,12 @@ const DefaultStaticDbString = "Static" // DefaultShardString is the default folder root name for per shard databases const DefaultShardString = "Shard" + +// TxPoolSourceMeNumItemsToPreemptivelyEvict is a configuration of the eviction algorithm +const TxPoolSourceMeNumItemsToPreemptivelyEvict = uint32(50000) + +// TxPoolDestinationMeNumItemsToPreemptivelyEvict is a configuration of the eviction algorithm +const TxPoolDestinationMeNumItemsToPreemptivelyEvict = uint32(1000) + +// ShardedDataNumItemsToPreemptivelyEvict is a configuration of the eviction algorithm +const ShardedDataNumItemsToPreemptivelyEvict = uint32(1000) diff --git a/storage/pruning/fullHistoryPruningStorer.go b/storage/pruning/fullHistoryPruningStorer.go index 71213b1dcdd..97852aa3bcd 100644 --- a/storage/pruning/fullHistoryPruningStorer.go +++ b/storage/pruning/fullHistoryPruningStorer.go @@ -184,6 +184,7 @@ func (fhps *FullHistoryPruningStorer) getOrOpenPersister(epoch uint32) (storage. } fhps.oldEpochsActivePersistersCache.Put([]byte(epochString), newPdata, 0) + log.Trace("full history pruning storer - init new storer", "epoch", epoch) fhps.persistersMapByEpoch[epoch] = newPdata return newPdata.getPersister(), nil diff --git a/storage/pruning/fullHistoryPruningStorer_test.go b/storage/pruning/fullHistoryPruningStorer_test.go index 0e0d43877e8..d1274499bb9 100644 --- a/storage/pruning/fullHistoryPruningStorer_test.go +++ b/storage/pruning/fullHistoryPruningStorer_test.go @@ -18,6 +18,7 @@ import ( "github.com/multiversx/mx-chain-go/storage/factory" "github.com/multiversx/mx-chain-go/storage/pathmanager" "github.com/multiversx/mx-chain-go/storage/pruning" + "github.com/multiversx/mx-chain-go/testscommon" logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -399,3 +400,33 @@ func TestFullHistoryPruningStorer_IsInterfaceNil(t *testing.T) { fhps, _ = pruning.NewFullHistoryPruningStorer(fhArgs) require.False(t, fhps.IsInterfaceNil()) } + +func TestFullHistoryPruningStorer_changeEpochClosesOldDbs(t *testing.T) { + t.Parallel() + + shouldCleanCalled := false + args := getDefaultArgs() + fhArgs := pruning.FullHistoryStorerArgs{ + StorerArgs: args, + NumOfOldActivePersisters: 2, + } + fhArgs.OldDataCleanerProvider = &testscommon.OldDataCleanerProviderStub{ + ShouldCleanCalled: func() bool { + shouldCleanCalled = true + return true + }, + } + fhps, err := pruning.NewFullHistoryPruningStorer(fhArgs) + require.Nil(t, err) + + numEpochsChanged := 10 + startEpoch := uint32(0) + for i := 0; i < numEpochsChanged; i++ { + startEpoch++ + key := []byte(fmt.Sprintf("key-%d", i)) + _, _ = fhps.GetFromEpoch(key, startEpoch) + err = fhps.ChangeEpochSimple(startEpoch) + require.Nil(t, err) + } + require.True(t, shouldCleanCalled) +} diff --git a/storage/pruning/pruningStorer.go b/storage/pruning/pruningStorer.go index 2007454a7c8..d40680e5c87 100644 --- a/storage/pruning/pruningStorer.go +++ b/storage/pruning/pruningStorer.go @@ -779,7 +779,7 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { } log.Debug("change epoch pruning storer success", "persister", ps.identifier, "epoch", epoch) - return nil + return ps.removeOldPersistersIfNeeded(header) } shardID := core.GetShardIDString(ps.shardCoordinator.SelfId()) @@ -802,6 +802,11 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { ps.activePersisters = append(singleItemPersisters, ps.activePersisters...) ps.persistersMapByEpoch[epoch] = newPersister + return ps.removeOldPersistersIfNeeded(header) +} + +func (ps *PruningStorer) removeOldPersistersIfNeeded(header data.HeaderHandler) error { + epoch := header.GetEpoch() wasExtended := ps.extendSavedEpochsIfNeeded(header) if wasExtended { if len(ps.activePersisters) > int(ps.numOfActivePersisters) { @@ -814,11 +819,12 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { return nil } - err = ps.closeAndDestroyPersisters(epoch) + err := ps.closeAndDestroyPersisters(epoch) if err != nil { log.Warn("closing persisters", "error", err.Error()) return err } + return nil } diff --git a/storage/txcache/txcache.go b/storage/txcache/txcache.go index 218a3749805..01e9212ea15 100644 --- a/storage/txcache/txcache.go +++ b/storage/txcache/txcache.go @@ -2,13 +2,20 @@ package txcache import ( "github.com/multiversx/mx-chain-storage-go/txcache" + "github.com/multiversx/mx-chain-storage-go/types" ) // WrappedTransaction contains a transaction, its hash and extra information type WrappedTransaction = txcache.WrappedTransaction -// TxGasHandler handles a transaction gas and gas cost -type TxGasHandler = txcache.TxGasHandler +// AccountState represents the state of an account (as seen by the mempool) +type AccountState = types.AccountState + +// MempoolHost provides blockchain information for mempool operations +type MempoolHost = txcache.MempoolHost + +// SelectionSession provides blockchain information for transaction selection +type SelectionSession = txcache.SelectionSession // ForEachTransaction is an iterator callback type ForEachTransaction = txcache.ForEachTransaction @@ -29,8 +36,8 @@ type DisabledCache = txcache.DisabledCache type CrossTxCache = txcache.CrossTxCache // NewTxCache creates a new transaction cache -func NewTxCache(config ConfigSourceMe, txGasHandler TxGasHandler) (*TxCache, error) { - return txcache.NewTxCache(config, txGasHandler) +func NewTxCache(config ConfigSourceMe, host MempoolHost) (*TxCache, error) { + return txcache.NewTxCache(config, host) } // NewDisabledCache creates a new disabled cache diff --git a/storage/txcache/txcache_test.go b/storage/txcache/txcache_test.go index cd0ded4f133..5b84daba2d5 100644 --- a/storage/txcache/txcache_test.go +++ b/storage/txcache/txcache_test.go @@ -16,37 +16,33 @@ func TestNewTxCache(t *testing.T) { t.Parallel() cfg := ConfigSourceMe{ - Name: "test", - NumChunks: 1, - NumBytesThreshold: 1000, - NumBytesPerSenderThreshold: 100, - CountThreshold: 10, - CountPerSenderThreshold: 100, - NumSendersToPreemptivelyEvict: 1, + Name: "test", + NumChunks: 1, + NumBytesThreshold: 1000, + NumBytesPerSenderThreshold: 100, + CountThreshold: 10, + CountPerSenderThreshold: 100, + NumItemsToPreemptivelyEvict: 1, } cache, err := NewTxCache(cfg, nil) assert.Nil(t, cache) - assert.Equal(t, common.ErrNilTxGasHandler, err) + assert.ErrorContains(t, err, "nil mempool host") }) t.Run("should work", func(t *testing.T) { t.Parallel() cfg := ConfigSourceMe{ - Name: "test", - NumChunks: 1, - NumBytesThreshold: 1000, - NumBytesPerSenderThreshold: 100, - CountThreshold: 10, - CountPerSenderThreshold: 100, - NumSendersToPreemptivelyEvict: 1, + Name: "test", + NumChunks: 1, + NumBytesThreshold: 1000, + NumBytesPerSenderThreshold: 100, + CountThreshold: 10, + CountPerSenderThreshold: 100, + NumItemsToPreemptivelyEvict: 1, } - cache, err := NewTxCache(cfg, &txcachemocks.TxGasHandlerMock{ - GasProcessingDivisor: 1, - MinimumGasPrice: 1, - MinimumGasMove: 1, - }) + cache, err := NewTxCache(cfg, txcachemocks.NewMempoolHostMock()) assert.NotNil(t, cache) assert.Nil(t, err) }) diff --git a/epochStart/mock/argumentsParserMock.go b/testscommon/argumentsParserMock.go similarity index 98% rename from epochStart/mock/argumentsParserMock.go rename to testscommon/argumentsParserMock.go index 02ce8f408ae..b23b66b682b 100644 --- a/epochStart/mock/argumentsParserMock.go +++ b/testscommon/argumentsParserMock.go @@ -1,4 +1,4 @@ -package mock +package testscommon import ( vmcommon "github.com/multiversx/mx-chain-vm-common-go" diff --git a/testscommon/components/components.go b/testscommon/components/components.go index 6e630b9050d..7f0f72992ca 100644 --- a/testscommon/components/components.go +++ b/testscommon/components/components.go @@ -456,10 +456,25 @@ func GetProcessArgs( ) bootstrapComponentsFactoryArgs := GetBootStrapFactoryArgs() - bootstrapComponentsFactory, _ := bootstrapComp.NewBootstrapComponentsFactory(bootstrapComponentsFactoryArgs) - bootstrapComponents, _ := bootstrapComp.NewTestManagedBootstrapComponents(bootstrapComponentsFactory) - _ = bootstrapComponents.Create() - _ = bootstrapComponents.SetShardCoordinator(shardCoordinator) + bootstrapComponentsFactory, err := bootstrapComp.NewBootstrapComponentsFactory(bootstrapComponentsFactoryArgs) + if err != nil { + panic(err) + } + + bootstrapComponents, err := bootstrapComp.NewTestManagedBootstrapComponents(bootstrapComponentsFactory) + if err != nil { + panic(err) + } + + err = bootstrapComponents.Create() + if err != nil { + panic(err) + } + + err = bootstrapComponents.SetShardCoordinator(shardCoordinator) + if err != nil { + panic(err) + } return processComp.ProcessComponentsFactoryArgs{ Config: testscommon.GetGeneralConfig(), @@ -704,7 +719,7 @@ func GetStatusComponentsFactoryArgsAndProcessComponents(shardCoordinator shardin { MarshallerType: "json", Mode: "client", - URL: "localhost:12345", + URL: "ws://localhost:12345", RetryDurationInSec: 1, }, }, @@ -739,10 +754,19 @@ func GetDataComponents(coreComponents factory.CoreComponentsHolder, shardCoordin dataArgs := GetDataArgs(coreComponents, shardCoordinator) dataComponentsFactory, err := dataComp.NewDataComponentsFactory(dataArgs) if err != nil { - fmt.Println(err.Error()) + panic(err) + } + + dataComponents, err := dataComp.NewManagedDataComponents(dataComponentsFactory) + if err != nil { + panic(err) } - dataComponents, _ := dataComp.NewManagedDataComponents(dataComponentsFactory) - _ = dataComponents.Create() + + err = dataComponents.Create() + if err != nil { + panic(err) + } + return dataComponents } diff --git a/testscommon/dataRetriever/poolFactory.go b/testscommon/dataRetriever/poolFactory.go index fae9b2bd5c6..5f151a2f73b 100644 --- a/testscommon/dataRetriever/poolFactory.go +++ b/testscommon/dataRetriever/poolFactory.go @@ -42,11 +42,8 @@ func CreateTxPool(numShards uint32, selfShard uint32) (dataRetriever.ShardedData }, NumberOfShards: numShards, SelfShardID: selfShard, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, }, ) } diff --git a/testscommon/dataRetriever/poolsHolderMock.go b/testscommon/dataRetriever/poolsHolderMock.go index 078a19f0893..0911bf44f93 100644 --- a/testscommon/dataRetriever/poolsHolderMock.go +++ b/testscommon/dataRetriever/poolsHolderMock.go @@ -4,6 +4,7 @@ import ( "time" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -51,11 +52,8 @@ func NewPoolsHolderMock() *PoolsHolderMock { SizeInBytesPerSender: 10000000, Shards: 16, }, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 1, }, ) diff --git a/testscommon/economicsmocks/economicsDataHandlerStub.go b/testscommon/economicsmocks/economicsDataHandlerStub.go index b6cf36f4491..a69206b12e6 100644 --- a/testscommon/economicsmocks/economicsDataHandlerStub.go +++ b/testscommon/economicsmocks/economicsDataHandlerStub.go @@ -46,6 +46,17 @@ type EconomicsHandlerStub struct { ComputeGasLimitInEpochCalled func(tx data.TransactionWithFeeHandler, epoch uint32) uint64 ComputeGasUsedAndFeeBasedOnRefundValueInEpochCalled func(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsedInEpochCalled func(tx data.TransactionWithFeeHandler, gasUsed uint64, epoch uint32) *big.Int + ComputeMoveBalanceFeeInEpochCalled func(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int + ComputeGasUnitsFromRefundValueCalled func(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 +} + +// ComputeGasUnitsFromRefundValue - +func (e *EconomicsHandlerStub) ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 { + if e.ComputeGasUnitsFromRefundValueCalled != nil { + return e.ComputeGasUnitsFromRefundValueCalled(tx, refundValue, epoch) + } + + return 0 } // ComputeFeeForProcessing - @@ -225,6 +236,14 @@ func (e *EconomicsHandlerStub) ComputeMoveBalanceFee(tx data.TransactionWithFeeH return big.NewInt(0) } +// ComputeMoveBalanceFeeInEpoch - +func (e *EconomicsHandlerStub) ComputeMoveBalanceFeeInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int { + if e.ComputeMoveBalanceFeeInEpochCalled != nil { + return e.ComputeMoveBalanceFeeInEpochCalled(tx, epoch) + } + return big.NewInt(0) +} + // ComputeTxFee - func (e *EconomicsHandlerStub) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { if e.ComputeTxFeeCalled != nil { diff --git a/testscommon/economicsmocks/economicsHandlerMock.go b/testscommon/economicsmocks/economicsHandlerMock.go index 88a54c90e72..304e86e37d2 100644 --- a/testscommon/economicsmocks/economicsHandlerMock.go +++ b/testscommon/economicsmocks/economicsHandlerMock.go @@ -26,6 +26,7 @@ type EconomicsHandlerMock struct { ComputeFeeCalled func(tx data.TransactionWithFeeHandler) *big.Int CheckValidityTxValuesCalled func(tx data.TransactionWithFeeHandler) error ComputeMoveBalanceFeeCalled func(tx data.TransactionWithFeeHandler) *big.Int + ComputeMoveBalanceFeeInEpochCalled func(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int ComputeTxFeeCalled func(tx data.TransactionWithFeeHandler) *big.Int DeveloperPercentageCalled func() float64 MinGasPriceCalled func() uint64 @@ -48,6 +49,11 @@ type EconomicsHandlerMock struct { ComputeTxFeeBasedOnGasUsedInEpochCalled func(tx data.TransactionWithFeeHandler, gasUsed uint64, epoch uint32) *big.Int } +// ComputeGasUnitsFromRefundValue - +func (ehm *EconomicsHandlerMock) ComputeGasUnitsFromRefundValue(_ data.TransactionWithFeeHandler, _ *big.Int, _ uint32) uint64 { + return 0 +} + // LeaderPercentage - func (ehm *EconomicsHandlerMock) LeaderPercentage() float64 { return ehm.LeaderPercentageCalled() @@ -196,7 +202,14 @@ func (ehm *EconomicsHandlerMock) ComputeMoveBalanceFee(tx data.TransactionWithFe return ehm.ComputeMoveBalanceFeeCalled(tx) } return big.NewInt(0) +} +// ComputeMoveBalanceFeeInEpoch - +func (ehm *EconomicsHandlerMock) ComputeMoveBalanceFeeInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int { + if ehm.ComputeMoveBalanceFeeInEpochCalled != nil { + return ehm.ComputeMoveBalanceFeeInEpochCalled(tx, epoch) + } + return big.NewInt(0) } // ComputeGasLimitBasedOnBalance - diff --git a/testscommon/esdtStorageHandlerStub.go b/testscommon/esdtStorageHandlerStub.go index 47825717409..599197e7fca 100644 --- a/testscommon/esdtStorageHandlerStub.go +++ b/testscommon/esdtStorageHandlerStub.go @@ -10,7 +10,7 @@ import ( // EsdtStorageHandlerStub - type EsdtStorageHandlerStub struct { - SaveESDTNFTTokenCalled func(senderAddress []byte, acnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64, esdtData *esdt.ESDigitalToken, isCreation bool, isReturnWithError bool) ([]byte, error) + SaveESDTNFTTokenCalled func(senderAddress []byte, acnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64, esdtData *esdt.ESDigitalToken, saveArgs vmcommon.NftSaveArgs) ([]byte, error) GetESDTNFTTokenOnSenderCalled func(acnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64) (*esdt.ESDigitalToken, error) GetESDTNFTTokenOnDestinationCalled func(acnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64) (*esdt.ESDigitalToken, bool, error) GetESDTNFTTokenOnDestinationWithCustomSystemAccountCalled func(accnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64, systemAccount vmcommon.UserAccountHandler) (*esdt.ESDigitalToken, bool, error) @@ -18,7 +18,7 @@ type EsdtStorageHandlerStub struct { SaveNFTMetaDataCalled func(tx data.TransactionHandler) error AddToLiquiditySystemAccCalled func(esdtTokenKey []byte, tokenType uint32, nonce uint64, transferValue *big.Int, keepMetadataOnZeroLiquidity bool) error SaveMetaDataToSystemAccountCalled func(tokenKey []byte, nonce uint64, esdtData *esdt.ESDigitalToken) error - GetMetaDataFromSystemAccountCalled func(bytes []byte, u uint64) (*esdt.MetaData, error) + GetMetaDataFromSystemAccountCalled func(bytes []byte, u uint64) (*esdt.ESDigitalToken, error) } // SaveMetaDataToSystemAccount - @@ -31,7 +31,7 @@ func (e *EsdtStorageHandlerStub) SaveMetaDataToSystemAccount(tokenKey []byte, no } // GetMetaDataFromSystemAccount - -func (e *EsdtStorageHandlerStub) GetMetaDataFromSystemAccount(bytes []byte, u uint64) (*esdt.MetaData, error) { +func (e *EsdtStorageHandlerStub) GetMetaDataFromSystemAccount(bytes []byte, u uint64) (*esdt.ESDigitalToken, error) { if e.GetMetaDataFromSystemAccountCalled != nil { return e.GetMetaDataFromSystemAccountCalled(bytes, u) } @@ -40,9 +40,9 @@ func (e *EsdtStorageHandlerStub) GetMetaDataFromSystemAccount(bytes []byte, u ui } // SaveESDTNFTToken - -func (e *EsdtStorageHandlerStub) SaveESDTNFTToken(senderAddress []byte, acnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64, esdtData *esdt.ESDigitalToken, isCreation bool, isReturnWithError bool) ([]byte, error) { +func (e *EsdtStorageHandlerStub) SaveESDTNFTToken(senderAddress []byte, acnt vmcommon.UserAccountHandler, esdtTokenKey []byte, nonce uint64, esdtData *esdt.ESDigitalToken, saveArgs vmcommon.NftSaveArgs) ([]byte, error) { if e.SaveESDTNFTTokenCalled != nil { - return e.SaveESDTNFTTokenCalled(senderAddress, acnt, esdtTokenKey, nonce, esdtData, isCreation, isReturnWithError) + return e.SaveESDTNFTTokenCalled(senderAddress, acnt, esdtTokenKey, nonce, esdtData, saveArgs) } return nil, nil diff --git a/testscommon/feeComputerStub.go b/testscommon/feeComputerStub.go index 33dcfbb4e4b..884351576d9 100644 --- a/testscommon/feeComputerStub.go +++ b/testscommon/feeComputerStub.go @@ -12,6 +12,7 @@ type FeeComputerStub struct { ComputeGasUsedAndFeeBasedOnRefundValueCalled func(tx *transaction.ApiTransactionResult, refundValue *big.Int) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsedCalled func(tx *transaction.ApiTransactionResult, gasUsed uint64) *big.Int ComputeGasLimitCalled func(tx *transaction.ApiTransactionResult) uint64 + ComputeMoveBalanceFeeCalled func(tx *transaction.ApiTransactionResult) *big.Int } // ComputeTransactionFee - @@ -49,6 +50,15 @@ func (stub *FeeComputerStub) ComputeGasLimit(tx *transaction.ApiTransactionResul return 0 } +// ComputeMoveBalanceFee - +func (stub *FeeComputerStub) ComputeMoveBalanceFee(tx *transaction.ApiTransactionResult) *big.Int { + if stub.ComputeMoveBalanceFeeCalled != nil { + return stub.ComputeMoveBalanceFeeCalled(tx) + } + + return big.NewInt(0) +} + // IsInterfaceNil returns true if there is no value under the interface func (stub *FeeComputerStub) IsInterfaceNil() bool { return false diff --git a/testscommon/scProcessorMock.go b/testscommon/scProcessorMock.go index 95e515b2950..ec96eb4c732 100644 --- a/testscommon/scProcessorMock.go +++ b/testscommon/scProcessorMock.go @@ -10,7 +10,7 @@ import ( // SCProcessorMock - type SCProcessorMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) ExecuteSmartContractTransactionCalled func(tx data.TransactionHandler, acntSrc, acntDst state.UserAccountHandler) (vmcommon.ReturnCode, error) ExecuteBuiltInFunctionCalled func(tx data.TransactionHandler, acntSrc, acntDst state.UserAccountHandler) (vmcommon.ReturnCode, error) DeploySmartContractCalled func(tx data.TransactionHandler, acntSrc state.UserAccountHandler) (vmcommon.ReturnCode, error) @@ -45,9 +45,9 @@ func (sc *SCProcessorMock) ProcessIfError( } // ComputeTransactionType - -func (sc *SCProcessorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (sc *SCProcessorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if sc.ComputeTransactionTypeCalled == nil { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false } return sc.ComputeTransactionTypeCalled(tx) diff --git a/testscommon/state/accountsAdapterStub.go b/testscommon/state/accountsAdapterStub.go index 034d1f6ae26..70be0b5cc95 100644 --- a/testscommon/state/accountsAdapterStub.go +++ b/testscommon/state/accountsAdapterStub.go @@ -11,6 +11,8 @@ import ( var errNotImplemented = errors.New("not implemented") +var _ state.AccountsAdapter = (*AccountsStub)(nil) + // AccountsStub - type AccountsStub struct { GetExistingAccountCalled func(addressContainer []byte) (vmcommon.AccountHandler, error) diff --git a/testscommon/state/userAccountStub.go b/testscommon/state/userAccountStub.go index ce54f059252..02192ccd8ee 100644 --- a/testscommon/state/userAccountStub.go +++ b/testscommon/state/userAccountStub.go @@ -22,6 +22,7 @@ type UserAccountStub struct { Address []byte CodeMetadata []byte CodeHash []byte + Nonce uint64 AddToBalanceCalled func(value *big.Int) error DataTrieTrackerCalled func() state.DataTrieTracker @@ -106,7 +107,7 @@ func (u *UserAccountStub) IncreaseNonce(_ uint64) { // GetNonce - func (u *UserAccountStub) GetNonce() uint64 { - return 0 + return u.Nonce } // SetCode - diff --git a/testscommon/toml/config.go b/testscommon/toml/config.go index 47a45839be0..7f64ee446d4 100644 --- a/testscommon/toml/config.go +++ b/testscommon/toml/config.go @@ -15,6 +15,8 @@ type Config struct { TestConfigStruct TestConfigNestedStruct TestMap + TestInterface + TestArray } // TestConfigI8 will hold an int8 value for testing @@ -24,7 +26,7 @@ type TestConfigI8 struct { // Int8 will hold the value type Int8 struct { - Value int8 + Number int8 } // TestConfigI16 will hold an int16 value for testing @@ -34,7 +36,7 @@ type TestConfigI16 struct { // Int16 will hold the value type Int16 struct { - Value int16 + Number int16 } // TestConfigI32 will hold an int32 value for testing @@ -44,7 +46,7 @@ type TestConfigI32 struct { // Int32 will hold the value type Int32 struct { - Value int32 + Number int32 } // TestConfigI64 will hold an int64 value for testing @@ -54,7 +56,7 @@ type TestConfigI64 struct { // Int64 will hold the value type Int64 struct { - Value int64 + Number int64 } // TestConfigU8 will hold an uint8 value for testing @@ -64,7 +66,7 @@ type TestConfigU8 struct { // Uint8 will hold the value type Uint8 struct { - Value uint8 + Number uint8 } // TestConfigU16 will hold an uint16 value for testing @@ -74,7 +76,7 @@ type TestConfigU16 struct { // Uint16 will hold the value type Uint16 struct { - Value uint16 + Number uint16 } // TestConfigU32 will hold an uint32 value for testing @@ -84,7 +86,7 @@ type TestConfigU32 struct { // Uint32 will hold the value type Uint32 struct { - Value uint32 + Number uint32 } // TestConfigU64 will hold an uint64 value for testing @@ -94,7 +96,7 @@ type TestConfigU64 struct { // Uint64 will hold the value type Uint64 struct { - Value uint64 + Number uint64 } // TestConfigF32 will hold a float32 value for testing @@ -104,7 +106,7 @@ type TestConfigF32 struct { // Float32 will hold the value type Float32 struct { - Value float32 + Number float32 } // TestConfigF64 will hold a float64 value for testing @@ -114,7 +116,7 @@ type TestConfigF64 struct { // Float64 will hold the value type Float64 struct { - Value float64 + Number float64 } // TestConfigStruct will hold a configuration struct for testing @@ -167,5 +169,21 @@ type MessageDescriptionOtherName struct { // TestMap will hold a map for testing type TestMap struct { - Value map[string]int + Map map[string]MapValues +} + +// MapValues will hold a value for map +type MapValues struct { + Number int +} + +// TestInterface will hold an interface for testing +type TestInterface struct { + Value interface{} +} + +// TestArray will hold an array of strings and integers +type TestArray struct { + Strings []string + Ints []int } diff --git a/testscommon/toml/config.toml b/testscommon/toml/config.toml index af54141fe5f..890e3922789 100644 --- a/testscommon/toml/config.toml +++ b/testscommon/toml/config.toml @@ -48,5 +48,10 @@ Text = "Config Nested Struct" Mesage = { Public = true, MessageDescription = [{ Text = "Text1" }, { Text = "Text2"}] } -[TestMap] - Value = { "key" = 0 } +[Map] + [Map.Key1] + Number = 999 + +[TestArray] + Strings = ["a", "b", "c"] + Ints = [0, 1, 2] diff --git a/testscommon/toml/overwrite.toml b/testscommon/toml/overwrite.toml index 5d1e6690caf..5e495e5c08b 100644 --- a/testscommon/toml/overwrite.toml +++ b/testscommon/toml/overwrite.toml @@ -1,38 +1,43 @@ OverridableConfigTomlValues = [ - { File = "config.toml", Path = "TestConfigI8.Int8", Value = 127 }, - { File = "config.toml", Path = "TestConfigI8.Int8", Value = 128 }, - { File = "config.toml", Path = "TestConfigI8.Int8", Value = -128 }, - { File = "config.toml", Path = "TestConfigI8.Int8", Value = -129 }, - { File = "config.toml", Path = "TestConfigI16.Int16", Value = 32767 }, - { File = "config.toml", Path = "TestConfigI16.Int16", Value = 32768 }, - { File = "config.toml", Path = "TestConfigI16.Int16", Value = -32768 }, - { File = "config.toml", Path = "TestConfigI16.Int16", Value = -32769 }, - { File = "config.toml", Path = "TestConfigI32.Int32", Value = 2147483647 }, - { File = "config.toml", Path = "TestConfigI32.Int32", Value = 2147483648 }, - { File = "config.toml", Path = "TestConfigI32.Int32", Value = -2147483648 }, - { File = "config.toml", Path = "TestConfigI32.Int32", Value = -2147483649 }, - { File = "config.toml", Path = "TestConfigI32.Int64", Value = 9223372036854775807 }, - { File = "config.toml", Path = "TestConfigI32.Int64", Value = -9223372036854775808 }, - { File = "config.toml", Path = "TestConfigU8.Uint8", Value = 255 }, - { File = "config.toml", Path = "TestConfigU8.Uint8", Value = 256 }, - { File = "config.toml", Path = "TestConfigU8.Uint8", Value = -256 }, - { File = "config.toml", Path = "TestConfigU16.Uint16", Value = 65535 }, - { File = "config.toml", Path = "TestConfigU16.Uint16", Value = 65536 }, - { File = "config.toml", Path = "TestConfigU16.Uint16", Value = -65536 }, - { File = "config.toml", Path = "TestConfigU32.Uint32", Value = 4294967295 }, - { File = "config.toml", Path = "TestConfigU32.Uint32", Value = 4294967296 }, - { File = "config.toml", Path = "TestConfigU32.Uint32", Value = -4294967296 }, - { File = "config.toml", Path = "TestConfigU64.Uint64", Value = 9223372036854775807 }, - { File = "config.toml", Path = "TestConfigU64.Uint64", Value = -9223372036854775808 }, - { File = "config.toml", Path = "TestConfigF32.Float32", Value = 3.4 }, - { File = "config.toml", Path = "TestConfigF32.Float32", Value = 3.4e+39 }, - { File = "config.toml", Path = "TestConfigF32.Float32", Value = -3.4 }, - { File = "config.toml", Path = "TestConfigF32.Float32", Value = -3.4e+40 }, - { File = "config.toml", Path = "TestConfigF64.Float64", Value = 1.7e+308 }, - { File = "config.toml", Path = "TestConfigF64.Float64", Value = -1.7e+308 }, - { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = 11 } }, - { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Nr = 222 } }, - { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = "11" } }, - { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct", Value = { Text = "Overwritten text", Message = { Public = false, MessageDescription = [{ Text = "Overwritten Text1" }] } } }, - { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription", Value = [{ Text = "Overwritten Text1" }, { Text = "Overwritten Text2" }] }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = 127 }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = 128 }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = -128 }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = -129 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = 32767 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = 32768 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = -32768 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = -32769 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = 2147483647 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = 2147483648 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = -2147483648 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = -2147483649 }, + { File = "config.toml", Path = "TestConfigI64.Int64.Number", Value = 9223372036854775807 }, + { File = "config.toml", Path = "TestConfigI64.Int64.Number", Value = -9223372036854775808 }, + { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = 255 }, + { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = 256 }, + { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = -256 }, + { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = 65535 }, + { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = 65536 }, + { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = -65536 }, + { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = 4294967295 }, + { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = 4294967296 }, + { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = -4294967296 }, + { File = "config.toml", Path = "TestConfigU64.Uint64.Number", Value = 9223372036854775807 }, + { File = "config.toml", Path = "TestConfigU64.Uint64.Number", Value = -9223372036854775808 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = 3.4 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = 3.4e+39 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = -3.4 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = -3.4e+40 }, + { File = "config.toml", Path = "TestConfigF64.Float64.Number", Value = 1.7e+308 }, + { File = "config.toml", Path = "TestConfigF64.Float64.Number", Value = -1.7e+308 }, + { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = 11 } }, + { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Nr = 222 } }, + { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = "11" } }, + { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct", Value = { Text = "Overwritten text", Message = { Public = false, MessageDescription = [{ Text = "Overwritten Text1" }] } } }, + { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription", Value = [{ Text = "Overwritten Text1" }, { Text = "Overwritten Text2" }] }, + { File = "config.toml", Path = "TestMap.Map", Value = { "Key1" = { Number = 10 }, "Key2" = { Number = 11 } } }, + { File = "config.toml", Path = "TestMap.Map", Value = { "Key2" = { Number = 2 }, "Key3" = { Number = 3 } } }, + { File = "config.toml", Path = "TestArray.Strings", Value = ["x", "y", "z"] }, + { File = "config.toml", Path = "TestArray.Ints", Value = [10, 20, 30] }, + { File = "config.toml", Path = "TestArray", Value = { Strings = ["x", "y", "z"], Ints = [10, 20, 30] } }, ] diff --git a/testscommon/transactionCoordinatorMock.go b/testscommon/transactionCoordinatorMock.go index a1889b0b753..4aeac14dcff 100644 --- a/testscommon/transactionCoordinatorMock.go +++ b/testscommon/transactionCoordinatorMock.go @@ -12,7 +12,7 @@ import ( // TransactionCoordinatorMock - type TransactionCoordinatorMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) RequestMiniBlocksAndTransactionsCalled func(header data.HeaderHandler) RequestBlockTransactionsCalled func(body *block.Body) IsDataPreparedForProcessingCalled func(haveTime func() time.Duration) error @@ -57,9 +57,9 @@ func (tcm *TransactionCoordinatorMock) CreateReceiptsHash() ([]byte, error) { } // ComputeTransactionType - -func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if tcm.ComputeTransactionTypeCalled == nil { - return 0, 0 + return 0, 0, false } return tcm.ComputeTransactionTypeCalled(tx) diff --git a/testscommon/txProcessorMock.go b/testscommon/txProcessorMock.go index c8c47dbf89e..7b39ef87d13 100644 --- a/testscommon/txProcessorMock.go +++ b/testscommon/txProcessorMock.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -12,6 +13,7 @@ import ( type TxProcessorMock struct { ProcessTransactionCalled func(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) VerifyTransactionCalled func(tx *transaction.Transaction) error + VerifyGuardianCalled func(tx *transaction.Transaction, account state.UserAccountHandler) error SetBalancesToTrieCalled func(accBalance map[string]*big.Int) (rootHash []byte, err error) ProcessSmartContractResultCalled func(scr *smartContractResult.SmartContractResult) (vmcommon.ReturnCode, error) } @@ -34,6 +36,15 @@ func (etm *TxProcessorMock) VerifyTransaction(tx *transaction.Transaction) error return nil } +// VerifyGuardian - +func (etm *TxProcessorMock) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { + if etm.VerifyGuardianCalled != nil { + return etm.VerifyGuardianCalled(tx, account) + } + + return nil +} + // SetBalancesToTrie - func (etm *TxProcessorMock) SetBalancesToTrie(accBalance map[string]*big.Int) (rootHash []byte, err error) { if etm.SetBalancesToTrieCalled != nil { diff --git a/testscommon/txProcessorStub.go b/testscommon/txProcessorStub.go index 1070fd21d74..7407800ef6f 100644 --- a/testscommon/txProcessorStub.go +++ b/testscommon/txProcessorStub.go @@ -2,6 +2,7 @@ package testscommon import ( "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -9,6 +10,7 @@ import ( type TxProcessorStub struct { ProcessTransactionCalled func(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) VerifyTransactionCalled func(tx *transaction.Transaction) error + VerifyGuardianCalled func(tx *transaction.Transaction, account state.UserAccountHandler) error } // ProcessTransaction - @@ -29,6 +31,15 @@ func (tps *TxProcessorStub) VerifyTransaction(tx *transaction.Transaction) error return nil } +// VerifyGuardian - +func (tps *TxProcessorStub) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { + if tps.VerifyGuardianCalled != nil { + return tps.VerifyGuardianCalled(tx, account) + } + + return nil +} + // IsInterfaceNil - func (tps *TxProcessorStub) IsInterfaceNil() bool { return tps == nil diff --git a/testscommon/txTypeHandlerMock.go b/testscommon/txTypeHandlerMock.go index 18a5a136477..bd08cc01c20 100644 --- a/testscommon/txTypeHandlerMock.go +++ b/testscommon/txTypeHandlerMock.go @@ -7,13 +7,13 @@ import ( // TxTypeHandlerMock - type TxTypeHandlerMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) } // ComputeTransactionType - -func (th *TxTypeHandlerMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (th *TxTypeHandlerMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if th.ComputeTransactionTypeCalled == nil { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false } return th.ComputeTransactionTypeCalled(tx) diff --git a/testscommon/txcachemocks/mempoolHostMock.go b/testscommon/txcachemocks/mempoolHostMock.go new file mode 100644 index 00000000000..3e9ecc995ec --- /dev/null +++ b/testscommon/txcachemocks/mempoolHostMock.go @@ -0,0 +1,41 @@ +package txcachemocks + +import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/data" +) + +// MempoolHostMock - +type MempoolHostMock struct { + ComputeTxFeeCalled func(tx data.TransactionWithFeeHandler) *big.Int + GetTransferredValueCalled func(tx data.TransactionHandler) *big.Int +} + +// NewMempoolHostMock - +func NewMempoolHostMock() *MempoolHostMock { + return &MempoolHostMock{} +} + +// ComputeTxFee - +func (mock *MempoolHostMock) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { + if mock.ComputeTxFeeCalled != nil { + return mock.ComputeTxFeeCalled(tx) + } + + return big.NewInt(0) +} + +// GetTransferredValue - +func (mock *MempoolHostMock) GetTransferredValue(tx data.TransactionHandler) *big.Int { + if mock.GetTransferredValueCalled != nil { + return mock.GetTransferredValueCalled(tx) + } + + return tx.GetValue() +} + +// IsInterfaceNil - +func (mock *MempoolHostMock) IsInterfaceNil() bool { + return mock == nil +} diff --git a/testscommon/txcachemocks/txCacheMock.go b/testscommon/txcachemocks/txCacheMock.go new file mode 100644 index 00000000000..c34db2d53b0 --- /dev/null +++ b/testscommon/txcachemocks/txCacheMock.go @@ -0,0 +1,220 @@ +package txcachemocks + +import "github.com/multiversx/mx-chain-storage-go/txcache" + +// TxCacheMock - +type TxCacheMock struct { + ClearCalled func() + PutCalled func(key []byte, value interface{}, sizeInBytes int) (evicted bool) + GetCalled func(key []byte) (value interface{}, ok bool) + HasCalled func(key []byte) bool + PeekCalled func(key []byte) (value interface{}, ok bool) + HasOrAddCalled func(key []byte, value interface{}, sizeInBytes int) (has, added bool) + RemoveCalled func(key []byte) + RemoveOldestCalled func() + KeysCalled func() [][]byte + LenCalled func() int + MaxSizeCalled func() int + RegisterHandlerCalled func(func(key []byte, value interface{})) + UnRegisterHandlerCalled func(id string) + CloseCalled func() error + + AddTxCalled func(tx *txcache.WrappedTransaction) (ok bool, added bool) + GetByTxHashCalled func(txHash []byte) (*txcache.WrappedTransaction, bool) + RemoveTxByHashCalled func(txHash []byte) bool + ImmunizeTxsAgainstEvictionCalled func(keys [][]byte) + ForEachTransactionCalled func(txcache.ForEachTransaction) + NumBytesCalled func() int + DiagnoseCalled func(deep bool) + GetTransactionsPoolForSenderCalled func(sender string) []*txcache.WrappedTransaction +} + +// NewTxCacheStub - +func NewTxCacheStub() *TxCacheMock { + return &TxCacheMock{} +} + +// Clear - +func (cache *TxCacheMock) Clear() { + if cache.ClearCalled != nil { + cache.ClearCalled() + } +} + +// Put - +func (cache *TxCacheMock) Put(key []byte, value interface{}, sizeInBytes int) (evicted bool) { + if cache.PutCalled != nil { + return cache.PutCalled(key, value, sizeInBytes) + } + + return false +} + +// Get - +func (cache *TxCacheMock) Get(key []byte) (value interface{}, ok bool) { + if cache.GetCalled != nil { + return cache.GetCalled(key) + } + + return nil, false +} + +// Has - +func (cache *TxCacheMock) Has(key []byte) bool { + if cache.HasCalled != nil { + return cache.HasCalled(key) + } + + return false +} + +// Peek - +func (cache *TxCacheMock) Peek(key []byte) (value interface{}, ok bool) { + if cache.PeekCalled != nil { + return cache.PeekCalled(key) + } + + return nil, false +} + +// HasOrAdd - +func (cache *TxCacheMock) HasOrAdd(key []byte, value interface{}, sizeInBytes int) (has, added bool) { + if cache.HasOrAddCalled != nil { + return cache.HasOrAddCalled(key, value, sizeInBytes) + } + + return false, false +} + +// Remove - +func (cache *TxCacheMock) Remove(key []byte) { + if cache.RemoveCalled != nil { + cache.RemoveCalled(key) + } +} + +// Keys - +func (cache *TxCacheMock) Keys() [][]byte { + if cache.KeysCalled != nil { + return cache.KeysCalled() + } + + return make([][]byte, 0) +} + +// Len - +func (cache *TxCacheMock) Len() int { + if cache.LenCalled != nil { + return cache.LenCalled() + } + + return 0 +} + +// SizeInBytesContained - +func (cache *TxCacheMock) SizeInBytesContained() uint64 { + return 0 +} + +// MaxSize - +func (cache *TxCacheMock) MaxSize() int { + if cache.MaxSizeCalled != nil { + return cache.MaxSizeCalled() + } + + return 0 +} + +// RegisterHandler - +func (cache *TxCacheMock) RegisterHandler(handler func(key []byte, value interface{}), _ string) { + if cache.RegisterHandlerCalled != nil { + cache.RegisterHandlerCalled(handler) + } +} + +// UnRegisterHandler - +func (cache *TxCacheMock) UnRegisterHandler(id string) { + if cache.UnRegisterHandlerCalled != nil { + cache.UnRegisterHandlerCalled(id) + } +} + +// Close - +func (cache *TxCacheMock) Close() error { + if cache.CloseCalled != nil { + return cache.CloseCalled() + } + + return nil +} + +// AddTx - +func (cache *TxCacheMock) AddTx(tx *txcache.WrappedTransaction) (ok bool, added bool) { + if cache.AddTxCalled != nil { + return cache.AddTxCalled(tx) + } + + return false, false +} + +// GetByTxHash - +func (cache *TxCacheMock) GetByTxHash(txHash []byte) (*txcache.WrappedTransaction, bool) { + if cache.GetByTxHashCalled != nil { + return cache.GetByTxHashCalled(txHash) + } + + return nil, false +} + +// RemoveTxByHash - +func (cache *TxCacheMock) RemoveTxByHash(txHash []byte) bool { + if cache.RemoveTxByHashCalled != nil { + return cache.RemoveTxByHashCalled(txHash) + } + + return false +} + +// ImmunizeTxsAgainstEviction - +func (cache *TxCacheMock) ImmunizeTxsAgainstEviction(keys [][]byte) { + if cache.ImmunizeTxsAgainstEvictionCalled != nil { + cache.ImmunizeTxsAgainstEvictionCalled(keys) + } +} + +// ForEachTransaction - +func (cache *TxCacheMock) ForEachTransaction(fn txcache.ForEachTransaction) { + if cache.ForEachTransactionCalled != nil { + cache.ForEachTransactionCalled(fn) + } +} + +// NumBytes - +func (cache *TxCacheMock) NumBytes() int { + if cache.NumBytesCalled != nil { + return cache.NumBytesCalled() + } + + return 0 +} + +// Diagnose - +func (cache *TxCacheMock) Diagnose(deep bool) { + if cache.DiagnoseCalled != nil { + cache.DiagnoseCalled(deep) + } +} + +// GetTransactionsPoolForSender - +func (cache *TxCacheMock) GetTransactionsPoolForSender(sender string) []*txcache.WrappedTransaction { + if cache.GetTransactionsPoolForSenderCalled != nil { + return cache.GetTransactionsPoolForSenderCalled(sender) + } + + return nil +} + +// IsInterfaceNil - +func (cache *TxCacheMock) IsInterfaceNil() bool { + return cache == nil +} diff --git a/testscommon/txcachemocks/txGasHandlerMock.go b/testscommon/txcachemocks/txGasHandlerMock.go index f9da5dfad09..a624e29372a 100644 --- a/testscommon/txcachemocks/txGasHandlerMock.go +++ b/testscommon/txcachemocks/txGasHandlerMock.go @@ -1,56 +1,84 @@ package txcachemocks import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" ) // TxGasHandler - type TxGasHandler interface { - SplitTxGasInCategories(tx data.TransactionWithFeeHandler) (uint64, uint64) - GasPriceForProcessing(tx data.TransactionWithFeeHandler) uint64 - GasPriceForMove(tx data.TransactionWithFeeHandler) uint64 MinGasPrice() uint64 - MinGasLimit() uint64 - MinGasPriceForProcessing() uint64 + MaxGasLimitPerTx() uint64 + ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int IsInterfaceNil() bool } // TxGasHandlerMock - type TxGasHandlerMock struct { - MinimumGasMove uint64 - MinimumGasPrice uint64 - GasProcessingDivisor uint64 + minGasLimit uint64 + minGasPrice uint64 + maxGasLimitPerTx uint64 + gasPerDataByte uint64 + gasPriceModifier float64 } -// SplitTxGasInCategories - -func (ghm *TxGasHandlerMock) SplitTxGasInCategories(tx data.TransactionWithFeeHandler) (uint64, uint64) { - moveGas := ghm.MinimumGasMove - return moveGas, tx.GetGasLimit() - moveGas +// NewTxGasHandlerMock - +func NewTxGasHandlerMock() *TxGasHandlerMock { + return &TxGasHandlerMock{ + minGasLimit: 50000, + minGasPrice: 1000000000, + maxGasLimitPerTx: 600000000, + gasPerDataByte: 1500, + gasPriceModifier: 0.01, + } } -// GasPriceForProcessing - -func (ghm *TxGasHandlerMock) GasPriceForProcessing(tx data.TransactionWithFeeHandler) uint64 { - return tx.GetGasPrice() / ghm.GasProcessingDivisor +// WithMinGasLimit - +func (ghm *TxGasHandlerMock) WithMinGasLimit(minGasLimit uint64) *TxGasHandlerMock { + ghm.minGasLimit = minGasLimit + return ghm } -// GasPriceForMove - -func (ghm *TxGasHandlerMock) GasPriceForMove(tx data.TransactionWithFeeHandler) uint64 { - return tx.GetGasPrice() +// WithMinGasPrice - +func (ghm *TxGasHandlerMock) WithMinGasPrice(minGasPrice uint64) *TxGasHandlerMock { + ghm.minGasPrice = minGasPrice + return ghm } // MinGasPrice - func (ghm *TxGasHandlerMock) MinGasPrice() uint64 { - return ghm.MinimumGasPrice + return ghm.minGasPrice +} + +// WithGasPriceModifier - +func (ghm *TxGasHandlerMock) WithGasPriceModifier(gasPriceModifier float64) *TxGasHandlerMock { + ghm.gasPriceModifier = gasPriceModifier + return ghm } -// MinGasLimit - -func (ghm *TxGasHandlerMock) MinGasLimit() uint64 { - return ghm.MinimumGasMove +// MaxGasLimitPerTx - +func (ghm *TxGasHandlerMock) MaxGasLimitPerTx() uint64 { + return ghm.maxGasLimitPerTx } -// MinGasPriceForProcessing - -func (ghm *TxGasHandlerMock) MinGasPriceForProcessing() uint64 { - return ghm.MinimumGasPrice / ghm.GasProcessingDivisor +// ComputeTxFee - +func (ghm *TxGasHandlerMock) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { + dataLength := uint64(len(tx.GetData())) + gasPriceForMovement := tx.GetGasPrice() + gasPriceForProcessing := uint64(float64(gasPriceForMovement) * ghm.gasPriceModifier) + + gasLimitForMovement := ghm.minGasLimit + dataLength*ghm.gasPerDataByte + if tx.GetGasLimit() < gasLimitForMovement { + return big.NewInt(0) + } + + gasLimitForProcessing := tx.GetGasLimit() - gasLimitForMovement + feeForMovement := core.SafeMul(gasPriceForMovement, gasLimitForMovement) + feeForProcessing := core.SafeMul(gasPriceForProcessing, gasLimitForProcessing) + fee := big.NewInt(0).Add(feeForMovement, feeForProcessing) + return fee } // IsInterfaceNil - diff --git a/trie/patriciaMerkleTrie.go b/trie/patriciaMerkleTrie.go index 70df549011f..da9eb87a65f 100644 --- a/trie/patriciaMerkleTrie.go +++ b/trie/patriciaMerkleTrie.go @@ -11,13 +11,14 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + logger "github.com/multiversx/mx-chain-logger-go" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/errors" "github.com/multiversx/mx-chain-go/trie/keyBuilder" "github.com/multiversx/mx-chain-go/trie/statistics" - logger "github.com/multiversx/mx-chain-logger-go" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) var log = logger.GetOrCreate("trie") @@ -118,10 +119,7 @@ func (tr *patriciaMerkleTrie) Update(key, value []byte) error { tr.mutOperation.Lock() defer tr.mutOperation.Unlock() - log.Trace("update trie", - "key", hex.EncodeToString(key), - "val", hex.EncodeToString(value), - ) + log.Trace("update trie", "key", key, "val", value) return tr.update(key, value, core.NotSpecified) } @@ -131,11 +129,7 @@ func (tr *patriciaMerkleTrie) UpdateWithVersion(key []byte, value []byte, versio tr.mutOperation.Lock() defer tr.mutOperation.Unlock() - log.Trace("update trie with version", - "key", hex.EncodeToString(key), - "val", hex.EncodeToString(value), - "version", version, - ) + log.Trace("update trie with version", "key", key, "val", value, "version", version) return tr.update(key, value, version) } diff --git a/trie/patriciaMerkleTrie_test.go b/trie/patriciaMerkleTrie_test.go index 4612b8ee331..8a02a8edcd9 100644 --- a/trie/patriciaMerkleTrie_test.go +++ b/trie/patriciaMerkleTrie_test.go @@ -17,6 +17,10 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/hashing/keccak" "github.com/multiversx/mx-chain-core-go/marshal" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/errChan" "github.com/multiversx/mx-chain-go/common/holders" @@ -28,9 +32,6 @@ import ( "github.com/multiversx/mx-chain-go/trie" "github.com/multiversx/mx-chain-go/trie/keyBuilder" "github.com/multiversx/mx-chain-go/trie/mock" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var emptyTrieHash = make([]byte, 32) @@ -1708,3 +1709,28 @@ func BenchmarkPatriciaMerkleTrie_RootHashAfterChanging30000NodesInBatchesOf200(b } } } + +func BenchmarkPatriciaMerkleTrie_Update(b *testing.B) { + tr := emptyTrie() + hsh := keccak.NewKeccak() + + nrValuesInTrie := 2000000 + values := make([][]byte, nrValuesInTrie) + + for i := 0; i < nrValuesInTrie; i++ { + key := hsh.Compute(strconv.Itoa(i)) + value := append(key, []byte(strconv.Itoa(i))...) + + _ = tr.Update(key, value) + values[i] = key + } + _ = tr.Commit() + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for j := 0; j < nrValuesInTrie; j++ { + _ = tr.Update(values[j], values[j]) + } + } +} diff --git a/vm/systemSmartContracts/delegation.go b/vm/systemSmartContracts/delegation.go index ab5c97cfce0..da23e0c8a15 100644 --- a/vm/systemSmartContracts/delegation.go +++ b/vm/systemSmartContracts/delegation.go @@ -2096,7 +2096,7 @@ func (d *delegation) claimRewards(args *vmcommon.ContractCallInput) vmcommon.Ret } } - d.createAndAddLogEntry(args, unclaimedRewardsBytes, boolToSlice(wasDeleted)) + d.createAndAddLogEntry(args, unclaimedRewardsBytes, boolToSlice(wasDeleted), args.RecipientAddr) return vmcommon.Ok } diff --git a/vm/systemSmartContracts/esdt.go b/vm/systemSmartContracts/esdt.go index 6852dbf04fc..99b29035aef 100644 --- a/vm/systemSmartContracts/esdt.go +++ b/vm/systemSmartContracts/esdt.go @@ -42,6 +42,7 @@ const canTransferNFTCreateRole = "canTransferNFTCreateRole" const upgradable = "canUpgrade" const canCreateMultiShard = "canCreateMultiShard" const upgradeProperties = "upgradeProperties" +const eGLD = "EGLD" const conversionBase = 10 @@ -547,9 +548,21 @@ func (e *esdt) registerAndSetRoles(args *vmcommon.ContractCallInput) vmcommon.Re func (e *esdt) getAllRolesForTokenType(tokenType string) ([][]byte, error) { switch tokenType { case core.NonFungibleESDT, core.NonFungibleESDTv2, core.DynamicNFTESDT: - nftRoles := [][]byte{[]byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleNFTBurn), []byte(core.ESDTRoleNFTUpdateAttributes), []byte(core.ESDTRoleNFTAddURI)} + nftRoles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTBurn), + []byte(core.ESDTRoleNFTUpdateAttributes), + []byte(core.ESDTRoleNFTAddURI), + } + if e.enableEpochsHandler.IsFlagEnabled(common.DynamicESDTFlag) { - nftRoles = append(nftRoles, [][]byte{[]byte(core.ESDTRoleNFTRecreate), []byte(core.ESDTRoleModifyCreator), []byte(core.ESDTRoleModifyRoyalties), []byte(core.ESDTRoleSetNewURI)}...) + nftRoles = append(nftRoles, [][]byte{ + []byte(core.ESDTRoleNFTRecreate), + []byte(core.ESDTRoleModifyCreator), + []byte(core.ESDTRoleModifyRoyalties), + []byte(core.ESDTRoleSetNewURI), + []byte(core.ESDTRoleNFTUpdate), + }...) } return nftRoles, nil @@ -558,8 +571,21 @@ func (e *esdt) getAllRolesForTokenType(tokenType string) ([][]byte, error) { case core.FungibleESDT: return [][]byte{[]byte(core.ESDTRoleLocalMint), []byte(core.ESDTRoleLocalBurn)}, nil case core.DynamicSFTESDT, core.DynamicMetaESDT: - dynamicRoles := [][]byte{[]byte(core.ESDTRoleNFTCreate), []byte(core.ESDTRoleNFTBurn), []byte(core.ESDTRoleNFTAddQuantity), []byte(core.ESDTRoleNFTUpdateAttributes), []byte(core.ESDTRoleNFTAddURI)} - dynamicRoles = append(dynamicRoles, [][]byte{[]byte(core.ESDTRoleNFTRecreate), []byte(core.ESDTRoleModifyCreator), []byte(core.ESDTRoleModifyRoyalties), []byte(core.ESDTRoleSetNewURI)}...) + dynamicRoles := [][]byte{ + []byte(core.ESDTRoleNFTCreate), + []byte(core.ESDTRoleNFTBurn), + []byte(core.ESDTRoleNFTAddQuantity), + []byte(core.ESDTRoleNFTUpdateAttributes), + []byte(core.ESDTRoleNFTAddURI), + } + + dynamicRoles = append(dynamicRoles, [][]byte{ + []byte(core.ESDTRoleNFTRecreate), + []byte(core.ESDTRoleModifyCreator), + []byte(core.ESDTRoleModifyRoyalties), + []byte(core.ESDTRoleSetNewURI), + []byte(core.ESDTRoleNFTUpdate), + }...) return dynamicRoles, nil } @@ -723,6 +749,11 @@ func isTokenNameHumanReadable(tokenName []byte) bool { } func (e *esdt) createNewTokenIdentifier(caller []byte, ticker []byte) ([]byte, error) { + if e.enableEpochsHandler.IsFlagEnabled(common.EGLDInESDTMultiTransferFlag) { + if bytes.Equal(ticker, []byte(eGLD)) { + return nil, vm.ErrCouldNotCreateNewTokenIdentifier + } + } newRandomBase := append(caller, e.eei.BlockChainHook().CurrentRandomSeed()...) newRandom := e.hasher.Compute(string(newRandomBase)) newRandomForTicker := newRandom[:tickerRandomSequenceLength] @@ -1635,6 +1666,11 @@ func (e *esdt) isSpecialRoleValidForNonFungible(argument string) error { return nil } return vm.ErrInvalidArgument + case core.ESDTRoleNFTUpdate: + if e.enableEpochsHandler.IsFlagEnabled(common.DynamicESDTFlag) { + return nil + } + return vm.ErrInvalidArgument default: return vm.ErrInvalidArgument } @@ -1660,6 +1696,8 @@ func (e *esdt) isSpecialRoleValidForDynamicNFT(argument string) error { return nil case core.ESDTRoleNFTRecreate: return nil + case core.ESDTRoleNFTUpdate: + return nil default: return vm.ErrInvalidArgument } @@ -1766,8 +1804,16 @@ func isDynamicTokenType(tokenType []byte) bool { } func rolesForDynamicWhichHasToBeSingular() []string { - return []string{core.ESDTRoleNFTCreate, core.ESDTRoleNFTUpdateAttributes, core.ESDTRoleNFTAddURI, - core.ESDTRoleSetNewURI, core.ESDTRoleModifyCreator, core.ESDTRoleModifyRoyalties, core.ESDTRoleNFTRecreate} + return []string{ + core.ESDTRoleNFTCreate, + core.ESDTRoleNFTUpdateAttributes, + core.ESDTRoleNFTAddURI, + core.ESDTRoleSetNewURI, + core.ESDTRoleModifyCreator, + core.ESDTRoleModifyRoyalties, + core.ESDTRoleNFTRecreate, + core.ESDTRoleNFTUpdate, + } } func (e *esdt) checkRolesForDynamicTokens( diff --git a/vm/systemSmartContracts/logs.go b/vm/systemSmartContracts/logs.go index 69af22820e1..c40834107f3 100644 --- a/vm/systemSmartContracts/logs.go +++ b/vm/systemSmartContracts/logs.go @@ -64,13 +64,10 @@ func (d *delegation) createAndAddLogEntryForDelegate( function == mergeValidatorDataToDelegation || function == changeOwner { address = contractCallInput.Arguments[0] - - topics = append(topics, contractCallInput.RecipientAddr) - } - if function == core.SCDeployInitFunctionName { - topics = append(topics, contractCallInput.RecipientAddr) } + topics = append(topics, contractCallInput.RecipientAddr) + entry := &vmcommon.LogEntry{ Identifier: []byte("delegate"), Address: address, diff --git a/vm/systemSmartContracts/logs_test.go b/vm/systemSmartContracts/logs_test.go index 5f88b1ddabd..4fc3f536878 100644 --- a/vm/systemSmartContracts/logs_test.go +++ b/vm/systemSmartContracts/logs_test.go @@ -37,6 +37,7 @@ func TestCreateLogEntryForDelegate(t *testing.T) { VMInput: vmcommon.VMInput{ CallerAddr: []byte("caller"), }, + RecipientAddr: []byte("recipient"), }, delegationValue, &GlobalFundData{ @@ -52,7 +53,7 @@ func TestCreateLogEntryForDelegate(t *testing.T) { require.Equal(t, &vmcommon.LogEntry{ Identifier: []byte("delegate"), Address: []byte("caller"), - Topics: [][]byte{delegationValue.Bytes(), big.NewInt(6000).Bytes(), big.NewInt(1).Bytes(), big.NewInt(1001000).Bytes()}, + Topics: [][]byte{delegationValue.Bytes(), big.NewInt(6000).Bytes(), big.NewInt(1).Bytes(), big.NewInt(1001000).Bytes(), []byte("recipient")}, }, res) }