Skip to content

Commit

Permalink
Bring getBlockBy... into execution-api schema compliance (#100)
Browse files Browse the repository at this point in the history
Bring the getBlockBy... apis into compliance
* always use hex string instead of numbers
* use full hashes
* supply required fields

Signed-off-by: Danno Ferrin <[email protected]>
  • Loading branch information
shemnon authored May 26, 2022
1 parent 95bc140 commit e12eff6
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 68 deletions.
45 changes: 27 additions & 18 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*/

import { Eth } from '../index';
import { Status } from '@hashgraph/sdk';
import { ContractId, Status } from '@hashgraph/sdk';
import { BigNumber } from '@hashgraph/sdk/lib/Transfer';
import { Logger } from 'pino';
import { Block, CachedBlock, Transaction } from './model';
Expand All @@ -40,6 +40,8 @@ export class EthImpl implements Eth {
static emptyHex = '0x';
static zeroHex = '0x0';
static emptyArrayHex = '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347';
static zeroAddressHex = '0x0000000000000000000000000000000000000000';
static emptyBloom = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
static defaultGas = 0x3d0900;

/**
Expand Down Expand Up @@ -568,10 +570,13 @@ export class EthImpl implements Eth {
return null;
}

const maxPriorityFee = contractResult.max_priority_fee_per_gas === EthImpl.emptyHex ? undefined : contractResult.max_priority_fee_per_gas;
const maxFee = contractResult.max_fee_per_gas === EthImpl.emptyHex ? undefined : contractResult.max_fee_per_gas;
const rSig = contractResult.r === null ? null : contractResult.r.substring(0, 66);
const sSig = contractResult.s === null ? null : contractResult.s.substring(0, 66);

return new Transaction({
accessList: contractResult.access_list,
accessList: undefined, // we don't support access lists, so punt for now
blockHash: contractResult.block_hash.substring(0, 66),
blockNumber: EthImpl.numberTo0x(contractResult.block_number),
chainId: contractResult.chain_id,
Expand All @@ -580,8 +585,8 @@ export class EthImpl implements Eth {
gasPrice: contractResult.gas_price,
hash: contractResult.hash.substring(0, 66),
input: contractResult.function_parameters,
maxPriorityFeePerGas: contractResult.max_priority_fee_per_gas,
maxFeePerGas: contractResult.max_fee_per_gas,
maxPriorityFeePerGas: maxPriorityFee,
maxFeePerGas: maxFee,
nonce: contractResult.nonce,
r: rSig,
s: sSig,
Expand Down Expand Up @@ -610,14 +615,18 @@ export class EthImpl implements Eth {
receiptResponse.max_fee_per_gas === undefined || receiptResponse.max_fee_per_gas == '0x'
? receiptResponse.gas_price
: receiptResponse.max_fee_per_gas;
const createdContract =
receiptResponse.created_contract_ids.length > 0
? EthImpl.prepend0x(ContractId.fromString(receiptResponse.created_contract_ids[0]).toSolidityAddress())
: undefined;
const answer = {
blockHash: receiptResponse.block_hash.substring(0, 66),
blockNumber: EthImpl.numberTo0x(receiptResponse.block_number),
from: receiptResponse.from,
to: receiptResponse.to,
cumulativeGasUsed: EthImpl.numberTo0x(receiptResponse.block_gas_used),
gasUsed: EthImpl.numberTo0x(receiptResponse.gas_used),
contractAddress: undefined, // FIXME translate from receiptResponse.created_contract_ids when `to` is empty,
contractAddress: createdContract,
logs: receiptResponse.logs,
logsBloom: receiptResponse.bloom,
transactionHash: receiptResponse.hash,
Expand Down Expand Up @@ -735,23 +744,23 @@ export class EthImpl implements Eth {

const blockHash = blockResponse.hash.substring(0, 66);
return new Block({
baseFeePerGas: 0,
baseFeePerGas: EthImpl.numberTo0x(0), //TODO should be gasPrice
difficulty: EthImpl.zeroHex,
extraData: EthImpl.emptyHex,
gasLimit: maxGasLimit,
gasUsed: gasUsed,
gasLimit: EthImpl.numberTo0x(maxGasLimit),
gasUsed: EthImpl.numberTo0x(gasUsed),
hash: blockHash,
logsBloom: blockResponse.logsBloom,
miner: EthImpl.emptyHex,
mixHash: EthImpl.emptyHex,
nonce: EthImpl.emptyHex,
number: blockResponse.number,
logsBloom: EthImpl.emptyBloom, //TODO calculate full block boom in mirror node
miner: EthImpl.zeroAddressHex,
mixHash: EthImpl.emptyArrayHex,
nonce: EthImpl.zeroHex,
number: EthImpl.numberTo0x(blockResponse.number),
parentHash: blockResponse.previous_hash.substring(0, 66),
receiptsRoot: EthImpl.emptyHex,
timestamp: timestamp,
receiptsRoot: EthImpl.emptyArrayHex,
timestamp: EthImpl.numberTo0x(Number(timestamp)),
sha3Uncles: EthImpl.emptyArrayHex,
size: blockResponse.size,
stateRoot: EthImpl.emptyHex,
size: EthImpl.numberTo0x(blockResponse.size | 0),
stateRoot: EthImpl.emptyArrayHex,
totalDifficulty: EthImpl.zeroHex,
transactions: showDetails ? transactionObjects : transactionHashes,
transactionsRoot: blockHash,
Expand Down Expand Up @@ -785,7 +794,7 @@ export class EthImpl implements Eth {
const rSig = contractResultDetails.r === null ? null : contractResultDetails.r.substring(0, 66);
const sSig = contractResultDetails.s === null ? null : contractResultDetails.s.substring(0, 66);
return new Transaction({
accessList: contractResultDetails.access_list,
accessList: undefined, // we don't support access lists for now, so punt
blockHash: contractResultDetails.block_hash.substring(0, 66),
blockNumber: EthImpl.numberTo0x(contractResultDetails.block_number),
chainId: contractResultDetails.chain_id,
Expand Down
9 changes: 7 additions & 2 deletions packages/relay/src/lib/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export class Receipt {
}

export class Transaction {
public readonly accessList!: string[] | null;
public readonly accessList!: AccessListEntry[] | null;
public readonly blockHash!: string | null;
public readonly blockNumber!: string | null;
public readonly chainId!: string;
Expand Down Expand Up @@ -178,4 +178,9 @@ export class Transaction {
this.v = args.v;
this.value = args.value;
}
}
}

export declare class AccessListEntry {
readonly address: string;
readonly storageKeys: string[];
}
100 changes: 52 additions & 48 deletions packages/relay/tests/lib/eth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ describe('Eth calls using MirrorNode', async function () {
const blockNumberHex = `0x${blockNumber.toString(16)}`;
const blockTransactionCount = 77;
const maxGasLimit = 250000;
const maxGasLimitHex = EthImpl.numberTo0x(maxGasLimit);
const firstTransactionTimestampSeconds = '1653077547';
const firstTransactionTimestampSecondsHex = EthImpl.numberTo0x(Number(firstTransactionTimestampSeconds));
const contractAddress1 = '0x000000000000000000000000000000000000055f';
const contractTimestamp1 = `${firstTransactionTimestampSeconds}.983983199`;
const contractHash1 = '0x4a563af33c4871b51a8b108aa2fe1dd5280a30dfb7236170ae5e5e7957eb6392';
Expand Down Expand Up @@ -197,7 +199,7 @@ describe('Eth calls using MirrorNode', async function () {
};

const results = defaultContractResults.results;
const totalGasUsed = results[0].gas_used + results[1].gas_used;
const totalGasUsed = EthImpl.numberTo0x(results[0].gas_used + results[1].gas_used);

this.afterEach(() => {
mock.resetHandlers();
Expand Down Expand Up @@ -234,31 +236,31 @@ describe('Eth calls using MirrorNode', async function () {
mock.onGet(`contracts/results?timestamp=gte:${defaultBlock.timestamp.from}&timestamp=lte:${defaultBlock.timestamp.to}`).reply(200, defaultContractResults);
mock.onGet(`contracts/${contractAddress1}/results/${contractTimestamp1}`).reply(200, defaultDetailedContractResults);
mock.onGet(`contracts/${contractAddress2}/results/${contractTimestamp2}`).reply(200, defaultDetailedContractResults);
const result = await ethImpl.getBlockByNumber(blockNumber, false);
const result = await ethImpl.getBlockByNumber(EthImpl.numberTo0x(blockNumber), false);
expect(result).to.exist;
if (result == null) return;

// verify aggregated info
expect(result.hash).equal(blockHashTrimmed);
expect(result.gasUsed).equal(totalGasUsed);
expect(result.gasLimit).equal(maxGasLimit);
expect(result.number).equal(blockNumber);
expect(result.gasLimit).equal(maxGasLimitHex);
expect(result.number).equal(blockNumberHex);
expect(result.parentHash).equal(blockHashPreviousTrimmed);
expect(result.timestamp).equal(firstTransactionTimestampSeconds);
expect(result.timestamp).equal(firstTransactionTimestampSecondsHex);
expect(result.transactions.length).equal(2);
expect((result.transactions[0] as string)).equal(contractHash1);
expect((result.transactions[1] as string)).equal(contractHash1);

// verify expected constants
expect(result.baseFeePerGas).equal(0);
expect(result.baseFeePerGas).equal(EthImpl.zeroHex);
expect(result.difficulty).equal(EthImpl.zeroHex);
expect(result.extraData).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.emptyHex);
expect(result.mixHash).equal(EthImpl.emptyHex);
expect(result.nonce).equal(EthImpl.emptyHex);
expect(result.receiptsRoot).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.zeroAddressHex);
expect(result.mixHash).equal(EthImpl.emptyArrayHex);
expect(result.nonce).equal(EthImpl.zeroHex);
expect(result.receiptsRoot).equal(EthImpl.emptyArrayHex);
expect(result.sha3Uncles).equal(EthImpl.emptyArrayHex);
expect(result.stateRoot).equal(EthImpl.emptyHex);
expect(result.stateRoot).equal(EthImpl.emptyArrayHex);
expect(result.totalDifficulty).equal(EthImpl.zeroHex);
expect(result.uncles).to.deep.equal([]);
});
Expand All @@ -269,30 +271,31 @@ describe('Eth calls using MirrorNode', async function () {
mock.onGet(`contracts/results?timestamp=gte:${defaultBlock.timestamp.from}&timestamp=lte:${defaultBlock.timestamp.to}`).reply(200, defaultContractResults);
mock.onGet(`contracts/${contractAddress1}/results/${contractTimestamp1}`).reply(200, defaultDetailedContractResultsWithNullNullableValues);
mock.onGet(`contracts/${contractAddress2}/results/${contractTimestamp2}`).reply(200, defaultDetailedContractResultsWithNullNullableValues);
const result = await ethImpl.getBlockByNumber(blockNumber, true);
const result = await ethImpl.getBlockByNumber(EthImpl.numberTo0x(blockNumber), true);
expect(result).to.exist;
if (result == null) return;

// verify aggregated info
expect(result.hash).equal(blockHashTrimmed);
expect(result.gasUsed).equal(totalGasUsed);
expect(result.gasLimit).equal(maxGasLimit);
expect(result.number).equal(blockNumber);
expect(result.gasLimit).equal(maxGasLimitHex);
expect(result.number).equal(blockNumberHex);
expect(result.parentHash).equal(blockHashPreviousTrimmed);
expect(result.timestamp).equal(firstTransactionTimestampSeconds);
expect(result.timestamp).equal(firstTransactionTimestampSecondsHex);
expect(result.transactions.length).equal(2);
expect((result.transactions[0] as Transaction).hash).equal(contractHash1);
expect((result.transactions[1] as Transaction).hash).equal(contractHash1);

// verify expected constants
expect(result.baseFeePerGas).equal(0);
expect(result.baseFeePerGas).equal(EthImpl.zeroHex);
expect(result.difficulty).equal(EthImpl.zeroHex);
expect(result.extraData).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.emptyHex);
expect(result.mixHash).equal(EthImpl.emptyHex);
expect(result.nonce).equal(EthImpl.emptyHex);
expect(result.receiptsRoot).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.zeroAddressHex);
expect(result.mixHash).equal(EthImpl.emptyArrayHex);
expect(result.nonce).equal(EthImpl.zeroHex);
expect(result.receiptsRoot).equal(EthImpl.emptyArrayHex);
expect(result.sha3Uncles).equal(EthImpl.emptyArrayHex);
expect(result.stateRoot).equal(EthImpl.emptyHex);
expect(result.stateRoot).equal(EthImpl.emptyArrayHex);
expect(result.totalDifficulty).equal(EthImpl.zeroHex);
expect(result.uncles).to.deep.equal([]);
});
Expand Down Expand Up @@ -320,7 +323,7 @@ describe('Eth calls using MirrorNode', async function () {
expect(result).to.exist;
if (result == null) return;

expect(result.number).equal(blockNumber);
expect(result.number).equal(blockNumberHex);
});

it('eth_getBlockByNumber with pending tag', async function() {
Expand All @@ -332,7 +335,7 @@ describe('Eth calls using MirrorNode', async function () {
expect(result).to.exist;
if (result == null) return;

expect(result.number).equal(blockNumber);
expect(result.number).equal(blockNumberHex);
});

it('eth_getBlockByNumber with earliest tag', async function() {
Expand All @@ -343,7 +346,7 @@ describe('Eth calls using MirrorNode', async function () {
expect(result).to.exist;
if (result == null) return;

expect(result.number).equal(blockNumber);
expect(result.number).equal(blockNumberHex);
});

it('eth_getBlockByNumber with hex number', async function() {
Expand All @@ -354,7 +357,7 @@ describe('Eth calls using MirrorNode', async function () {
expect(result).to.exist;
if (result == null) return;

expect(result.number).equal(blockNumber);
expect(result.number).equal(blockNumberHex);
});

it('eth_getBlockByHash with match', async function () {
Expand All @@ -371,24 +374,24 @@ describe('Eth calls using MirrorNode', async function () {
// verify aggregated info
expect(result.hash).equal(blockHashTrimmed);
expect(result.gasUsed).equal(totalGasUsed);
expect(result.gasLimit).equal(maxGasLimit);
expect(result.number).equal(blockNumber);
expect(result.gasLimit).equal(maxGasLimitHex);
expect(result.number).equal(blockNumberHex);
expect(result.parentHash).equal(blockHashPreviousTrimmed);
expect(result.timestamp).equal(firstTransactionTimestampSeconds);
expect(result.timestamp).equal(firstTransactionTimestampSecondsHex);
expect(result.transactions.length).equal(2);
expect((result.transactions[0] as string)).equal(contractHash1);
expect((result.transactions[1] as string)).equal(contractHash1);

// verify expected constants
expect(result.baseFeePerGas).equal(0);
expect(result.baseFeePerGas).equal(EthImpl.zeroHex);
expect(result.difficulty).equal(EthImpl.zeroHex);
expect(result.extraData).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.emptyHex);
expect(result.mixHash).equal(EthImpl.emptyHex);
expect(result.nonce).equal(EthImpl.emptyHex);
expect(result.receiptsRoot).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.zeroAddressHex);
expect(result.mixHash).equal(EthImpl.emptyArrayHex);
expect(result.nonce).equal(EthImpl.zeroHex);
expect(result.receiptsRoot).equal(EthImpl.emptyArrayHex);
expect(result.sha3Uncles).equal(EthImpl.emptyArrayHex);
expect(result.stateRoot).equal(EthImpl.emptyHex);
expect(result.stateRoot).equal(EthImpl.emptyArrayHex);
expect(result.totalDifficulty).equal(EthImpl.zeroHex);
expect(result.uncles).to.deep.equal([]);
});
Expand All @@ -402,29 +405,29 @@ describe('Eth calls using MirrorNode', async function () {

const result = await ethImpl.getBlockByHash(blockHash, true);
expect(result).to.exist;
if (result == null) return;

// verify aggregated info
expect(result.hash).equal(blockHashTrimmed);
expect(result.gasUsed).equal(totalGasUsed);
expect(result.gasLimit).equal(maxGasLimit);
expect(result.number).equal(blockNumber);
expect(result.gasLimit).equal(maxGasLimitHex);
expect(result.number).equal(blockNumberHex);
expect(result.parentHash).equal(blockHashPreviousTrimmed);
expect(result.timestamp).equal(firstTransactionTimestampSeconds);
expect(result.timestamp).equal(firstTransactionTimestampSecondsHex);
expect(result.transactions.length).equal(2);
expect((result.transactions[0] as Transaction).hash).equal(contractHash1);
expect((result.transactions[1] as Transaction).hash).equal(contractHash1);

// verify expected constants
expect(result.baseFeePerGas).equal(0);
expect(result.baseFeePerGas).equal(EthImpl.zeroHex);
expect(result.difficulty).equal(EthImpl.zeroHex);
expect(result.extraData).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.emptyHex);
expect(result.mixHash).equal(EthImpl.emptyHex);
expect(result.nonce).equal(EthImpl.emptyHex);
expect(result.receiptsRoot).equal(EthImpl.emptyHex);
expect(result.miner).equal(EthImpl.zeroAddressHex);
expect(result.mixHash).equal(EthImpl.emptyArrayHex);
expect(result.nonce).equal(EthImpl.zeroHex);
expect(result.receiptsRoot).equal(EthImpl.emptyArrayHex);
expect(result.sha3Uncles).equal(EthImpl.emptyArrayHex);
expect(result.sha3Uncles).equal(EthImpl.emptyArrayHex);
expect(result.stateRoot).equal(EthImpl.emptyHex);
expect(result.stateRoot).equal(EthImpl.emptyArrayHex);
expect(result.totalDifficulty).equal(EthImpl.zeroHex);
expect(result.uncles).to.deep.equal([]);
});
Expand Down Expand Up @@ -693,7 +696,7 @@ describe('Eth', async function () {

const defaultTxHash = '0x4a563af33c4871b51a8b108aa2fe1dd5280a30dfb7236170ae5e5e7957eb6392';
const defaultTransaction = {
"accessList": "0x",
"accessList": undefined,
"blockHash": "0xd693b532a80fed6392b428604171fb32fdbf953728a3a7ecc7d4062b1652c042",
"blockNumber": 17,
"chainId": "0x12a",
Expand All @@ -702,8 +705,8 @@ describe('Eth', async function () {
"gasPrice": "0x4a817c80",
"hash": defaultTxHash,
"input": "0x0707",
"maxFeePerGas": "0x",
"maxPriorityFeePerGas": "0x",
"maxFeePerGas": undefined,
"maxPriorityFeePerGas": undefined,
"nonce": 1,
"r": "0xd693b532a80fed6392b428604171fb32fdbf953728a3a7ecc7d4062b1652c042",
"s": "0x24e9c602ac800b983b035700a14b23f78a253ab762deab5dc27e3555a750b354",
Expand Down Expand Up @@ -734,7 +737,7 @@ describe('Eth', async function () {
"status": "0x1",
"transactionHash": "0x4a563af33c4871b51a8b108aa2fe1dd5280a30dfb7236170ae5e5e7957eb6392",
"transactionIndex": "0x1",
"contractAddress": undefined,
"contractAddress": "0x0000000000000000000000000000000000001b59",
"root": undefined
};

Expand Down Expand Up @@ -976,6 +979,7 @@ describe('Eth', async function () {

mock.onGet(`contracts/results/${defaultTxHash}`).reply(200, detailedResultsWithNullNullableValues);
const result = await ethImpl.getTransactionByHash(defaultTxHash);
if (result == null) return;

expect(result).to.exist;
expect(result.accessList).to.eq(defaultTransaction.accessList);
Expand Down

0 comments on commit e12eff6

Please sign in to comment.