Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.
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
2 changes: 1 addition & 1 deletion src/chains/ethereum/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class Block {
// get the maxFeePerGas and maxPriorityFeePerGas, use those to calculate
// the effectiveGasPrice and add it to `extra` above, or we can just
// leave it out of extra and update the effectiveGasPrice after like this
tx.updateEffectiveGasPrice(header.baseFeePerGas);
tx.updateEffectiveGasPrice(header.baseFeePerGas?.toBigInt());
return txFn(tx);
}) as IncludeTransactions extends true ? TypedTransactionJSON[] : Data[];

Expand Down
10 changes: 3 additions & 7 deletions src/chains/ethereum/ethereum/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1885,16 +1885,12 @@ export default class EthereumApi implements Api {

const transactionPromise = transactions.get(txHash);
const receiptPromise = transactionReceipts.get(txHash);
const blockPromise = transactionPromise.then(t =>
t ? blocks.get(t.blockNumber.toBuffer()) : null
);
const [transaction, receipt, block] = await Promise.all([
const [transaction, receipt] = await Promise.all([
transactionPromise,
receiptPromise,
blockPromise
receiptPromise
]);
if (transaction) {
return receipt.toJSON(block, transaction, common);
return receipt.toJSON(transaction, common);
}

// if we are performing "strict" instamining, then check to see if the
Expand Down
24 changes: 5 additions & 19 deletions src/chains/ethereum/ethereum/src/miner/miner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const updateBloom = (blockBloom: Buffer, bloom: Buffer) => {
const sortByPrice = (values: TypedTransaction[], a: number, b: number) =>
values[a].effectiveGasPrice > values[b].effectiveGasPrice;

const refresher = (item: TypedTransaction, context: Quantity) =>
const refresher = (item: TypedTransaction, context: bigint) =>
item.updateEffectiveGasPrice(context);

export default class Miner extends Emittery<{
Expand All @@ -93,7 +93,7 @@ export default class Miner extends Emittery<{
#isBusy: boolean = false;
#paused: boolean = false;
#resumer: Promise<void>;
#currentBlockBaseFeePerGas: Quantity;
#currentBlockBaseFeePerGas: bigint;
#resolver: (value: void) => void;

/**
Expand Down Expand Up @@ -127,10 +127,7 @@ export default class Miner extends Emittery<{
}

// create a Heap that sorts by gasPrice
readonly #priced = new Heap<TypedTransaction, Quantity>(
sortByPrice,
refresher
);
readonly #priced = new Heap<TypedTransaction, bigint>(sortByPrice, refresher);
/*
* @param executables - A live Map of pending transactions from the transaction
* pool. The miner will update this Map by removing the best transactions
Expand All @@ -149,7 +146,7 @@ export default class Miner extends Emittery<{
this.#executables = executables;
this.#createBlock = (previousBlock: Block) => {
const newBlock = createBlock(previousBlock);
this.#setCurrentBlockBaseFeePerGas(newBlock);
this.#currentBlockBaseFeePerGas = newBlock.header.baseFeePerGas;
return newBlock;
};

Expand Down Expand Up @@ -182,7 +179,7 @@ export default class Miner extends Emittery<{
this.#updatePricedHeap();
return;
} else {
this.#setCurrentBlockBaseFeePerGas(block);
this.#currentBlockBaseFeePerGas = block.header.baseFeePerGas;
this.#setPricedHeap();
const result = await this.#mine(block, maxTransactions, onlyOneBlock);
this.emit("idle");
Expand Down Expand Up @@ -584,15 +581,4 @@ export default class Miner extends Emittery<{
public toggleStepEvent(enable: boolean) {
this.#emitStepEvent = enable;
}

/**
* Sets the #currentBlockBaseFeePerGas property if the current block
* has a baseFeePerGas property
*/
#setCurrentBlockBaseFeePerGas = (block: RuntimeBlock) => {
const baseFeePerGas = block.header.baseFeePerGas;
// before london hard fork, there will be no baseFeePerGas on the block
this.#currentBlockBaseFeePerGas =
baseFeePerGas === undefined ? undefined : Quantity.from(baseFeePerGas);
};
}
6 changes: 5 additions & 1 deletion src/chains/ethereum/ethereum/src/transaction-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { EthereumInternalOptions } from "@ganache/ethereum-options";
import { Executables } from "./miner/executables";
import { TypedTransaction } from "@ganache/ethereum-transaction";
import { Block } from "@ganache/ethereum-block";

/**
* Checks if the `replacer` is eligible to replace the `replacee` transaction
Expand Down Expand Up @@ -188,7 +189,10 @@ export default class TransactionPool extends Emittery<{ drain: undefined }> {
!transaction.effectiveGasPrice &&
this.#blockchain.common.isActivatedEIP(1559)
) {
const baseFeePerGas = this.#blockchain.blocks.latest.header.baseFeePerGas;
const baseFeePerGas = Block.calcNextBaseFee(
this.#blockchain.blocks.latest
);

transaction.updateEffectiveGasPrice(baseFeePerGas);
}

Expand Down
87 changes: 60 additions & 27 deletions src/chains/ethereum/ethereum/tests/api/eth/eth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,34 +543,67 @@ describe("api", () => {
assert(receipt.transactionIndex, "0x0");
});

it("eth_getTransactionByHash", async () => {
await provider.send("eth_subscribe", ["newHeads"]);
const txJson = {
type: "0x2",
chainId: "0x539",
nonce: "0x0",
from: accounts[0],
to: accounts[1],
value: "0x1",
maxPriorityFeePerGas: "0xf",
maxFeePerGas: "0xfffffffff",
gas: "0x15f90",
input: "0x01",
accessList: []
} as any;
const hash = await provider.send("eth_sendTransaction", [txJson]);
const _message = await provider.once("message");
// we want these values set for when we check against the return data,
// but they shouldn't be used in eth_sendTransaction, so we'll set them now
txJson.transactionIndex = "0x0";
txJson.gasPrice = "0x342770cf";

const tx = await provider.send("eth_getTransactionByHash", [hash]);
describe("eth_getTransactionByHash", () => {
it("returns a transaction matching the sent transaction", async () => {
await provider.send("eth_subscribe", ["newHeads"]);
const txJson = {
type: "0x2",
chainId: "0x539",
nonce: "0x0",
from: accounts[0],
to: accounts[1],
value: "0x1",
maxPriorityFeePerGas: "0xf",
maxFeePerGas: "0xfffffffff",
gas: "0x15f90",
input: "0x01",
accessList: []
} as any;
const hash = await provider.send("eth_sendTransaction", [txJson]);
const _message = await provider.once("message");
// we want these values set for when we check against the return data,
// but they shouldn't be used in eth_sendTransaction, so we'll set them now
txJson.transactionIndex = "0x0";
txJson.gasPrice = "0x342770cf";

const tx = await provider.send("eth_getTransactionByHash", [hash]);

// loop over all of the data we set to verify it matches
for (const [key, value] of Object.entries(txJson)) {
assert.deepStrictEqual(value, tx[key]);
}
});

// loop over all of the data we set to verify it matches
for (const [key, value] of Object.entries(txJson)) {
assert.deepStrictEqual(value, tx[key]);
}
it("has a `gasPrice` that matches the corresponding receipt's `effectiveGasPrice` after the transaction is mined", async () => {
await provider.send("eth_subscribe", ["newHeads"]);
await provider.send("miner_stop", []);
const txJson = {
type: "0x2",
from: accounts[0],
to: accounts[1],
maxPriorityFeePerGas: "0x77359400",
maxFeePerGas: "0x6FC23AC00"
} as any;
// we previously had a bug where the `gasPrice` field returned from
// `eth_getTransactionByHash` was incorrect when multiple transactions
// were sent, because we weren't assigning the correct `effectiveGasPrice`
// when adding the transaction to the pool. see:
// https://github.com/trufflesuite/ganache/issues/4094
const hashes = await Promise.all([
provider.send("eth_sendTransaction", [txJson]),
provider.send("eth_sendTransaction", [txJson])
]);
await provider.send("miner_start", []);
const _message = await provider.once("message");
for (const hash of hashes) {
const tx = await provider.send("eth_getTransactionByHash", [hash]);
const receipt = await provider.send("eth_getTransactionReceipt", [
hash
]);
assert.deepStrictEqual(tx.gasPrice, receipt.effectiveGasPrice);
assert.deepStrictEqual(tx.gasPrice.toString(), "0xab5d04c0");
}
});
});
});
});
12 changes: 8 additions & 4 deletions src/chains/ethereum/ethereum/tests/transaction-pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ describe("transaction pool", async () => {
},
common,
blocks: {
latest: { header: { baseFeePerGas: Quantity.from(875000000) } }
latest: {
header: {
baseFeePerGas: Quantity.from(875000000),
gasLimit: Quantity.from(30000000),
gasUsed: Quantity.from(0)
}
}
}
};
});
Expand Down Expand Up @@ -147,9 +153,7 @@ describe("transaction pool", async () => {
}
},
common,
blocks: {
latest: { header: { baseFeePerGas: Quantity.from(875000000) } }
}
blocks: blockchain.blocks
} as any;
const txPool = new TransactionPool(options.miner, fakeNonceChain, origins);
const executableTx = TransactionFactory.fromRpc(executableRpc, common);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,11 @@ export class EIP1559FeeMarketTransaction extends RuntimeTransaction {
return computeIntrinsicsFeeMarketTx(v, <EIP1559FeeMarketDatabaseTx>raw);
}

public updateEffectiveGasPrice(baseFeePerGas: Quantity) {
const baseFeePerGasBigInt = baseFeePerGas.toBigInt();
public updateEffectiveGasPrice(baseFeePerGas: bigint) {
const maxFeePerGas = this.maxFeePerGas.toBigInt();
const maxPriorityFeePerGas = this.maxPriorityFeePerGas.toBigInt();
const a = maxFeePerGas - baseFeePerGasBigInt;
const a = maxFeePerGas - baseFeePerGas;
const tip = a < maxPriorityFeePerGas ? a : maxPriorityFeePerGas;
this.effectiveGasPrice = Quantity.from(baseFeePerGasBigInt + tip);
this.effectiveGasPrice = Quantity.from(baseFeePerGas + tip);
}
}
2 changes: 1 addition & 1 deletion src/chains/ethereum/transaction/src/runtime-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,5 @@ export abstract class RuntimeTransaction extends BaseTransaction {
);

protected abstract toVmTransaction();
protected abstract updateEffectiveGasPrice(baseFeePerGas?: Quantity);
protected abstract updateEffectiveGasPrice(baseFeePerGas: bigint);
}
15 changes: 2 additions & 13 deletions src/chains/ethereum/transaction/src/transaction-receipt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,30 +133,19 @@ export class InternalTransactionReceipt {
}
}

public toJSON(
block: {
hash(): Data;
header: { number: Quantity; baseFeePerGas?: Quantity };
},
transaction: TypedTransaction,
common: Common
) {
public toJSON(transaction: TypedTransaction, common: Common) {
const raw = this.raw;
const contractAddress =
this.contractAddress.length === 0
? null
: Data.from(this.contractAddress);
const blockHash = block.hash();
const blockNumber = block.header.number;
const { blockHash, blockNumber } = transaction;
const blockLog = BlockLogs.create(blockHash);
const transactionHash = transaction.hash;
const transactionIndex = transaction.index;
blockLog.blockNumber = blockNumber;
raw[3].forEach(l => blockLog.append(transactionIndex, transactionHash, l));
const logs = [...blockLog.toJSON()];
if (block.header.baseFeePerGas) {
transaction.updateEffectiveGasPrice(block.header.baseFeePerGas);
}
const json: TransactionReceipt = {
transactionHash,
transactionIndex,
Expand Down