Get missing block fields (#51)

* Decode header and get missing block fields

* Move method to get block data to misc in util

* Remove unnecessary encoding of header data
This commit is contained in:
prathamesh0 2021-11-17 18:14:02 +05:30 committed by nabarun
parent 800ad79baf
commit ec3a8a31a7
7 changed files with 161 additions and 27 deletions

View File

@ -33,6 +33,10 @@ export interface Block {
td: string;
txRoot: string;
receiptRoot: string;
uncleHash: string;
difficulty: string;
gasLimit: string;
gasUsed: string;
}
export interface EventData {
@ -263,9 +267,19 @@ export const createBlock = async (instanceExports: any, blockData: Block): Promi
const parentHashByteArray = await ByteArray.fromHexString(parentHashStringPtr);
const parentHash = await Bytes.fromByteArray(parentHashByteArray);
const uncleHashStringPtr = await __newString(blockData.uncleHash);
const uncleHashByteArray = await ByteArray.fromHexString(uncleHashStringPtr);
const uncleHash = await Bytes.fromByteArray(uncleHashByteArray);
const blockNumberStringPtr = await __newString(blockData.blockNumber);
const blockNumber = await BigInt.fromString(blockNumberStringPtr);
const gasUsedStringPtr = await __newString(blockData.gasUsed);
const gasUsed = await BigInt.fromString(gasUsedStringPtr);
const gasLimitStringPtr = await __newString(blockData.gasLimit);
const gasLimit = await BigInt.fromString(gasLimitStringPtr);
const timestampStringPtr = await __newString(blockData.timestamp);
const blockTimestamp = await BigInt.fromString(timestampStringPtr);
@ -281,35 +295,30 @@ export const createBlock = async (instanceExports: any, blockData: Block): Promi
const receiptsRootByteArray = await ByteArray.fromHexString(receiptRootStringPtr);
const receiptsRoot = await Bytes.fromByteArray(receiptsRootByteArray);
const difficultyStringPtr = await __newString(blockData.difficulty);
const difficulty = await BigInt.fromString(difficultyStringPtr);
const tdStringPtr = await __newString(blockData.td);
const totalDifficulty = await BigInt.fromString(tdStringPtr);
const unclesHashPtr = await Bytes.empty();
const authorPtr = await Address.zero();
const gasUsedPtr = await BigInt.fromI32(0);
const gasLimitPtr = await BigInt.fromI32(0);
const difficultyPtr = await BigInt.fromI32(0);
// Missing fields from watcher in block data:
// unclesHash
// author
// gasUsed
// gasLimit
// difficulty
// size
return await ethereum.Block.__new(
blockHash,
parentHash,
unclesHashPtr,
uncleHash,
authorPtr,
stateRoot,
transactionsRoot,
receiptsRoot,
blockNumber,
gasUsedPtr,
gasLimitPtr,
gasUsed,
gasLimit,
blockTimestamp,
difficultyPtr,
difficulty,
totalDifficulty,
null
);

View File

@ -11,7 +11,7 @@ import { ContractInterface, utils } from 'ethers';
import { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { IndexerInterface } from '@vulcanize/util';
import { IndexerInterface, getFullBlock } from '@vulcanize/util';
import { createBlock, createEvent, getSubgraphConfig, resolveEntityFieldConflicts } from './utils';
import { Context, instantiate } from './loader';
@ -98,13 +98,8 @@ export class GraphWatcher {
async handleEvent (eventData: any) {
const { contract, event, eventSignature, block, tx, eventIndex } = eventData;
const {
allEthHeaderCids: {
nodes: [
blockData
]
}
} = await this._postgraphileClient.getBlocks({ blockHash: block.hash });
// TODO: Use blockData fetched in handleBlock.
const blockData = await getFullBlock(this._postgraphileClient, block.hash);
this._context.event.block = blockData;
@ -150,13 +145,7 @@ export class GraphWatcher {
}
async handleBlock (blockHash: string) {
const {
allEthHeaderCids: {
nodes: [
blockData
]
}
} = await this._postgraphileClient.getBlocks({ blockHash });
const blockData = await getFullBlock(this._postgraphileClient, blockHash);
// Call block handler(s) for each contract.
for (const dataSource of this._dataSources) {

View File

@ -82,6 +82,16 @@ export class EthClient {
);
}
async getFullBlocks ({ blockNumber, blockHash }: { blockNumber?: number, blockHash?: string }): Promise<any> {
return this._graphqlClient.query(
ethQueries.getFullBlocks,
{
blockNumber,
blockHash
}
);
}
async getBlockByHash (blockHash?: string): Promise<any> {
const { block } = await this._graphqlClient.query(ethQueries.getBlockByHash, { blockHash });
block.number = parseInt(block.number, 16);

View File

@ -82,6 +82,30 @@ query allEthHeaderCids($blockNumber: BigInt, $blockHash: String) {
}
`;
export const getFullBlocks = gql`
query allEthHeaderCids($blockNumber: BigInt, $blockHash: String) {
allEthHeaderCids(condition: { blockNumber: $blockNumber, blockHash: $blockHash }) {
nodes {
cid
blockNumber
blockHash
parentHash
timestamp
stateRoot
td
txRoot
receiptRoot
uncleRoot
bloom
blockByMhKey {
key
data
}
}
}
}
`;
export const getBlockByHash = gql`
query block($blockHash: Bytes32) {
block(hash: $blockHash) {
@ -133,6 +157,7 @@ export default {
getLogs,
getBlockWithTransactions,
getBlocks,
getFullBlocks,
getBlockByHash,
subscribeBlocks,
subscribeTransactions

View File

@ -10,6 +10,7 @@
"ethers": "^5.2.0",
"fs-extra": "^10.0.0",
"lodash": "^4.17.21",
"multiformats": "^9.4.8",
"pg-boss": "^6.1.0",
"toml": "^3.0.0"
},
@ -21,6 +22,7 @@
"@vulcanize/cache": "^0.1.0",
"@vulcanize/ipld-eth-client": "^0.1.0",
"apollo-server-express": "^2.25.0",
"decimal.js": "^10.3.1",
"eslint": "^7.27.0",
"eslint-config-semistandard": "^15.0.1",
"eslint-config-standard": "^16.0.3",

61
packages/util/src/eth.ts Normal file
View File

@ -0,0 +1,61 @@
import debug from 'debug';
import { utils } from 'ethers';
const log = debug('vulcanize:eth');
function decodeInteger(value : string, defaultValue: BigInt): BigInt
function decodeInteger(value : string) : BigInt | undefined
function decodeInteger (value : string, defaultValue?: BigInt): BigInt | undefined {
if (value === undefined || value === null || value.length === 0) return defaultValue;
if (value === '0x') return BigInt(0);
return BigInt(value);
}
function decodeNumber(value : string, defaultValue: number): number
function decodeNumber(value : string) : number | undefined
function decodeNumber (value : string, defaultValue?: number): number | undefined {
if (value === undefined || value === null || value.length === 0) return defaultValue;
if (value === '0x') return 0;
return Number(value);
}
function decodeHex (hex: string): any {
return Buffer.from(hex.slice(2), 'hex');
}
export function decodeHeader (rlp : Uint8Array): any {
try {
const data = utils.RLP.decode(rlp);
try {
return {
Parent: decodeHex(data[0]),
UnclesDigest: decodeHex(data[1]),
Beneficiary: decodeHex(data[2]),
StateRoot: decodeHex(data[4]),
TxRoot: decodeHex(data[4]),
RctRoot: decodeHex(data[5]),
Bloom: decodeHex(data[6]),
Difficulty: decodeInteger(data[7], BigInt(0)),
Number: decodeInteger(data[8], BigInt(0)),
GasLimit: decodeInteger(data[9], BigInt(0)),
GasUsed: decodeInteger(data[10], BigInt(0)),
Time: decodeNumber(data[11]) || 0,
Extra: decodeHex(data[12]),
MixDigest: decodeHex(data[13]),
Nonce: decodeInteger(data[14], BigInt(0)),
BaseFee: decodeInteger(data[15])
};
} catch (error: any) {
log(error);
return undefined;
}
} catch (error: any) {
log(error);
return undefined;
}
}
export function decodeData (hexLiteral: string): Uint8Array {
return Uint8Array.from(Buffer.from(hexLiteral.slice(2), 'hex'));
}

View File

@ -9,10 +9,13 @@ import { hideBin } from 'yargs/helpers';
import { utils, getDefaultProvider, providers } from 'ethers';
import Decimal from 'decimal.js';
import { EthClient } from '@vulcanize/ipld-eth-client';
import { DEFAULT_CONFIG_PATH } from './constants';
import { Config } from './config';
import { JobQueue } from './job-queue';
import { GraphDecimal } from './graph-decimal';
import * as EthDecoder from './eth';
/**
* Method to wait for specified time.
@ -169,3 +172,38 @@ class CustomFormatter extends providers.Formatter {
throw new Error('invalid blockTag');
}
}
export const getFullBlock = async (ethClient: EthClient, blockHash: string): Promise<any> => {
const {
allEthHeaderCids: {
nodes: [
fullBlock
]
}
} = await ethClient.getFullBlocks({ blockHash });
assert(fullBlock.blockByMhKey);
// Deecode the header data.
const header = EthDecoder.decodeHeader(EthDecoder.decodeData(fullBlock.blockByMhKey.data));
assert(header);
// TODO:
// 1. Get author
// 2. Calculate size
return {
cid: fullBlock.cid,
blockNumber: fullBlock.blockNumber,
blockHash: fullBlock.blockHash,
parentHash: fullBlock.parentHash,
timestamp: fullBlock.timestamp,
stateRoot: fullBlock.stateRoot,
td: fullBlock.td,
txRoot: fullBlock.txRoot,
receiptRoot: fullBlock.receiptRoot,
uncleHash: fullBlock.uncleRoot,
difficulty: header.Difficulty.toString(),
gasLimit: header.GasLimit.toString(),
gasUsed: header.GasUsed.toString()
};
};