Skip to content

Commit

Permalink
Throw timeout error and do not make the node unhealthy (hashgraph#1298)
Browse files Browse the repository at this point in the history
  • Loading branch information
ochikov authored Oct 21, 2022
1 parent 0dfaf5c commit 4578f61
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/channel/NodeChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default class NodeChannel extends Channel {
setTimeout(() => {
if (!received) {
this._client.close();
callback(new GrpcServicesError(GrpcStatus.Unavailable));
callback(new GrpcServicesError(GrpcStatus.Timeout));
}
}, 10_000);

Expand Down
12 changes: 8 additions & 4 deletions src/grpc/GrpcStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ export default class GrpcStatus {
return GrpcStatus.AlreadyExists;
case 7:
return GrpcStatus.PermissionDenied;
case 16:
return GrpcStatus.Unauthenticated;
case 8:
return GrpcStatus.ResourceExhausted;
case 9:
Expand All @@ -72,6 +70,10 @@ export default class GrpcStatus {
return GrpcStatus.Unavailable;
case 15:
return GrpcStatus.DataLoss;
case 16:
return GrpcStatus.Unauthenticated;
case 17:
return GrpcStatus.Timeout;
default:
throw new Error(
"(BUG) non-exhaustive GrpcStatus switch statement"
Expand Down Expand Up @@ -118,7 +120,8 @@ export default class GrpcStatus {
return "UNAVAILABLE";
case GrpcStatus.DataLoss:
return "DATA_LOSS";

case GrpcStatus.Timeout:
return "TIMEOUT";
default:
return `UNKNOWN (${this._code})`;
}
Expand All @@ -140,7 +143,6 @@ GrpcStatus.DeadlineExceeded = new GrpcStatus(4);
GrpcStatus.NotFound = new GrpcStatus(5);
GrpcStatus.AlreadyExists = new GrpcStatus(6);
GrpcStatus.PermissionDenied = new GrpcStatus(7);
GrpcStatus.Unauthenticated = new GrpcStatus(16);
GrpcStatus.ResourceExhausted = new GrpcStatus(8);
GrpcStatus.FailedPrecondition = new GrpcStatus(9);
GrpcStatus.Aborted = new GrpcStatus(10);
Expand All @@ -149,3 +151,5 @@ GrpcStatus.Unimplemented = new GrpcStatus(12);
GrpcStatus.Internal = new GrpcStatus(13);
GrpcStatus.Unavailable = new GrpcStatus(14);
GrpcStatus.DataLoss = new GrpcStatus(15);
GrpcStatus.Unauthenticated = new GrpcStatus(16);
GrpcStatus.Timeout = new GrpcStatus(17);
84 changes: 84 additions & 0 deletions test/integration/ContractCallIntegrationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js";

const smartContractBytecode =
"608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033";
const readDataBytecode =
"0x608060405234801561001057600080fd5b5061026c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806304806dd61461003b5780634278774714610064575b600080fd5b61004e610049366004610178565b610084565b60405161005b91906101a2565b60405180910390f35b610077610072366004610178565b610101565b60405161005b91906101f1565b606060008262ffffff1667ffffffffffffffff8111156100b457634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156100f957816020015b60408051808201909152600080825260208201528152602001906001900390816100d25790505b509392505050565b606060008262ffffff1667ffffffffffffffff81111561013157634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156100f957816020015b60408051602081019091526000815281526020019060019003908161014f579050509392505050565b600060208284031215610189578081fd5b813562ffffff8116811461019b578182fd5b9392505050565b602080825282518282018190526000919060409081850190868401855b828110156101e4578151805185528601518685015292840192908501906001016101bf565b5091979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561022a578351518352928401929184019160010161020d565b5090969550505050505056fea26469706673582212201dc78aeb6e1955ac889c23cf72d0595af987863764ccb6270c7825992093969264736f6c63430008040033";

describe("ContractCallIntegration", function () {
it("should be executable", async function () {
Expand Down Expand Up @@ -312,4 +314,86 @@ describe("ContractCallIntegration", function () {

await env.close();
});

it("should timeout when network node takes longer than 10s to execute the transaction", async function () {
this.timeout(120000);

const env = await IntegrationTestEnv.new();
const operatorKey = env.operatorKey.publicKey;

const response = await new FileCreateTransaction()
.setKeys([operatorKey])
.setContents(readDataBytecode)
.execute(env.client);

let receipt = await response.getReceipt(env.client);

expect(receipt.fileId).to.not.be.null;
expect(receipt.fileId != null ? receipt.fileId.num > 0 : false).to.be
.true;

const file = receipt.fileId;

receipt = await (
await new ContractCreateTransaction()
.setAdminKey(operatorKey)
//Set the file ID of the Hedera file storing the bytecode
.setBytecodeFileId(file)
//Set the gas to instantiate the contract
.setGas(100000)
//Provide the constructor parameters for the contract
.setConstructorParameters()
.execute(env.client)
).getReceipt(env.client);

expect(receipt.contractId).to.not.be.null;
expect(receipt.contractId != null ? receipt.contractId.num > 0 : false)
.to.be.true;

const contractId = receipt.contractId;

let err = false;
try {
const contractQuery = await new ContractCallQuery()
//Set the gas for the query
.setGas(15_000_000)
//Set the contract ID to return the request for
.setContractId(contractId)
//Set the contract function to call
.setFunction(
"getLotsOfData",
new ContractFunctionParameters().addUint24(20000)
)
//Set the query payment for the node returning the request
//This value must cover the cost of the request otherwise will fail
.setQueryPayment(new Hbar(2));

//Submit to a Hedera network
// const txResponse = await contractQuery.execute(client);
// const txResponse2 = await contractQuery2.execute(client);
await contractQuery.execute(env.client);
} catch (error) {
err = error;
}
expect(err.toString()).to.includes("TIMEOUT");

await (
await new ContractDeleteTransaction()
.setContractId(contractId)
.setTransferAccountId(env.client.operatorAccountId)
.execute(env.client)
).getReceipt(env.client);

await (
await new FileDeleteTransaction()
.setFileId(file)
.execute(env.client)
).getReceipt(env.client);

if (!err) {
throw new Error("query did not error");
}

await env.close();
});
});
37 changes: 25 additions & 12 deletions test/integration/client/BaseIntegrationTestEnv.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,29 @@ export default class BaseIntegrationTestEnv {
) {
client = options.client.forTestnet();
} else if (
options.env.HEDERA_NETWORK != null &&
options.env.HEDERA_NETWORK == "localhost"
(options.env.HEDERA_NETWORK != null &&
options.env.HEDERA_NETWORK == "localhost") ||
options.env.HEDERA_NETWORK == "local-node"
) {
client = options.client.forNetwork({
"127.0.0.1:50213": "0.0.3",
"127.0.0.1:50214": "0.0.4",
"127.0.0.1:50215": "0.0.5",
});
} else if (options.env.CONFIG_FILE != null) {
client = await options.client.fromConfigFile(options.env.CONFIG_FILE);
client = await options.client.fromConfigFile(
options.env.CONFIG_FILE
);
} else {
throw new Error("Failed to construct client for IntegrationTestEnv");
throw new Error(
"Failed to construct client for IntegrationTestEnv"
);
}

if (options.env.OPERATOR_ID != null && options.env.OPERATOR_KEY != null) {
if (
options.env.OPERATOR_ID != null &&
options.env.OPERATOR_KEY != null
) {
const operatorId = AccountId.fromString(options.env.OPERATOR_ID);
const operatorKey = PrivateKey.fromString(options.env.OPERATOR_KEY);

Expand All @@ -91,7 +99,8 @@ export default class BaseIntegrationTestEnv {
const originalOperatorKey = client.operatorAccountKey;
const originalOperatorId = client.operatorAccountId;

client.setMaxNodeAttempts(1)
client
.setMaxNodeAttempts(1)
.setNodeMinBackoff(0)
.setNodeMaxBackoff(0)
.setNodeMinReadmitPeriod(0)
Expand All @@ -100,8 +109,9 @@ export default class BaseIntegrationTestEnv {
await client.pingAll();

const network = {};
const nodeAccountIds = options.nodeAccountIds != null ? options.nodeAccountIds : 1;
for (const [ key, value ] of Object.entries(client.network)) {
const nodeAccountIds =
options.nodeAccountIds != null ? options.nodeAccountIds : 1;
for (const [key, value] of Object.entries(client.network)) {
network[key] = value;

if (Object.keys(network).length >= nodeAccountIds) {
Expand All @@ -114,7 +124,9 @@ export default class BaseIntegrationTestEnv {

const response = await new AccountCreateTransaction()
.setKey(newOperatorKey)
.setInitialBalance(new Hbar(options.balance != null ? options.balance : 100))
.setInitialBalance(
new Hbar(options.balance != null ? options.balance : 100)
)
.execute(client);

const newOperatorId = (await response.getReceipt(client)).accountId;
Expand Down Expand Up @@ -142,9 +154,10 @@ export default class BaseIntegrationTestEnv {
}

for (const token of options.token) {
await (await new TokenDeleteTransaction()
.setTokenId(token)
.execute(this.client)
await (
await new TokenDeleteTransaction()
.setTokenId(token)
.execute(this.client)
).getReceipt(this.client);
}
}
Expand Down
3 changes: 3 additions & 0 deletions test/integration/client/NodeIntegrationTestEnv.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import Client from "../../../src/client/NodeClient.js";
import BaseIntegrationTestEnv from "./BaseIntegrationTestEnv.js";
import dotenv from "dotenv";

dotenv.config();

export { Client };

Expand Down

0 comments on commit 4578f61

Please sign in to comment.