feat: [FEATURE] Safe Block Fetch #464

This commit is contained in:
ppedziwiatr
2023-10-20 11:23:26 +02:00
committed by Tadeuchi
parent 249e3707c3
commit 98e3df415a
6 changed files with 83 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "warp-contracts", "name": "warp-contracts",
"version": "1.4.21", "version": "1.4.22-beta.1",
"description": "An implementation of the SmartWeave smart contract protocol.", "description": "An implementation of the SmartWeave smart contract protocol.",
"types": "./lib/types/index.d.ts", "types": "./lib/types/index.d.ts",
"main": "./lib/cjs/index.js", "main": "./lib/cjs/index.js",

View File

@@ -13,6 +13,7 @@ import { LoggerFactory } from '../../../logging/LoggerFactory';
import { DeployPlugin } from 'warp-contracts-plugin-deploy'; import { DeployPlugin } from 'warp-contracts-plugin-deploy';
import { VM2Plugin } from 'warp-contracts-plugin-vm2'; import { VM2Plugin } from 'warp-contracts-plugin-vm2';
import { InteractionCompleteEvent } from '../../../core/modules/StateEvaluator'; import { InteractionCompleteEvent } from '../../../core/modules/StateEvaluator';
import { NetworkCommunicationError } from '../../../utils/utils';
describe('Testing the Profit Sharing Token', () => { describe('Testing the Profit Sharing Token', () => {
let contractSrc: string; let contractSrc: string;
@@ -117,7 +118,7 @@ describe('Testing the Profit Sharing Token', () => {
expect(resultVM.target).toEqual('uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M'); expect(resultVM.target).toEqual('uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M');
}); });
it('should properly dispatch en event', async () => { it('should properly dispatch an event', async () => {
let handlerCalled = false; let handlerCalled = false;
const interactionResult = await pst.writeInteraction({ const interactionResult = await pst.writeInteraction({
function: 'dispatchEvent' function: 'dispatchEvent'
@@ -208,4 +209,36 @@ describe('Testing the Profit Sharing Token', () => {
expect((await pst.currentBalance(walletAddress)).balance).toEqual(startBalance - 100); expect((await pst.currentBalance(walletAddress)).balance).toEqual(startBalance - 100);
}); });
}); });
describe("when loading data from Arweave", () => {
it('should allow to safe fetch from external api', async () => {
const blockData = await arweave.blocks.getCurrent();
await pst.writeInteraction({
function: 'loadBlockData',
height: blockData.height
});
await mineBlock(warp);
const result = await pst.readState();
expect((result.cachedValue.state as any).blocks['' + blockData.height]).toEqual(blockData.indep_hash);
});
// note: this has to be the last test.
it('should stop evaluation on safe fetch error', async () => {
const blockData = await arweave.blocks.getCurrent();
await pst.writeInteraction({
function: 'loadBlockData',
height: blockData.height,
throwError: true
});
await mineBlock(warp);
await expect(pst.readState()).rejects.toThrowError(NetworkCommunicationError);
});
});
}); });

View File

@@ -1,4 +1,4 @@
export function handle(state, action) { export async function handle(state, action) {
const balances = state.balances; const balances = state.balances;
const canEvolve = state.canEvolve; const canEvolve = state.canEvolve;
const input = action.input; const input = action.input;
@@ -37,6 +37,24 @@ export function handle(state, action) {
return { state }; return { state };
} }
if (input.function === 'loadBlockData') {
const height = input.height;
const throwError = input.throwError;
const blockData = await SmartWeave.safeArweaveGet(
throwError
? `/blockkkk/height/${height}`
: `/block/height/${height}`
);
if (!state.blocks) {
state.blocks = {};
}
state.blocks["" + height] = blockData.indep_hash;
return { state };
}
if (input.function === 'balance') { if (input.function === 'balance') {
const target = input.target; const target = input.target;
const ticker = state.ticker; const ticker = state.ticker;

View File

@@ -37,6 +37,24 @@ export async function handle(state, action) {
return {state}; return {state};
} }
if (input.function === 'loadBlockData') {
const height = input.height;
const throwError = input.throwError;
const blockData = await SmartWeave.safeArweaveGet(
throwError
? `/blockkkk/height/${height}`
: `/block/height/${height}`
);
if (!state.blocks) {
state.blocks = {};
}
state.blocks["" + height] = blockData.indep_hash;
return { state };
}
if (input.function === 'dispatchEvent') { if (input.function === 'dispatchEvent') {
return { return {
state, state,

View File

@@ -5,6 +5,7 @@ import { GQLNodeInterface, GQLTagInterface, VrfData } from './gqlResult';
import { CacheKey, SortKeyCache } from '../cache/SortKeyCache'; import { CacheKey, SortKeyCache } from '../cache/SortKeyCache';
import { SortKeyCacheRangeOptions } from '../cache/SortKeyCacheRangeOptions'; import { SortKeyCacheRangeOptions } from '../cache/SortKeyCacheRangeOptions';
import { InteractionState } from '../contract/states/InteractionState'; import { InteractionState } from '../contract/states/InteractionState';
import { safeGet } from '../utils/utils';
/** /**
* *
@@ -47,6 +48,8 @@ export class SmartWeaveGlobal {
owner: string; owner: string;
}; };
unsafeClient: Arweave; unsafeClient: Arweave;
baseArweaveUrl: string;
safeArweaveGet: (input: RequestInfo | URL, init?: RequestInit) => Promise<unknown>;
contracts: { contracts: {
readContractState: (contractId: string) => Promise<any>; readContractState: (contractId: string) => Promise<any>;
@@ -79,6 +82,10 @@ export class SmartWeaveGlobal {
wallets: arweave.wallets, wallets: arweave.wallets,
crypto: arweave.crypto crypto: arweave.crypto
}; };
this.baseArweaveUrl = `${arweave.api.config.protocol}://${arweave.api.config.host}:${arweave.api.config.port}`;
this.safeArweaveGet = async function(query: string) {
return safeGet(`${this.baseArweaveUrl}${query}`);
};
this.evaluationOptions = evaluationOptions; this.evaluationOptions = evaluationOptions;

View File

@@ -115,3 +115,7 @@ export async function getJsonResponse<T>(response: Promise<Response>): Promise<T
throw new NetworkCommunicationError(`Error while parsing json response: ${JSON.stringify(e)}`); throw new NetworkCommunicationError(`Error while parsing json response: ${JSON.stringify(e)}`);
} }
} }
export async function safeGet<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {
return getJsonResponse(fetch(input, init));
}