From 3bae5be4223eb266ca3c144f41edc459af9b2c98 Mon Sep 17 00:00:00 2001 From: ppedziwiatr Date: Fri, 20 Oct 2023 11:23:26 +0200 Subject: [PATCH] feat: [FEATURE] Safe Block Fetch #464 --- package.json | 2 +- src/__tests__/integration/basic/pst.test.ts | 35 ++++++++++++++++++- .../integration/data/token-evolve.js | 20 ++++++++++- src/__tests__/integration/data/token-pst.js | 18 ++++++++++ src/legacy/smartweave-global.ts | 7 ++++ src/utils/utils.ts | 4 +++ 6 files changed, 83 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 23c00c6b..97d23362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "warp-contracts", - "version": "1.4.21", + "version": "1.4.22-beta.1", "description": "An implementation of the SmartWeave smart contract protocol.", "types": "./lib/types/index.d.ts", "main": "./lib/cjs/index.js", diff --git a/src/__tests__/integration/basic/pst.test.ts b/src/__tests__/integration/basic/pst.test.ts index 1574dac3..0ec2767b 100644 --- a/src/__tests__/integration/basic/pst.test.ts +++ b/src/__tests__/integration/basic/pst.test.ts @@ -13,6 +13,7 @@ import { LoggerFactory } from '../../../logging/LoggerFactory'; import { DeployPlugin } from 'warp-contracts-plugin-deploy'; import { VM2Plugin } from 'warp-contracts-plugin-vm2'; import { InteractionCompleteEvent } from '../../../core/modules/StateEvaluator'; +import { NetworkCommunicationError } from '../../../utils/utils'; describe('Testing the Profit Sharing Token', () => { let contractSrc: string; @@ -117,7 +118,7 @@ describe('Testing the Profit Sharing Token', () => { 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; const interactionResult = await pst.writeInteraction({ function: 'dispatchEvent' @@ -208,4 +209,36 @@ describe('Testing the Profit Sharing Token', () => { 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); + }); + }); }); diff --git a/src/__tests__/integration/data/token-evolve.js b/src/__tests__/integration/data/token-evolve.js index 9cf08e02..da5d945d 100644 --- a/src/__tests__/integration/data/token-evolve.js +++ b/src/__tests__/integration/data/token-evolve.js @@ -1,4 +1,4 @@ -export function handle(state, action) { +export async function handle(state, action) { const balances = state.balances; const canEvolve = state.canEvolve; const input = action.input; @@ -37,6 +37,24 @@ export function handle(state, action) { 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') { const target = input.target; const ticker = state.ticker; diff --git a/src/__tests__/integration/data/token-pst.js b/src/__tests__/integration/data/token-pst.js index 9a0960f6..89b43841 100644 --- a/src/__tests__/integration/data/token-pst.js +++ b/src/__tests__/integration/data/token-pst.js @@ -37,6 +37,24 @@ export async function handle(state, action) { 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') { return { state, diff --git a/src/legacy/smartweave-global.ts b/src/legacy/smartweave-global.ts index 725af939..69f382ca 100644 --- a/src/legacy/smartweave-global.ts +++ b/src/legacy/smartweave-global.ts @@ -5,6 +5,7 @@ import { GQLNodeInterface, GQLTagInterface, VrfData } from './gqlResult'; import { CacheKey, SortKeyCache } from '../cache/SortKeyCache'; import { SortKeyCacheRangeOptions } from '../cache/SortKeyCacheRangeOptions'; import { InteractionState } from '../contract/states/InteractionState'; +import { safeGet } from '../utils/utils'; /** * @@ -47,6 +48,8 @@ export class SmartWeaveGlobal { owner: string; }; unsafeClient: Arweave; + baseArweaveUrl: string; + safeArweaveGet: (input: RequestInfo | URL, init?: RequestInit) => Promise; contracts: { readContractState: (contractId: string) => Promise; @@ -79,6 +82,10 @@ export class SmartWeaveGlobal { wallets: arweave.wallets, 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; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index edc0f1a0..e7c69332 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -115,3 +115,7 @@ export async function getJsonResponse(response: Promise): Promise(input: RequestInfo | URL, init?: RequestInit): Promise { + return getJsonResponse(fetch(input, init)); +}