Skip to content

Commit

Permalink
Merge pull request #774 from iotaledger/task/api-fallback
Browse files Browse the repository at this point in the history
[Task]: Make node primary in fetches and Chronicle the fallback (Stardust)
  • Loading branch information
msarcev authored May 5, 2023
2 parents d8a6af8 + 23753ba commit d5e2c92
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 59 deletions.
8 changes: 4 additions & 4 deletions api/src/utils/stardust/searchExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class SearchExecutor {
if (searchQuery.output) {
promises.push(
new Promise((resolve, reject) => {
StardustTangleHelper.tryFetchPermanodeThenNode<string, IOutputResponse>(
StardustTangleHelper.tryFetchNodeThenPermanode<string, IOutputResponse>(
searchQuery.output,
"output",
network
Expand All @@ -148,7 +148,7 @@ export class SearchExecutor {
if (searchQuery.aliasId) {
promises.push(
new Promise((resolve, reject) => {
StardustTangleHelper.tryFetchPermanodeThenNode<string, IOutputsResponse>(
StardustTangleHelper.tryFetchNodeThenPermanode<string, IOutputsResponse>(
searchQuery.aliasId,
"alias",
network,
Expand All @@ -174,7 +174,7 @@ export class SearchExecutor {
if (searchQuery.nftId) {
promises.push(
new Promise((resolve, reject) => {
StardustTangleHelper.tryFetchPermanodeThenNode<string, IOutputsResponse>(
StardustTangleHelper.tryFetchNodeThenPermanode<string, IOutputsResponse>(
searchQuery.nftId,
"nft",
network,
Expand All @@ -200,7 +200,7 @@ export class SearchExecutor {
if (searchQuery.foundryId) {
promises.push(
new Promise((resolve, reject) => {
StardustTangleHelper.tryFetchPermanodeThenNode<string, IOutputsResponse>(
StardustTangleHelper.tryFetchNodeThenPermanode<string, IOutputsResponse>(
searchQuery.foundryId,
"foundry",
network,
Expand Down
7 changes: 6 additions & 1 deletion api/src/utils/stardust/searchQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ export class SearchQueryBuilder {
}

// also perform a tag search
tag = Converter.utf8ToHex(this.query, true);
const maybeTag = Converter.isHex(this.query, true) ?
HexHelper.addPrefix(this.query) :
Converter.utf8ToHex(this.query, true);
if (maybeTag.length < 66) {
tag = maybeTag;
}
}

return {
Expand Down
86 changes: 40 additions & 46 deletions api/src/utils/stardust/stardustTangleHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class StardustTangleHelper {
*/
public static async block(network: INetwork, blockId: string): Promise<IBlockResponse> {
blockId = HexHelper.addPrefix(blockId);
const blockRaw = await this.tryFetchPermanodeThenNode<string, Uint8Array>(
const blockRaw = await this.tryFetchNodeThenPermanode<string, Uint8Array>(
blockId,
"blockRaw",
network
Expand Down Expand Up @@ -106,7 +106,7 @@ export class StardustTangleHelper {
*/
public static async blockDetails(network: INetwork, blockId: string): Promise<IBlockDetailsResponse> {
blockId = HexHelper.addPrefix(blockId);
const metadata = await this.tryFetchPermanodeThenNode<string, IBlockMetadata>(
const metadata = await this.tryFetchNodeThenPermanode<string, IBlockMetadata>(
blockId,
"blockMetadata",
network
Expand All @@ -130,7 +130,7 @@ export class StardustTangleHelper {
transactionId: string
): Promise<ITransactionDetailsResponse> {
transactionId = HexHelper.addPrefix(transactionId);
const blockRaw = await this.tryFetchPermanodeThenNode<string, Uint8Array>(
const blockRaw = await this.tryFetchNodeThenPermanode<string, Uint8Array>(
transactionId,
"transactionIncludedBlockRaw",
network
Expand Down Expand Up @@ -159,7 +159,7 @@ export class StardustTangleHelper {
* @returns The item details.
*/
public static async outputDetails(network: INetwork, outputId: string): Promise<IOutputDetailsResponse> {
const outputResponse = await this.tryFetchPermanodeThenNode<string, IOutputResponse>(
const outputResponse = await this.tryFetchNodeThenPermanode<string, IOutputResponse>(
outputId,
"output",
network
Expand All @@ -177,24 +177,22 @@ export class StardustTangleHelper {
* @returns The item details.
*/
public static async outputsDetails(network: INetwork, outputIds: string[]): Promise<IOutputResponse[]> {
const promises: Promise<void>[] = [];
const promises: Promise<IOutputDetailsResponse>[] = [];
const outputResponses: IOutputResponse[] = [];

for (const outputId of outputIds) {
const promise = this.outputDetails(network, outputId)
.then(response => {
if (response.output?.output && response.output?.metadata) {
outputResponses.push(response.output);
}
})
.catch(e => {
logger.warn(`[StardustTangleHelper] Failed fetching output ${outputId} on ${network.network}. Cause ${e}`);
});

const promise = this.outputDetails(network, outputId);
promises.push(promise);
}
try {
await Promise.all(promises);
await Promise.all(promises)
.then(results => {
for (const outputDetails of results) {
if (outputDetails.output?.output && outputDetails.output?.metadata) {
outputResponses.push(outputDetails.output);
}
}
});

return outputResponses;
} catch (e) {
Expand All @@ -211,7 +209,7 @@ export class StardustTangleHelper {
public static async milestoneDetailsById(
network: INetwork, milestoneId: string
): Promise<IMilestoneDetailsResponse | undefined> {
const milestonePayload = await this.tryFetchPermanodeThenNode<string, IMilestonePayload>(
const milestonePayload = await this.tryFetchNodeThenPermanode<string, IMilestonePayload>(
milestoneId,
"milestoneById",
network
Expand Down Expand Up @@ -239,7 +237,7 @@ export class StardustTangleHelper {
public static async milestoneDetailsByIndex(
network: INetwork, milestoneIndex: number
): Promise<IMilestoneDetailsResponse | undefined> {
const milestonePayload = await this.tryFetchPermanodeThenNode<number, IMilestonePayload>(
const milestonePayload = await this.tryFetchNodeThenPermanode<number, IMilestonePayload>(
milestoneIndex,
"milestoneByIndex",
network
Expand Down Expand Up @@ -273,7 +271,7 @@ export class StardustTangleHelper {
let outputIds: string[] = [];

do {
const outputIdsResponse = await this.tryFetchPermanodeThenNode<Record<string, unknown>, IOutputsResponse>(
const outputIdsResponse = await this.tryFetchNodeThenPermanode<Record<string, unknown>, IOutputsResponse>(
{ addressBech32, cursor },
"basicOutputs",
network,
Expand Down Expand Up @@ -304,7 +302,7 @@ export class StardustTangleHelper {
let outputIds: string[] = [];

do {
const outputIdsResponse = await this.tryFetchPermanodeThenNode<Record<string, unknown>, IOutputsResponse>(
const outputIdsResponse = await this.tryFetchNodeThenPermanode<Record<string, unknown>, IOutputsResponse>(
{ stateControllerBech32: addressBech32, cursor },
"aliases",
network,
Expand Down Expand Up @@ -335,7 +333,7 @@ export class StardustTangleHelper {
let outputIds: string[] = [];

do {
const outputIdsResponse = await this.tryFetchPermanodeThenNode<Record<string, unknown>, IOutputsResponse>(
const outputIdsResponse = await this.tryFetchNodeThenPermanode<Record<string, unknown>, IOutputsResponse>(
{ addressBech32, cursor },
"nfts",
network,
Expand All @@ -347,7 +345,6 @@ export class StardustTangleHelper {
} while (cursor);

const outputResponses = await this.outputsDetails(network, outputIds);

return {
outputs: outputResponses
};
Expand All @@ -363,7 +360,7 @@ export class StardustTangleHelper {
network: INetwork,
aliasId: string
): Promise<IAliasResponse | undefined> {
const aliasOutput = await this.tryFetchPermanodeThenNode<string, IOutputsResponse>(
const aliasOutput = await this.tryFetchNodeThenPermanode<string, IOutputsResponse>(
aliasId,
"alias",
network,
Expand Down Expand Up @@ -392,7 +389,7 @@ export class StardustTangleHelper {
aliasAddress: string
): Promise<IFoundriesResponse | undefined> {
try {
const response = await this.tryFetchPermanodeThenNode<Record<string, unknown>, IOutputsResponse>(
const response = await this.tryFetchNodeThenPermanode<Record<string, unknown>, IOutputsResponse>(
{ aliasAddressBech32: aliasAddress },
"foundries",
network,
Expand All @@ -419,7 +416,7 @@ export class StardustTangleHelper {
network: INetwork,
foundryId: string
): Promise<IFoundryResponse | undefined> {
const foundryOutput = await this.tryFetchPermanodeThenNode<string, IOutputsResponse>(
const foundryOutput = await this.tryFetchNodeThenPermanode<string, IOutputsResponse>(
foundryId,
"foundry",
network,
Expand Down Expand Up @@ -448,7 +445,7 @@ export class StardustTangleHelper {
nftId: string
): Promise<INftDetailsResponse | undefined> {
try {
const nftOutputs = await this.tryFetchPermanodeThenNode<string, IOutputsResponse>(
const nftOutputs = await this.tryFetchNodeThenPermanode<string, IOutputsResponse>(
nftId,
"nft",
network,
Expand Down Expand Up @@ -482,7 +479,7 @@ export class StardustTangleHelper {
cursor?: string
): Promise<IBasicOutputsResponse | undefined> {
try {
const basicOutputIdsResponse: IOutputsResponse = await this.tryFetchPermanodeThenNode<
const basicOutputIdsResponse: IOutputsResponse = await this.tryFetchNodeThenPermanode<
Record<string, unknown>,
IOutputsResponse
>({ tagHex: encodedTag, pageSize, cursor }, "basicOutputs", network, true);
Expand Down Expand Up @@ -510,7 +507,7 @@ export class StardustTangleHelper {
cursor?: string
): Promise<INftOutputsResponse | undefined> {
try {
const nftOutputIdsResponse: IOutputsResponse = await this.tryFetchPermanodeThenNode<
const nftOutputIdsResponse: IOutputsResponse = await this.tryFetchNodeThenPermanode<
Record<string, unknown>,
IOutputsResponse
>({ tagHex: encodedTag, pageSize, cursor }, "nfts", network, true);
Expand Down Expand Up @@ -590,15 +587,15 @@ export class StardustTangleHelper {
}

/**
* Generic helper function to try fetching from permanode client (if configured).
* On failure (or not present), we try to fetch from node.
* Generic helper function to try fetching from node client.
* On failure (or not present), we try to fetch from permanode (if configured).
* @param args The argument(s) to pass to the fetch calls.
* @param methodName The function to call on the client.
* @param network The network config in context.
* @param isIndexerCall The boolean flag for indexer api instead of core api.
* @returns The results or null if call(s) failed.
*/
public static async tryFetchPermanodeThenNode<A, R>(
public static async tryFetchNodeThenPermanode<A, R>(
args: A,
methodName: string,
network: INetwork,
Expand All @@ -609,8 +606,19 @@ export class StardustTangleHelper {
permaNodeEndpointUser, permaNodeEndpointPassword, disableApiFallback
} = network;
const isFallbackEnabled = !disableApiFallback;
const node = !isIndexerCall ?
new SingleNodeClient(provider, { userName: user, password }) :
new IndexerPluginClient(
new SingleNodeClient(provider, { userName: user, password })
);

try {
// try fetch from node
const result: Promise<R> = node[methodName](args);
return await result;
} catch { }

if (permaNodeEndpoint) {
if (permaNodeEndpoint && isFallbackEnabled) {
const permanode = !isIndexerCall ?
new SingleNodeClient(
permaNodeEndpoint,
Expand All @@ -630,20 +638,6 @@ export class StardustTangleHelper {
} catch { }
}

if (!permaNodeEndpoint || isFallbackEnabled) {
const node = !isIndexerCall ?
new SingleNodeClient(provider, { userName: user, password }) :
new IndexerPluginClient(
new SingleNodeClient(provider, { userName: user, password })
);

try {
// try fetch from node
const result: Promise<R> = node[methodName](args);
return await result;
} catch { }
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TokenSchemeTypes } from "@iota/iota.js-stardust";
import React from "react";
import { useTokenRegistryNativeTokenCheck } from "../../../../helpers/hooks/useTokenRegistryNativeTokenCheck";
import { formatNumberWithCommas } from "../../../../helpers/stardust/valueFormatHelper";
import { ITokenMetadata } from "../../../../models/api/stardust/foundry/ITokenMetadata";
import "./TokenInfoSection.scss";

Expand All @@ -22,9 +23,9 @@ interface TokenInfoSectionProps {
const TokenInfoSection: React.FC<TokenInfoSectionProps> = ({ tokenId, tokenScheme, tokenMetadata }) => {
const [isWhitelisted] = useTokenRegistryNativeTokenCheck(tokenId);

const maximumSupply = Number(tokenScheme.maximumSupply);
const mintedTokens = Number(tokenScheme.mintedTokens);
const meltedTokens = Number(tokenScheme.meltedTokens);
const maximumSupply = formatNumberWithCommas(tokenScheme.maximumSupply);
const mintedTokens = formatNumberWithCommas(tokenScheme.mintedTokens);
const meltedTokens = formatNumberWithCommas(tokenScheme.meltedTokens);
return (
<div className="token-info">
<div className="section no-border-bottom padding-b-0">
Expand Down
13 changes: 9 additions & 4 deletions client/src/app/routes/stardust/AddressState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BASIC_OUTPUT_TYPE, Bech32Helper, HexEncodedString, IAliasOutput, IMetadataFeature, IOutputResponse, METADATA_FEATURE_TYPE, OutputTypes } from "@iota/iota.js-stardust";
import { ALIAS_ADDRESS_TYPE, BASIC_OUTPUT_TYPE, Bech32Helper, HexEncodedString, IAliasOutput, IMetadataFeature, IOutputResponse, METADATA_FEATURE_TYPE, NFT_ADDRESS_TYPE, OutputTypes } from "@iota/iota.js-stardust";
import { Converter, ReadStream } from "@iota/util.js-stardust";
import { Reducer, useContext, useEffect, useReducer } from "react";
import { useLocation, useParams } from "react-router-dom";
Expand Down Expand Up @@ -95,13 +95,18 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch<Partial<IA

const addressBech32: string | null = state.bech32AddressDetails?.bech32 ?? null;
const addressHex: string | null = state.bech32AddressDetails?.hex ?? null;
const addressType: number | null = state.bech32AddressDetails?.type ?? null;
const [addressBasicOutputs, isBasicOutputsLoading] = useAddressBasicOutputs(network, addressBech32);
const [addressAliasOutputs, isAliasOutputsLoading] = useAddressAliasOutputs(network, addressBech32);
const [addressNftOutputs, isNftOutputsLoading] = useAddressNftOutputs(network, addressBech32);
const [, nftMetadata, issuerId, isNftDetailsLoading] = useNftDetails(network, addressHex);
const [aliasOutput, isAliasDetailsLoading] = useAliasDetails(network, addressHex);
const [, nftMetadata, issuerId, isNftDetailsLoading] = useNftDetails(
network, addressType === NFT_ADDRESS_TYPE ? addressHex : null
);
const [aliasOutput, isAliasDetailsLoading] = useAliasDetails(
network, addressType === ALIAS_ADDRESS_TYPE ? addressHex : null
);
const [aliasFoundries, isAliasFoundriesLoading] = useAliasControlledFoundries(
network, state.bech32AddressDetails
network, addressType === ALIAS_ADDRESS_TYPE ? state.bech32AddressDetails : null
);
const [balance, sigLockedBalance] = useAddressBalance(
network, state.bech32AddressDetails?.bech32 ?? null
Expand Down
2 changes: 1 addition & 1 deletion client/src/app/routes/stardust/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class Search extends AsyncComponent<RouteComponentProps<SearchRouteProps>, Searc
query
});

if (response) {
if (response && Object.keys(response).length > 0) {
let route = "";
let routeParam = query;
let redirectState = {};
Expand Down
11 changes: 11 additions & 0 deletions client/src/helpers/stardust/valueFormatHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ export function formatAmount(
return `${amount}${tokenInfo.unit}`;
}

/**
* Formats a number by adding commas as thousands separators.
* @param {string|number} value - The number to format. Can be a string or a number.
* @returns {string} The formatted number as a string, with commas separating thousands.
*/
export function formatNumberWithCommas(
value: string | number
): string {
return BigInt(value).toLocaleString("en", { useGrouping: true });
}

/**
* Format amount to two decimal places without rounding off.
* @param value The raw amount to format.
Expand Down

0 comments on commit d5e2c92

Please sign in to comment.