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

Add NodeGateway retrying nonce requests if response outdated #1946

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/AeSdkAepp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
UnAuthorizedAccountError,
RpcConnectionError,
} from './utils/errors';
import Node from './Node';
import Node from './node/Direct';
import BrowserConnection from './aepp-wallet-communication/connection/Browser';

/**
Expand Down
2 changes: 1 addition & 1 deletion src/AeSdkBase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Node from './Node';
import Node from './node/Direct';
import AccountBase from './account/Base';
import {
CompilerError, DuplicateNodeError, NodeNotFoundError, NotImplementedError, TypeError,
Expand Down
2 changes: 1 addition & 1 deletion src/AeSdkMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import createDelegationSignature from './contract/delegation-signature';
import * as contractGaMethods from './contract/ga';
import { buildTxAsync } from './tx/builder';
import { mapObject, UnionToIntersection, wrapWithProxy } from './utils/other';
import Node from './Node';
import Node from './node/Direct';
import { TxParamsAsync } from './tx/builder/schema.generated';
import AccountBase from './account/Base';
import { Encoded } from './utils/encoder';
Expand Down
2 changes: 1 addition & 1 deletion src/account/Base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Encoded } from '../utils/encoder';
import Node from '../Node';
import Node from '../node/Direct';
import CompilerBase from '../contract/compiler/Base';
import { AensName, ConsensusProtocolVersion, Int } from '../tx/builder/constants';
import { AciValue, Domain } from '../utils/typed-data';
Expand Down
2 changes: 1 addition & 1 deletion src/account/LedgerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import AccountLedger, { CLA, GET_ADDRESS, GET_APP_CONFIGURATION } from './Ledger
import { UnsupportedVersionError } from '../utils/errors';
import { Encoded } from '../utils/encoder';
import semverSatisfies from '../utils/semver-satisfies';
import Node from '../Node';
import Node from '../node/Direct';

/**
* A factory class that generates instances of AccountLedger based on provided transport.
Expand Down
2 changes: 1 addition & 1 deletion src/aens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Encoded, Encoding } from './utils/encoder';
import { UnsupportedProtocolError } from './utils/errors';
import { sendTransaction, SendTransactionOptions, getName } from './chain';
import { buildTxAsync, BuildTxOptions } from './tx/builder';
import { TransformNodeType } from './Node';
import { TransformNodeType } from './node/Base';
import { NameEntry, NamePointer } from './apis/node';
import AccountBase from './account/Base';
import { AddressEncodings } from './tx/builder/field-types/address';
Expand Down
2 changes: 1 addition & 1 deletion src/aepp-wallet-communication/rpc/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Encoded } from '../../utils/encoder';
import { Domain, AciValue } from '../../utils/typed-data';
import { METHODS, SUBSCRIPTION_TYPES, WALLET_TYPE } from '../schema';
import { TransformNodeType } from '../../Node';
import { TransformNodeType } from '../../node/Base';
import { SignedTx } from '../../apis/node';
import { AensName } from '../../tx/builder/constants';

Expand Down
3 changes: 2 additions & 1 deletion src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
AensPointerContextError, DryRunError, InvalidAensNameError, TransactionError,
TxTimedOutError, TxNotInChainError, InternalError,
} from './utils/errors';
import Node, { TransformNodeType } from './Node';
import { TransformNodeType } from './node/Base';
import Node from './node/Direct';
import {
Account as AccountNode, ByteCode, ContractObject, DryRunResult, DryRunResults,
Generation, KeyBlock, MicroBlockHeader, NameEntry, SignedTx,
Expand Down
3 changes: 2 additions & 1 deletion src/contract/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import {
ContractCallObject as NodeContractCallObject, Event as NodeEvent,
} from '../apis/node';
import CompilerBase, { Aci } from './compiler/Base';
import Node, { TransformNodeType } from '../Node';
import { TransformNodeType } from '../node/Base';
import Node from '../node/Direct';
import {
getAccount, getContract, getContractByteCode, resolveName, txDryRun, sendTransaction,
SendTransactionOptions,
Expand Down
2 changes: 1 addition & 1 deletion src/contract/delegation-signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ArgumentError } from '../utils/errors';
import { AensName } from '../tx/builder/constants';
import AccountBase from '../account/Base';
import { isNameValid } from '../tx/builder/helpers';
import Node from '../Node';
import Node from '../node/Direct';

function ensureOracleQuery(oq: string): asserts oq is Encoded.OracleQueryId {
if (!oq.startsWith('oq_')) throw new ArgumentError('oq', 'oracle query', oq);
Expand Down
2 changes: 1 addition & 1 deletion src/contract/ga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ArgumentError, IllegalArgumentError } from '../utils/errors';
import { concatBuffers } from '../utils/other';
import AccountBase from '../account/Base';
import Contract from './Contract';
import Node from '../Node';
import Node from '../node/Direct';
import { sendTransaction, SendTransactionOptions, getAccount } from '../chain';
import CompilerBase from './compiler/Base';

Expand Down
3 changes: 2 additions & 1 deletion src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export { default as AeSdkBase } from './AeSdkBase';
export { default as AeSdk } from './AeSdk';
export { default as AeSdkAepp } from './AeSdkAepp';
export { default as AeSdkWallet } from './AeSdkWallet';
export { default as Node } from './Node';
export { default as Node } from './node/Direct';
export { default as NodeGateway } from './node/Gateway';
export { default as verifyTransaction } from './tx/validator';
export { default as AccountBase } from './account/Base';
export { default as MemoryAccount } from './account/Memory';
Expand Down
104 changes: 4 additions & 100 deletions src/Node.ts → src/node/Base.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
// eslint-disable-next-line max-classes-per-file
import BigNumber from 'bignumber.js';
import { OperationArguments, OperationSpec } from '@azure/core-client';
import {
genRequestQueuesPolicy, genCombineGetRequestsPolicy, genErrorFormatterPolicy,
genVersionCheckPolicy, genRetryOnFailurePolicy,
} from './utils/autorest';
import { Node as NodeApi, NodeOptionalParams, ErrorModel } from './apis/node';
import { mapObject } from './utils/other';
import { UnsupportedVersionError } from './utils/errors';
import { Encoded } from './utils/encoder';
import { ConsensusProtocolVersion } from './tx/builder/constants';
import { Node as NodeApi } from '../apis/node';
import { mapObject } from '../utils/other';
import { Encoded } from '../utils/encoder';

const bigIntPropertyNames = [
'balance', 'queryFee', 'fee', 'amount', 'nameFee', 'channelAmount',
Expand Down Expand Up @@ -104,95 +98,5 @@ type NodeTransformedApi = new (...args: ConstructorParameters<typeof NodeApi>) =
? NodeApi[Name] : TransformNodeType<NodeApi[Name]>
};

interface NodeInfo {
url: string;
nodeNetworkId: string;
version: string;
consensusProtocolVersion: ConsensusProtocolVersion;
}

export default class Node extends (NodeTransformed as unknown as NodeTransformedApi) {
#networkIdPromise?: Promise<string | Error>;

/**
* @param url - Url for node API
* @param options - Options
* @param options.ignoreVersion - Don't ensure that the node is supported
* @param options.retryCount - Amount of extra requests to do in case of failure
* @param options.retryOverallDelay - Time in ms to wait between all retries
*/
constructor(
url: string,
{
ignoreVersion = false, retryCount = 3, retryOverallDelay = 800, ...options
}: NodeOptionalParams & {
ignoreVersion?: boolean;
retryCount?: number;
retryOverallDelay?: number;
} = {},
) {
// eslint-disable-next-line constructor-super
super(url, {
allowInsecureConnection: true,
additionalPolicies: [
genRequestQueuesPolicy(),
genCombineGetRequestsPolicy(),
genRetryOnFailurePolicy(retryCount, retryOverallDelay),
genErrorFormatterPolicy((body: ErrorModel) => ` ${body.reason}`),
],
...options,
});
if (!ignoreVersion) {
const statusPromise = this.getStatus();
const versionPromise = statusPromise.then(({ nodeVersion }) => nodeVersion, (error) => error);
this.#networkIdPromise = statusPromise.then(({ networkId }) => networkId, (error) => error);
this.pipeline.addPolicy(
genVersionCheckPolicy('node', '/v3/status', versionPromise, '6.2.0', '7.0.0'),
);
}
this.intAsString = true;
}

/**
* Returns network ID provided by node.
* This method won't do extra requests on subsequent calls.
*/
async getNetworkId(): Promise<string> {
this.#networkIdPromise ??= this.getStatus().then(({ networkId }) => networkId);
const networkId = await this.#networkIdPromise;
if (networkId instanceof Error) throw networkId;
return networkId;
}

async getNodeInfo(): Promise<NodeInfo> {
const {
nodeVersion,
networkId: nodeNetworkId,
protocols,
topBlockHeight,
} = await this.getStatus();

const consensusProtocolVersion = protocols
.filter(({ effectiveAtHeight }) => topBlockHeight >= effectiveAtHeight)
.reduce(
(acc, p) => (p.effectiveAtHeight > acc.effectiveAtHeight ? p : acc),
{ effectiveAtHeight: -1, version: 0 },
)
.version;
if (ConsensusProtocolVersion[consensusProtocolVersion] == null) {
const version = consensusProtocolVersion.toString();
const versions = Object.values(ConsensusProtocolVersion)
.filter((el) => typeof el === 'number').map((el) => +el);
const geVersion = Math.min(...versions).toString();
const ltVersion = (Math.max(...versions) + 1).toString();
throw new UnsupportedVersionError('consensus protocol', version, geVersion, ltVersion);
}

return {
url: this.$host,
nodeNetworkId,
version: nodeVersion,
consensusProtocolVersion,
};
}
export default class NodeBase extends (NodeTransformed as unknown as NodeTransformedApi) {
}
112 changes: 112 additions & 0 deletions src/node/Direct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import {
genRequestQueuesPolicy, genCombineGetRequestsPolicy, genErrorFormatterPolicy,
genVersionCheckPolicy, genRetryOnFailurePolicy,
} from '../utils/autorest';
import NodeBase from './Base';
import { UnsupportedVersionError } from '../utils/errors';
import { ConsensusProtocolVersion } from '../tx/builder/constants';
import { NodeOptionalParams, ErrorModel } from '../apis/node';

interface NodeInfo {
url: string;
nodeNetworkId: string;
version: string;
consensusProtocolVersion: ConsensusProtocolVersion;
}

export default class NodeDefault extends NodeBase {
#networkIdPromise?: Promise<string | Error>;

/**
* @param url - Url for node API
* @param options - Options
* @param options.ignoreVersion - Don't ensure that the node is supported
* @param options.retryCount - Amount of extra requests to do in case of failure
* @param options.retryOverallDelay - Time in ms to wait between all retries
*/
constructor(
url: string,
{
ignoreVersion = false, _disableGatewayWarning = false,
retryCount = 3, retryOverallDelay = 800,
...options
}: NodeOptionalParams & {
ignoreVersion?: boolean;
_disableGatewayWarning?: boolean;
retryCount?: number;
retryOverallDelay?: number;
} = {},
) {
const { hostname } = new URL(url);
if (
!_disableGatewayWarning
&& ['mainnet.aeternity.io', 'testnet.aeternity.io'].includes(hostname)
) {
console.warn(`Node: use NodeGateway to connect to ${hostname} for better reliability.`);
}
// eslint-disable-next-line constructor-super
super(url, {
allowInsecureConnection: true,
additionalPolicies: [
genRequestQueuesPolicy(),
genCombineGetRequestsPolicy(),
// TODO: move to NodeGateway in the next breaking release
genRetryOnFailurePolicy(retryCount, retryOverallDelay),
genErrorFormatterPolicy((body: ErrorModel) => ` ${body.reason}`),
],
...options,
});
if (!ignoreVersion) {
const statusPromise = this.getStatus();
const versionPromise = statusPromise.then(({ nodeVersion }) => nodeVersion, (error) => error);
this.#networkIdPromise = statusPromise.then(({ networkId }) => networkId, (error) => error);
this.pipeline.addPolicy(
genVersionCheckPolicy('node', '/v3/status', versionPromise, '6.2.0', '7.0.0'),
);
}
this.intAsString = true;
}

/**
* Returns network ID provided by node.
* This method won't do extra requests on subsequent calls.
*/
async getNetworkId(): Promise<string> {
this.#networkIdPromise ??= this.getStatus().then(({ networkId }) => networkId);
const networkId = await this.#networkIdPromise;
if (networkId instanceof Error) throw networkId;
return networkId;
}

async getNodeInfo(): Promise<NodeInfo> {
const {
nodeVersion,
networkId: nodeNetworkId,
protocols,
topBlockHeight,
} = await this.getStatus();

const consensusProtocolVersion = protocols
.filter(({ effectiveAtHeight }) => topBlockHeight >= effectiveAtHeight)
.reduce(
(acc, p) => (p.effectiveAtHeight > acc.effectiveAtHeight ? p : acc),
{ effectiveAtHeight: -1, version: 0 },
)
.version;
if (ConsensusProtocolVersion[consensusProtocolVersion] == null) {
const version = consensusProtocolVersion.toString();
const versions = Object.values(ConsensusProtocolVersion)
.filter((el) => typeof el === 'number').map((el) => +el);
const geVersion = Math.min(...versions).toString();
const ltVersion = (Math.max(...versions) + 1).toString();
throw new UnsupportedVersionError('consensus protocol', version, geVersion, ltVersion);
}

return {
url: this.$host,
nodeNetworkId,
version: nodeVersion,
consensusProtocolVersion,
};
}
}
Loading