Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement a validation to check if a transaction is submitted to a node id which is not present in the Client's list #2124

Open
agadzhalov opened this issue Dec 6, 2024 · 1 comment
Milestone

Comments

@agadzhalov
Copy link
Contributor

Description

Recently there were increased complaints for INVALID_NODE_ACCOUNT error. After debugging with the team it turns out if a transaction is built to be a submitted to a specific node, but that node is not listed in the Clinet's list instead of throwing an error the SDK will send the transaction to a random node and it will reach the consensus node where it will fail at PRECHECK. This is possible because of a this logic in the JS SDK code.

Fix

We must introduce a new error to the user indicating that they are trying to submit a transaction to a node id which is not available in the Client's list and the user will have to adjust its setup. This way the user experience when using the JS SDK and interacting with the Hedera network will be improved.

Example of the new error:

Attempting to execute a transaction against node id 0.0.X, which is not present in Client's node list. Please review and adjust your setup. 

By introducing the new error we will prevent sending it to the consensus node and failing at PRECHECK

How to reproduce

Steps

  1. Create a client that uses a default address book like Client.forTestne
  2. Create a transaction and sign but not submit it.
  3. Create a second Client and modify the list of nodes for that network by creating a custom network.
  4. Remove two of the nodes from the network being used in the first client.
  5. Take the transaction from the first client and submit it using the next client

Run the following example. The whole setup can be found in this repository and example - Four.js

import {
    AccountId,
    PrivateKey,
    Client,
    Hbar,
    TransferTransaction,
} from "@hashgraph/sdk";
import { sortNodes } from '../utils/SortNodeList.js';
import { trimmedNodes } from "../utils/ListOfTrimmedNodes.js";
import { findMissingNodes } from "../utils/FindMissingNodes.js";

import dotenv from "dotenv";

dotenv.config();

/**
 * Case 4: Create a client that uses a default address book like Client.forTestnet and use it to create a transaction and sign but not submit it.
 * Create a second Client and modify the list of nodes for that network by creating a custom network. 
 * Remove two of the nodes from the network being used in the first client. Take the transaction from the first client and submit it using the next client.
 */
async function main() {
    if (
        process.env.OPERATOR_ID == null ||
        process.env.OPERATOR_KEY == null ||
        process.env.HEDERA_NETWORK == null
    ) {
        throw new Error(
            "Environment variables OPERATOR_ID, HEDERA_NETWORK, and OPERATOR_KEY are required."
        );
    }

    // 1. Configure account
    const operatorId = AccountId.fromString(process.env.OPERATOR_ID);
    const operatorKey = PrivateKey.fromStringECDSA(process.env.OPERATOR_KEY);

    // 2. Setup the first client
    const clientOne = Client.forMainnet();
    clientOne.setOperator(operatorId, operatorKey);

    // 3. Get nodes fro, clientOne
    const nodesClient = clientOne._network._network.keys();
    const sortedNodes = sortNodes([...nodesClient]);

    // 4. Defines the account which will receive the hbars after a TransferTransaction.
    const recipientAccountId = new AccountId(3941207);

    // 5. Setup the second client with trimmed nodes, Nodes 16 and Node 21 are removed
    // trimmedNodes are in utils/ListOfTrimmedNodes.js
    const clientTwo = Client.forNetwork(trimmedNodes);
    clientTwo.setOperator(operatorId, operatorKey);

    // 6. We run a loop in order to randomly hit some of the missing nodes
    // Each 5 seconds a transaction is submitted to one of the nodes.
    // If the transaction is submitted to one of the missing nodes INVALID_NODE_ACCOUNT error will be thrown
    setInterval(async () => {
        try {
            // 7. Only sign the transaction with ClientOne, but don't submit it.
            const signTransferTransaction = await new TransferTransaction()
                .addHbarTransfer(operatorId, Hbar.fromTinybars(-1)) //Sending account
                .addHbarTransfer(recipientAccountId, Hbar.fromTinybars(1)) //Receiving account
                .freezeWith(clientOne)
                .sign(operatorKey);


            // 8. Log the missing nodes
            const nodesTrimmedClient = sortNodes([...clientTwo._network._network.keys()]); 
            console.log("Missing nodes", findMissingNodes(sortedNodes, nodesTrimmedClient));
            
            // 9. Execute the transaction with ClientTwo.
            const transferTransaction = await signTransferTransaction.execute(clientTwo);
            // 10. Gets the record and logs it.
            const transactionRecord = await transferTransaction.getRecord(clientTwo);
            console.log(
                "The transfer transaction from account " +
                operatorId +
                " to account " +
                recipientAccountId +
                " was: " +
                transactionRecord.transactionId
                +
                " executed against node id "
                + transferTransaction.nodeId.toString()
            );
        } catch (e) {
            console.log(e);
        }
    }, 5000)
}

void main();
@agadzhalov agadzhalov added this to the v2.47.0 milestone Dec 10, 2024
@Lemeri123
Copy link

Lemeri123 commented Dec 21, 2024

could you assign this to me please

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

No branches or pull requests

2 participants