Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix eth_estimateGas for hollow account creation (#844) #860

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/relay/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default {
BLOCK_GAS_LIMIT: 15_000_000,
ISTANBUL_TX_DATA_NON_ZERO_COST: 16,
TX_BASE_COST: 21_000,
TX_HOLLOW_ACCOUNT_CREATION_GAS: 587_000,
TX_DEFAULT_GAS: 400_000,
TX_CREATE_EXTRA: 32_000,
TX_DATA_ZERO_COST: 4,
Expand Down
6 changes: 5 additions & 1 deletion packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class EthImpl implements Eth {
static emptyBloom = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
static defaultGas = EthImpl.numberTo0x(constants.TX_DEFAULT_GAS);
static gasTxBaseCost = EthImpl.numberTo0x(constants.TX_BASE_COST);
static gasTxHollowAccountCreation = EthImpl.numberTo0x(constants.TX_HOLLOW_ACCOUNT_CREATION_GAS);
static ethTxType = 'EthereumTransaction';
static ethEmptyTrie = '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421';
static defaultGasUsedRatio = 0.5;
Expand Down Expand Up @@ -325,8 +326,11 @@ export class EthImpl implements Eth {
async estimateGas(transaction: any, _blockParam: string | null, requestId?: string) {
const requestIdPrefix = formatRequestIdMessage(requestId);
this.logger.trace(`${requestIdPrefix} estimateGas(transaction=${JSON.stringify(transaction)}, _blockParam=${_blockParam})`);
//this checks whether this is a transfer transaction and not a contract function execution
if (!transaction || !transaction.data || transaction.data === '0x') {
return EthImpl.gasTxBaseCost;
const toAccount = await this.mirrorNodeClient.getAccount(transaction.to);
// when account exists return default base gas, otherwise return the minimum amount of gas to create an account entity
return toAccount ? EthImpl.gasTxBaseCost : EthImpl.gasTxHollowAccountCreation;
} else {
return EthImpl.defaultGas;
}
Expand Down
20 changes: 17 additions & 3 deletions packages/relay/tests/lib/eth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2218,19 +2218,33 @@ describe('Eth calls using MirrorNode', async function () {
expect(gas).to.equal(EthImpl.defaultGas);
});

it('eth_estimateGas transfer to existing account', async function() {
const receiverAddress = '0x5b98Ce3a4D1e1AC55F15Da174D5CeFcc5b8FB994';
mock.onGet(`accounts/${receiverAddress}`).reply(200, { address: receiverAddress });

const gas = await ethImpl.estimateGas({
to: receiverAddress,
value: 100_000_000_000
}, null);
expect(gas).to.equal(EthImpl.gasTxBaseCost);
});

it('eth_estimateGas empty call returns transfer cost', async function () {
mock.onGet(`accounts/undefined`).reply(404);
const gas = await ethImpl.estimateGas({}, null);
expect(gas).to.equal(EthImpl.gasTxBaseCost);
expect(gas).to.equal(EthImpl.gasTxHollowAccountCreation);
});

it('eth_estimateGas empty input transfer cost', async function () {
mock.onGet(`accounts/undefined`).reply(404);
const gas = await ethImpl.estimateGas({ data: "" }, null);
expect(gas).to.equal(EthImpl.gasTxBaseCost);
expect(gas).to.equal(EthImpl.gasTxHollowAccountCreation);
});

it('eth_estimateGas zero input returns transfer cost', async function () {
mock.onGet(`accounts/undefined`).reply(404);
const gas = await ethImpl.estimateGas({ data: "0x" }, null);
expect(gas).to.equal(EthImpl.gasTxBaseCost);
expect(gas).to.equal(EthImpl.gasTxHollowAccountCreation);
});

it('eth_gasPrice', async function () {
Expand Down
1 change: 1 addition & 0 deletions packages/relay/tests/lib/openrpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ describe("Open RPC Specification", function () {
});

it('should execute "eth_estimateGas"', async function () {
mock.onGet(`accounts/undefined`).reply(404);
const response = await ethImpl.estimateGas({}, null);

validateResponseSchema(methodsResponseSchema.eth_estimateGas, response);
Expand Down
28 changes: 28 additions & 0 deletions packages/server/tests/acceptance/rpc_batch2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {
let contractExecuteTimestamp;
let mirrorContract;
let mirrorContractDetails;
let mirrorSecondaryAccount;
let requestId;

const CHAIN_ID = process.env.CHAIN_ID || 0;
Expand Down Expand Up @@ -99,6 +100,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {

// get contract result details
mirrorContractDetails = await mirrorNode.get(`/contracts/${contractId}/results/${contractExecuteTimestamp}`, requestId);
mirrorSecondaryAccount = (await mirrorNode.get(`accounts?account.id=${accounts[1].accountId}`, requestId)).accounts[0];

const latestBlock = (await mirrorNode.get(`/blocks?limit=1&order=desc`, requestId)).blocks[0];
blockNumberAtStartOfTests = latestBlock.number;
Expand All @@ -117,6 +119,32 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () {
expect(res).to.not.be.equal('0x0');
});

it('@release should execute "eth_estimateGas" for contract call', async function() {
const res = await relay.call('eth_estimateGas', [{
to: mirrorContract.evm_address,
data: BASIC_CONTRACT_PING_CALL_DATA
}], requestId);
expect(res).to.contain('0x');
expect(res).to.equal(EthImpl.defaultGas);
});

it('@release should execute "eth_estimateGas" for existing account', async function() {
const res = await relay.call('eth_estimateGas', [{
to: mirrorSecondaryAccount.evm_address
}], requestId);
expect(res).to.contain('0x');
expect(res).to.equal(EthImpl.gasTxBaseCost);
});

it('@release should execute "eth_estimateGas" hollow account creation', async function() {
const hollowAccount = ethers.Wallet.createRandom();
const res = await relay.call('eth_estimateGas', [{
to: hollowAccount.address
}], requestId);
expect(res).to.contain('0x');
expect(res).to.equal(EthImpl.gasTxHollowAccountCreation);
});

it('should execute "eth_estimateGas" with to, from, value and gas filed', async function () {
const res = await relay.call('eth_estimateGas', [{
from: '0x114f60009ee6b84861c0cdae8829751e517bc4d7',
Expand Down