diff --git a/packages/graph-node/assembly/index.ts b/packages/graph-node/assembly/index.ts index 99084968..acf593d7 100644 --- a/packages/graph-node/assembly/index.ts +++ b/packages/graph-node/assembly/index.ts @@ -72,3 +72,11 @@ export class Bar { return this.prop; } } + +export function testLog (): void { + log.debug('Debug message: {}, {}', ['value1', 'value2']); + log.info('Info message: {}', ['value1', 'value2']); + log.warning('Warning message', []); + log.error('Error message', []); + log.critical('Critical message', []); +} diff --git a/packages/graph-node/package.json b/packages/graph-node/package.json index 6b5cada3..b604a1bd 100644 --- a/packages/graph-node/package.json +++ b/packages/graph-node/package.json @@ -30,7 +30,7 @@ "asbuild:debug": "asc assembly/index.ts --lib ./node_modules --exportRuntime --target debug --runPasses asyncify", "asbuild:release": "asc assembly/index.ts --lib ./node_modules --exportRuntime --target release --runPasses asyncify", "asbuild": "yarn asbuild:debug && yarn asbuild:release", - "test": "yarn asbuild:debug && mocha src/**/*.test.ts", + "test": "yarn asbuild:debug && DEBUG=vulcanize:* mocha src/**/*.test.ts", "build:example": "cd test/subgraph/example1 && yarn && yarn build", "watch": "DEBUG=vulcanize:* nodemon --watch src src/watcher.ts", "compare-entity": "DEBUG=vulcanize:* ts-node src/cli/compare/compare-entity.ts" diff --git a/packages/graph-node/src/loader.test.ts b/packages/graph-node/src/loader.test.ts index f25b2914..a9217aa8 100644 --- a/packages/graph-node/src/loader.test.ts +++ b/packages/graph-node/src/loader.test.ts @@ -64,4 +64,11 @@ describe('wasm loader tests', () => { const bar = await Bar.__new(await __newString('test')); expect(__getString(await bar.prop)).to.equal('test'); }); + + it('should log messages', async () => { + const { testLog } = exports; + + // Should print all log messages for different levels. + await testLog(); + }); }); diff --git a/packages/graph-node/src/loader.ts b/packages/graph-node/src/loader.ts index b24c6c96..b119a2a0 100644 --- a/packages/graph-node/src/loader.ts +++ b/packages/graph-node/src/loader.ts @@ -13,11 +13,12 @@ import { } from 'ethers'; import JSONbig from 'json-bigint'; import BN from 'bn.js'; +import debug from 'debug'; import loader from '@vulcanize/assemblyscript/lib/loader'; import { IndexerInterface } from '@vulcanize/util'; -import { TypeId } from './types'; +import { TypeId, Level } from './types'; import { Block, fromEthereumValue, toEthereumValue, resolveEntityFieldConflicts, GraphDecimal, digitsToString } from './utils'; import { Database } from './database'; @@ -52,6 +53,8 @@ export interface Context { } } +const log = debug('vulcanize:graph-node'); + export const instantiate = async ( database: Database, indexer: IndexerInterface, @@ -76,7 +79,7 @@ export const instantiate = async ( return null; } - return database.toGraphEntity(exports, entityName, entityData); + return database.toGraphEntity(instanceExports, entityName, entityData); }, 'store.set': async (entity: number, id: number, data: number) => { const entityName = __getString(entity); @@ -84,7 +87,7 @@ export const instantiate = async ( const entityInstance = await Entity.wrap(data); assert(context.event.block); - let dbData = await database.fromGraphEntity(exports, context.event.block, entityName, entityInstance); + let dbData = await database.fromGraphEntity(instanceExports, context.event.block, entityName, entityInstance); await database.saveEntity(entityName, dbData); // Resolve any field name conflicts in the dbData for auto-diff. @@ -103,8 +106,8 @@ export const instantiate = async ( await indexer.createDiffStaged(dataSource.address, context.event.block.blockHash, diffData); }, - 'log.log': (_: number, msg: number) => { - console.log('log.log', __getString(msg)); + 'log.log': (level: number, msg: number) => { + log('log %s | %s', Level[level], __getString(msg)); }, 'test.asyncMethod': async () => { @@ -148,7 +151,7 @@ export const instantiate = async ( try { const functionParamsPromise = functionParams.map(async param => { const ethereumValue = await ethereum.Value.wrap(param); - return fromEthereumValue(exports, ethereumValue); + return fromEthereumValue(instanceExports, ethereumValue); }); functionParams = await Promise.all(functionParamsPromise); @@ -172,7 +175,7 @@ export const instantiate = async ( output: any, index: number ) => toEthereumValue( - exports, + instanceExports, output, result[index] ) @@ -214,8 +217,14 @@ export const instantiate = async ( return ptr; }, - 'typeConversion.bigIntToHex': () => { - console.log('index typeConversion.bigIntToHex'); + 'typeConversion.bigIntToHex': async (bigInt: number) => { + const bigIntInstance = await BigInt.wrap(bigInt); + const bigIntString = await bigIntInstance.toString(); + + const bigNumber = BigNumber.from(__getString(bigIntString)); + const bigNumberHex = bigNumber.toHexString(); + + return __newString(bigNumberHex); }, 'typeConversion.bytesToHex': async (bytes: number) => { @@ -225,11 +234,19 @@ export const instantiate = async ( return ptr; }, - 'typeConversion.bytesToString': () => { - console.log('index typeConversion.bytesToString'); + 'typeConversion.bytesToString': async (bytes: number) => { + const byteArray = __getArray(bytes); + const string = utils.toUtf8String(byteArray); + const ptr = await __newString(string); + + return ptr; }, - 'typeConversion.bytesToBase58': () => { - console.log('index typeConversion.bytesToBase58'); + 'typeConversion.bytesToBase58': async (n: number) => { + const uint8Array = __getArray(n); + const string = utils.base58.encode(uint8Array); + const ptr = await __newString(string); + + return ptr; } }, numbers: { @@ -464,20 +481,80 @@ export const instantiate = async ( return remainderBigInt; }, - 'bigInt.bitOr': () => { - console.log('bigInt.bitOr'); + 'bigInt.bitOr': async (x: number, y: number) => { + // Create a bigNumber x. + const xBigInt = await BigInt.wrap(x); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); + + // Create a bigNumber y. + const yBigInt = await BigInt.wrap(y); + const yStringPtr = await yBigInt.toString(); + const yBigNumber = BigNumber.from(__getString(yStringPtr)); + + // Perform the bigNumber bit or operation. + const res = xBigNumber.or(yBigNumber); + const ptr = await __newString(res.toString()); + const resBigInt = BigInt.fromString(ptr); + + return resBigInt; }, - 'bigInt.bitAnd': () => { - console.log('bigInt.bitAnd'); + 'bigInt.bitAnd': async (x: number, y: number) => { + // Create a bigNumber x. + const xBigInt = await BigInt.wrap(x); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); + + // Create a bigNumber y. + const yBigInt = await BigInt.wrap(y); + const yStringPtr = await yBigInt.toString(); + const yBigNumber = BigNumber.from(__getString(yStringPtr)); + + // Perform the bigNumber bit and operation. + const res = xBigNumber.and(yBigNumber); + const ptr = await __newString(res.toString()); + const resBigInt = BigInt.fromString(ptr); + + return resBigInt; }, - 'bigInt.leftShift': () => { - console.log('bigInt.leftShift'); + 'bigInt.leftShift': async (x: number, y: number) => { + // Create a bigNumber x. + const xBigInt = await BigInt.wrap(x); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); + + // Perform the bigNumber left shift operation. + const res = xBigNumber.shl(y); + const ptr = await __newString(res.toString()); + const resBigInt = BigInt.fromString(ptr); + + return resBigInt; }, - 'bigInt.rightShift': () => { - console.log('bigInt.rightShift'); + 'bigInt.rightShift': async (x: number, y: number) => { + // Create a bigNumber x. + const xBigInt = await BigInt.wrap(x); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); + + // Perform the bigNumber right shift operation. + const res = xBigNumber.shr(y); + const ptr = await __newString(res.toString()); + const resBigInt = BigInt.fromString(ptr); + + return resBigInt; }, - 'bigInt.pow': () => { - console.log('bigInt.pow'); + 'bigInt.pow': async (x: number, y: number) => { + // Create a bigNumber x. + const xBigInt = await BigInt.wrap(x); + const xStringPtr = await xBigInt.toString(); + const xBigNumber = BigNumber.from(__getString(xStringPtr)); + + // Perform the bigNumber pow operation. + const res = xBigNumber.pow(y); + const ptr = await __newString(res.toString()); + const resBigInt = BigInt.fromString(ptr); + + return resBigInt; } }, datasource: { @@ -490,17 +567,17 @@ export const instantiate = async ( }; const instance = await loader.instantiate(buffer, imports); - const { exports } = instance; + const { exports: instanceExports } = instance; - const { __getString, __newString, __getArray, __newArray } = exports; + const { __getString, __newString, __getArray, __newArray } = instanceExports; // TODO: Assign from types file generated by graph-cli - const getIdOfType: idOfType = exports.id_of_type as idOfType; - const BigDecimal: any = exports.BigDecimal as any; - const BigInt: any = exports.BigInt as any; - const Address: any = exports.Address as any; - const ethereum: any = exports.ethereum as any; - const Entity: any = exports.Entity as any; + const getIdOfType: idOfType = instanceExports.id_of_type as idOfType; + const BigDecimal: any = instanceExports.BigDecimal as any; + const BigInt: any = instanceExports.BigInt as any; + const Address: any = instanceExports.Address as any; + const ethereum: any = instanceExports.ethereum as any; + const Entity: any = instanceExports.Entity as any; return instance; }; diff --git a/packages/graph-node/src/numbers.test.ts b/packages/graph-node/src/numbers.test.ts index 69ab2036..cc5c7908 100644 --- a/packages/graph-node/src/numbers.test.ts +++ b/packages/graph-node/src/numbers.test.ts @@ -125,6 +125,41 @@ describe('numbers wasm tests', () => { expect(__getString(ptr)).to.equal('1283174719'); }); + it('should execute bigInt bitOr API', async () => { + const { testBigIntBitOr, __getString, __newString } = exports; + + const ptr = await testBigIntBitOr(await __newString('2315432122132354'), await __newString('5465265645')); + expect(__getString(ptr)).to.equal('2315433208543215'); + }); + + it('should execute bigInt bitAnd API', async () => { + const { testBigIntBitAnd, __getString, __newString } = exports; + + const ptr = await testBigIntBitAnd(await __newString('2315432122132354'), await __newString('5465265645')); + expect(__getString(ptr)).to.equal('4378854784'); + }); + + it('should execute bigInt leftShift API', async () => { + const { testBigIntLeftShift, __getString, __newString } = exports; + + const ptr = await testBigIntLeftShift(await __newString('2315432122132354'), 3); + expect(__getString(ptr)).to.equal('18523456977058832'); + }); + + it('should execute bigInt rightShift API', async () => { + const { testBigIntRightShift, __getString, __newString } = exports; + + const ptr = await testBigIntRightShift(await __newString('2315432122132354'), 3); + expect(__getString(ptr)).to.equal('289429015266544'); + }); + + it('should execute bigInt pow API', async () => { + const { testBigIntPow, __getString, __newString } = exports; + + const ptr = await testBigIntPow(await __newString('2315432'), 5); + expect(__getString(ptr)).to.equal('66551853520489467542782546706432'); + }); + it('should execute bigDecimal toString API', async () => { const { testBigDecimalToString, __newString, __getString } = exports; diff --git a/packages/graph-node/src/type-conversion.test.ts b/packages/graph-node/src/type-conversion.test.ts index e38153b0..be7ab941 100644 --- a/packages/graph-node/src/type-conversion.test.ts +++ b/packages/graph-node/src/type-conversion.test.ts @@ -4,6 +4,7 @@ import path from 'path'; import { expect } from 'chai'; +import { utils, BigNumber } from 'ethers'; import { instantiate } from './loader'; import { getTestDatabase, getTestIndexer } from '../test/utils'; @@ -56,4 +57,39 @@ describe('typeConversion wasm tests', () => { const ptr = await testStringToH160(); expect(__getString(ptr)).to.equal('0xafad925b5eae1e370196cba39893e858ff7257d5'); }); + + it('should execute typeConversion bigIntToHex API', async () => { + const { testBigIntToHex, __getString, __getArray, __newString } = exports; + + // Using smaller to also test with BigInt.fromI32 + const bigNumber = BigNumber.from('2342353'); + const value = await __newString(bigNumber.toString()); + + const ptr = await testBigIntToHex(value); + const ptrs = __getArray(ptr); + expect(__getString(ptrs[0])).to.equal(__getString(ptrs[1])); + expect(__getString(ptrs[0])).to.equal(bigNumber.toHexString()); + }); + + it('should execute typeConversion bytesToString API', async () => { + const { testBytesToString, __getString, __newString } = exports; + + const testString = 'test string'; + const value = await __newString(testString); + + const ptr = await testBytesToString(value); + expect(__getString(ptr)).to.equal(testString); + }); + + it('should execute typeConversion bytesToBase58 API', async () => { + const { testBytesToBase58, __getString, __newString } = exports; + + const testString = 'test base58'; + const value = await __newString(testString); + + const ptr = await testBytesToBase58(value); + + const base58String = utils.base58.encode(utils.toUtf8Bytes(testString)); + expect(__getString(ptr)).to.equal(base58String); + }); }); diff --git a/packages/graph-node/src/types.ts b/packages/graph-node/src/types.ts index 977e66c2..a4a23144 100644 --- a/packages/graph-node/src/types.ts +++ b/packages/graph-node/src/types.ts @@ -2,7 +2,7 @@ // Copyright 2021 Vulcanize, Inc. // -// TypeId from https://github.com/graphprotocol/graph-ts/blob/master/global/global.ts +// Enum types from @graphprotocol/graph-ts. export enum TypeId { String = 0, ArrayBuffer = 1, @@ -58,7 +58,6 @@ export enum TypeId { ArrayBigDecimal = 51, } -// ethereum ValueKind from https://github.com/graphprotocol/graph-ts/blob/master/chain/ethereum.ts#L13 export enum EthereumValueKind { ADDRESS = 0, FIXED_BYTES = 1, @@ -72,7 +71,6 @@ export enum EthereumValueKind { TUPLE = 9, } -// ValueKind from https://github.com/graphprotocol/graph-ts/blob/master/common/value.ts#L8 export enum ValueKind { STRING = 0, INT = 1, @@ -83,3 +81,11 @@ export enum ValueKind { BYTES = 6, BIGINT = 7, } + +export enum Level { + CRITICAL = 0, + ERROR = 1, + WARNING = 2, + INFO = 3, + DEBUG = 4, +} diff --git a/packages/graph-node/src/watcher.ts b/packages/graph-node/src/watcher.ts index 86937d91..db175811 100644 --- a/packages/graph-node/src/watcher.ts +++ b/packages/graph-node/src/watcher.ts @@ -129,7 +129,7 @@ export class GraphWatcher { return; } - const { instance: { exports }, contractInterface } = this._dataSourceMap[contract]; + const { instance: { exports: instanceExports }, contractInterface } = this._dataSourceMap[contract]; const eventFragment = contractInterface.getEvent(eventSignature); @@ -142,9 +142,9 @@ export class GraphWatcher { }; // Create ethereum event to be passed to the wasm event handler. - const ethereumEvent = await createEvent(exports, contract, data); + const ethereumEvent = await createEvent(instanceExports, contract, data); - await exports[eventHandler.handler](ethereumEvent); + await instanceExports[eventHandler.handler](ethereumEvent); } async handleBlock (blockHash: string) { @@ -159,14 +159,14 @@ export class GraphWatcher { continue; } - const { instance: { exports } } = this._dataSourceMap[dataSource.source.address]; + const { instance: { exports: instanceExports } } = this._dataSourceMap[dataSource.source.address]; // Create ethereum block to be passed to a wasm block handler. - const ethereumBlock = await createBlock(exports, blockData); + const ethereumBlock = await createBlock(instanceExports, blockData); // Call all the block handlers one after the another for a contract. const blockHandlerPromises = dataSource.mapping.blockHandlers.map(async (blockHandler: any): Promise => { - await exports[blockHandler.handler](ethereumBlock); + await instanceExports[blockHandler.handler](ethereumBlock); }); await Promise.all(blockHandlerPromises); diff --git a/packages/graph-node/test/subgraph/example1/src/mapping.ts b/packages/graph-node/test/subgraph/example1/src/mapping.ts index edbd24f0..d6304054 100644 --- a/packages/graph-node/test/subgraph/example1/src/mapping.ts +++ b/packages/graph-node/test/subgraph/example1/src/mapping.ts @@ -112,7 +112,6 @@ export function testGetEthCall (): void { log.debug('In test get eth call', []); // Bind the contract to the address that emitted the event. - // TODO: Address.fromString throws error in WASM module instantiation. const contractAddress = dataSource.address(); const contract = Example1.bind(contractAddress); @@ -170,6 +169,26 @@ export function testBytesToHex (): string { return res; } +export function testBytesToString (value: string): string { + log.debug('In test bytesToString', []); + + const byteArray = ByteArray.fromUTF8(value); + const res = byteArray.toString(); + log.debug('typeConversion.bytesToString result: {}', [res]); + + return res; +} + +export function testBytesToBase58 (value: string): string { + log.debug('In test bytesToBase58', []); + + const byteArray = ByteArray.fromUTF8(value); + const res = byteArray.toBase58(); + log.debug('typeConversion.bytesToBase58 result: {}', [res]); + + return res; +} + export function testBigIntToString (): string { log.debug('In test bigIntToString', []); @@ -325,6 +344,61 @@ export function testBigIntMod (value1: string, value2: string): string { return res.toString(); } +export function testBigIntBitOr (value1: string, value2: string): string { + log.debug('In test bigInt.bitOr', []); + + const bigInt1 = BigInt.fromString(value1); + const bigInt2 = BigInt.fromString(value2); + + const res = bigInt1 | bigInt2; + log.debug('bigInt.bitOr result: {}', [res.toString()]); + return res.toString(); +} + +export function testBigIntBitAnd (value1: string, value2: string): string { + log.debug('In test bigInt.bitAnd', []); + + const bigInt1 = BigInt.fromString(value1); + const bigInt2 = BigInt.fromString(value2); + + const res = bigInt1 & bigInt2; + log.debug('bigInt.bitAnd result: {}', [res.toString()]); + return res.toString(); +} + +export function testBigIntLeftShift (value1: string, value2: u8): string { + log.debug('In test bigInt.leftShift', []); + + const bigInt1 = BigInt.fromString(value1); + const bits = value2; + + const res = bigInt1 << bits; + log.debug('bigInt.leftShift result: {}', [res.toString()]); + return res.toString(); +} + +export function testBigIntRightShift (value1: string, value2: u8): string { + log.debug('In test bigInt.RightShift', []); + + const bigInt1 = BigInt.fromString(value1); + const bits = value2; + + const res = bigInt1 >> bits; + log.debug('bigInt.RightShift result: {}', [res.toString()]); + return res.toString(); +} + +export function testBigIntPow (value1: string, value2: u8): string { + log.debug('In test bigInt.pow', []); + + const bigInt1 = BigInt.fromString(value1); + const exp = value2; + + const res = bigInt1.pow(exp); + log.debug('bigInt.pow result: {}', [res.toString()]); + return res.toString(); +} + export function testBigIntFromString (value: string): string { log.debug('In test bigInt.fromString', []); @@ -354,3 +428,20 @@ export function testBigIntWithI32 (value: string): string[] { return [res1, res2, res3]; } + +export function testBigIntToHex (value: string): string[] { + log.debug('In testBigIntToHex', []); + + const variableI32: i32 = parseInt(value) as i32; + + const bigInt1 = BigInt.fromI32(variableI32); + const bigInt2 = BigInt.fromString(value); + + const res1 = bigInt1.toHex(); + log.debug('bigInt.toHex result 1: {}', [res1]); + + const res2 = bigInt2.toHex(); + log.debug('bigInt.toHex result 2: {}', [res2]); + + return [res1, res2]; +} diff --git a/packages/util/src/events.ts b/packages/util/src/events.ts index 0b928713..0cf0b518 100644 --- a/packages/util/src/events.ts +++ b/packages/util/src/events.ts @@ -88,11 +88,11 @@ export class EventWatcher { switch (kind) { case JOB_KIND_INDEX: - this._handleIndexingComplete(data); + await this._handleIndexingComplete(data); break; case JOB_KIND_PRUNE: - this._handlePruningComplete(data); + await this._handlePruningComplete(data); break; default: