feat: handle fetch errors

This commit is contained in:
ppe
2023-04-07 21:28:38 +02:00
committed by just_ppe
parent 5ffc438749
commit be52a03a23
9 changed files with 79 additions and 101 deletions

View File

@@ -137,7 +137,7 @@ describe('WarpGatewayInteractionsLoader -> load', () => {
try {
await loader.load(contractId, fromBlockHeight, toBlockHeight);
} catch (e) {
expect(e).toEqual(new Error('Unable to retrieve transactions. Warp gateway responded with status 504.'));
expect(e).toEqual(new Error('Error while communicating with gateway: {"status":504,"ok":false}'));
}
});
it('should throw an error when request fails', async () => {
@@ -148,7 +148,7 @@ describe('WarpGatewayInteractionsLoader -> load', () => {
try {
await loader.load(contractId, fromBlockHeight, toBlockHeight);
} catch (e) {
expect(e).toEqual(new Error('Unable to retrieve transactions. Warp gateway responded with status 500.'));
expect(e).toEqual(new Error('Error while communicating with gateway: {"status":500,"ok":false,"body":{"message":"request fails"}}'));
}
});
});

View File

@@ -20,7 +20,7 @@ import { Benchmark } from '../logging/Benchmark';
import { LoggerFactory } from '../logging/LoggerFactory';
import { Evolve } from '../plugins/Evolve';
import { ArweaveWrapper } from '../utils/ArweaveWrapper';
import { sleep, stripTrailingSlash } from '../utils/utils';
import { getJsonResponse, sleep, stripTrailingSlash } from '../utils/utils';
import {
BenchmarkStats,
Contract,
@@ -32,7 +32,7 @@ import {
import { ArTransfer, ArWallet, emptyTransfer, Tags } from './deploy/CreateContract';
import { InnerWritesEvaluator } from './InnerWritesEvaluator';
import { generateMockVrf } from '../utils/vrf';
import { Signature, CustomSignature } from './Signature';
import { CustomSignature, Signature } from './Signature';
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
import { Mutex } from 'async-mutex';
@@ -334,8 +334,9 @@ export class HandlerBasedContract<State> implements Contract<State> {
options.vrf
);
const response = await this.warpFetchWrapper
.fetch(`${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/sequencer/register`, {
const response = this.warpFetchWrapper.fetch(
`${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/sequencer/register`,
{
method: 'POST',
body: JSON.stringify(interactionTx),
headers: {
@@ -343,21 +344,11 @@ export class HandlerBasedContract<State> implements Contract<State> {
'Content-Type': 'application/json',
Accept: 'application/json'
}
})
.then((res) => {
this.logger.debug(res);
return res.ok ? res.json() : Promise.reject(res);
})
.catch((error) => {
this.logger.error(error);
if (error.body?.message) {
this.logger.error(error.body.message);
}
throw new Error(`Unable to bundle interaction: ${JSON.stringify(error)}`);
});
}
);
return {
bundlrResponse: response,
bundlrResponse: await getJsonResponse(response),
originalTxId: interactionTx.id
};
}

View File

@@ -29,7 +29,7 @@ import { SortKeyCache } from '../cache/SortKeyCache';
import { ContractDefinition, SrcCache } from './ContractDefinition';
import { CustomSignature } from '../contract/Signature';
import { Transaction } from '../utils/types/arweave-types';
import {DEFAULT_LEVEL_DB_LOCATION, WARP_GW_URL} from './WarpFactory';
import { DEFAULT_LEVEL_DB_LOCATION, WARP_GW_URL } from './WarpFactory';
import { LevelDbCache } from '../cache/impl/LevelDbCache';
import { SourceData } from '../contract/deploy/Source';
import { BundlerSigner, DataItem } from '../contract/deploy/DataItem';

View File

@@ -13,7 +13,7 @@ import { Warp, WarpEnvironment } from '../../Warp';
import { TagsParser } from './TagsParser';
import { CacheKey, SortKeyCache, SortKeyCacheResult } from '../../../cache/SortKeyCache';
import { Transaction } from '../../../utils/types/arweave-types';
import { stripTrailingSlash } from '../../../utils/utils';
import { getJsonResponse, stripTrailingSlash } from '../../../utils/utils';
/**
* An extension to {@link ContractDefinitionLoader} that makes use of
@@ -66,20 +66,10 @@ export class WarpGatewayContractDefinitionLoader implements DefinitionLoader {
async doLoad<State>(contractTxId: string, forcedSrcTxId?: string): Promise<ContractDefinition<State>> {
try {
const baseUrl = stripTrailingSlash(this._warp.gwUrl());
const result: ContractDefinition<State> = await fetch(
`${baseUrl}/gateway/contract?txId=${contractTxId}${forcedSrcTxId ? `&srcTxId=${forcedSrcTxId}` : ''}`
)
.then((res) => {
return res.ok ? res.json() : Promise.reject(res);
})
.catch((error) => {
if (error.body?.message) {
this.rLogger.error(error.body.message);
}
throw new Error(
`Unable to retrieve contract data. Warp gateway responded with status ${error.status}:${error.body?.message}`
);
});
const result: ContractDefinition<State> = await getJsonResponse(
fetch(`${baseUrl}/gateway/contract?txId=${contractTxId}${forcedSrcTxId ? `&srcTxId=${forcedSrcTxId}` : ''}`)
);
if (result.srcBinary != null && !(result.srcBinary instanceof Buffer)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
result.srcBinary = Buffer.from((result.srcBinary as any).data);

View File

@@ -2,7 +2,7 @@ import { GQLNodeInterface } from '../../../legacy/gqlResult';
import { Benchmark } from '../../../logging/Benchmark';
import { LoggerFactory } from '../../../logging/LoggerFactory';
import 'warp-isomorphic';
import { stripTrailingSlash } from '../../../utils/utils';
import { getJsonResponse, stripTrailingSlash } from '../../../utils/utils';
import { GW_TYPE, InteractionsLoader } from '../InteractionsLoader';
import { EvaluationOptions } from '../StateEvaluator';
import { Warp } from '../../Warp';
@@ -23,6 +23,14 @@ export const enum SourceType {
BOTH = 'both'
}
type InteractionsResult = {
interactions: GQLNodeInterface[];
paging: {
limit: number;
items: number;
};
};
/**
* The aim of this implementation of the {@link InteractionsLoader} is to make use of
* Warp Gateway ({@link https://github.com/redstone-finance/redstone-sw-gateway})
@@ -75,29 +83,24 @@ export class WarpGatewayInteractionsLoader implements InteractionsLoader {
const url = `${baseUrl}/gateway/v2/interactions-sort-key`;
const response = await fetch(
`${url}?${new URLSearchParams({
contractId: contractId,
...(fromSortKey ? { from: fromSortKey } : ''),
...(toSortKey ? { to: toSortKey } : ''),
page: (++page).toString(),
fromSdk: 'true',
...(this.confirmationStatus && this.confirmationStatus.confirmed ? { confirmationStatus: 'confirmed' } : ''),
...(this.confirmationStatus && this.confirmationStatus.notCorrupted
? { confirmationStatus: 'not_corrupted' }
: ''),
...(effectiveSourceType == SourceType.BOTH ? '' : { source: effectiveSourceType })
})}`
)
.then((res) => {
return res.ok ? res.json() : Promise.reject(res);
})
.catch((error) => {
if (error.body?.message) {
this.logger.error(error.body.message);
}
throw new Error(`Unable to retrieve transactions. Warp gateway responded with status ${error.status}.`);
});
const response = await getJsonResponse<InteractionsResult>(
fetch(
`${url}?${new URLSearchParams({
contractId: contractId,
...(fromSortKey ? { from: fromSortKey } : ''),
...(toSortKey ? { to: toSortKey } : ''),
page: (++page).toString(),
fromSdk: 'true',
...(this.confirmationStatus && this.confirmationStatus.confirmed
? { confirmationStatus: 'confirmed' }
: ''),
...(this.confirmationStatus && this.confirmationStatus.notCorrupted
? { confirmationStatus: 'not_corrupted' }
: ''),
...(effectiveSourceType == SourceType.BOTH ? '' : { source: effectiveSourceType })
})}`
)
);
this.logger.debug(`Loading interactions: page ${page} loaded in ${benchmarkRequestTime.elapsed()}`);
interactions.push(...response.interactions);

View File

@@ -4,7 +4,7 @@ import { Buffer as isomorphicBuffer } from 'warp-isomorphic';
import { LoggerFactory } from '../logging/LoggerFactory';
import { BlockData, NetworkInfoInterface, Transaction } from './types/arweave-types';
import { Warp } from '../core/Warp';
import { stripTrailingSlash } from './utils';
import { getJsonResponse, stripTrailingSlash } from './utils';
export class ArweaveWrapper {
private readonly logger = LoggerFactory.INST.create('ArweaveWrapper');
@@ -115,22 +115,6 @@ export class ArweaveWrapper {
}
private async doFetchInfo<R>(url: string): Promise<R> {
try {
const response = await fetch(url)
.then((res) => {
return res.ok ? res.json() : Promise.reject(res);
})
.catch((error) => {
if (error.body?.message) {
this.logger.error(error.body.message);
}
throw new Error(`Unable to retrieve info. ${error.status}: ${error.body?.message}`);
});
return response;
} catch (e) {
this.logger.error('Error while loading info', e);
throw e;
}
return await getJsonResponse<R>(fetch(url));
}
}

View File

@@ -1,6 +1,6 @@
/* eslint-disable */
import copy from 'fast-copy';
import { Buffer } from 'warp-isomorphic';
import {Buffer} from 'warp-isomorphic';
export const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
@@ -86,3 +86,20 @@ export function bufToBn(buf: Buffer) {
}
export const isBrowser = new Function('try {return this===window;}catch(e){ return false;}');
export async function getJsonResponse<T>(response: Promise<Response>): Promise<T> {
let r: Response;
try {
r = await response;
} catch (e) {
throw new Error(`Error while communicating with gateway: ${JSON.stringify(e)}`);
}
if (!r?.ok) {
const text = await r.text();
throw new Error(`${r.status}: ${text}`);
}
const result = await r.json();
return result as T;
}

View File

@@ -20,18 +20,14 @@ async function main() {
const warp = WarpFactory
.forMainnet({...defaultCacheOptions, inMemory: true})
.useGwUrl("http://localhost:5666/")
let wallet: JWKInterface = readJSON('./.secrets/33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA.json');
try {
const contract = warp
.contract("dD1DuvgM_Vigtnv4vl2H1IYn9CgLvYuhbEWPOL-_4Mw")
const cacheResult = await contract
.setEvaluationOptions({
remoteStateSyncEnabled: true
})
.readState(/*'000001110120,1675251724861,1e1115cbd63aaf205584e2dcb2ca669b40409a0392e0ebf15ff7efac1ecbb24b'*/);
console.log(cacheResult.sortKey);
.contract("G89hzE_P9xCWErQebDuIRdB6zsrmAIjw-WU0g0e8J3M")
await contract.readState();
//console.dir(cacheResult.cachedValue.state, {depth: null});
} catch (e) {
console.error(e);

View File

@@ -20,25 +20,26 @@ async function main() {
try {
const warp = WarpFactory.forMainnet({...defaultCacheOptions, inMemory: true})
.use(new DeployPlugin());
.use(new DeployPlugin())
.useGwUrl("http://localhost:5666/");
const jsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.js'), 'utf8');
const initialState = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.json'), 'utf8');
// case 1 - full deploy, js contract
const { contractTxId, srcTxId } = await warp.deploy({
/* const { contractTxId, srcTxId } = await warp.deploy({
wallet: new ArweaveSigner(wallet),
initState: initialState,
src: jsContractSrc
/*evaluationManifest: {
/!*evaluationManifest: {
evaluationOptions: {
useKVStorage: true
}
}*/
}*!/
});
console.log('contractTxId:', contractTxId);
console.log('srcTxId:', srcTxId);
console.log('srcTxId:', srcTxId);*/
// case 2 - deploy from source, js contract
/*const {contractTxId} = await warp.createContract.deployFromSourceTx({
wallet,
@@ -62,15 +63,17 @@ async function main() {
srcTxId: "5wXT-A0iugP9pWEyw-iTbB0plZ_AbmvlNKyBfGS3AUY",
});*/
const contract = warp.contract<any>(contractTxId).setEvaluationOptions({}).connect(wallet);
const contract = warp.contract<any>('SG9sKOZvKFQ7EcpJU3bS0pQWp2idQf3VY2Ki_5-hDjo').setEvaluationOptions({
sequencerUrl: 'http://localhost:5666/'
}).connect(wallet);
await Promise.all([
contract.writeInteraction<any>({
/* contract.writeInteraction<any>({
function: 'transfer',
target: 'M-mpNeJbg9h7mZ-uHaNsa5jwFFRAq0PsTkNWXJ-ojwI',
qty: 100
}),
contract.writeInteraction<any>({
}),*/
/*contract.writeInteraction<any>({
function: 'transfer',
target: 'M-mpNeJbg9h7mZ-uHaNsa5jwFFRAq0PsTkNWXJ-ojwI',
qty: 100
@@ -81,14 +84,8 @@ async function main() {
qty: 100
}, {
disableBundling: true
})
/*contract.writeInteraction<any>({
function: "mint",
target: 'follows:0xe0',
qty: 100
}),*/
})*/
]);
const {cachedValue} = await contract.readState();
//logger.info("Result", await contract.getStorageValue('33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA'));