Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

bug fix for transaction receipts with multiple transactions in one block #268

Merged
merged 6 commits into from
Feb 19, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/database/receiptserializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ ReceiptSerializer.prototype.encode = function(receipt, done) {

ReceiptSerializer.prototype.decode = function(json, done) {
var self = this;
// Make sure we can handle mixed/upper-case transaction hashes
// it doesn't seem possible to record a transaction hash that isn't
// already lower case, as that's the way ganache generates them, however
// I don't think it will hurt anything to normalize here anyway.
// If you can figure out how to test this please feel free to add a test!
var txHash = json.transactionHash.toLowerCase();
davidmurdoch marked this conversation as resolved.
Show resolved Hide resolved

this.database.transactions.get(json.transactionHash, function(err, tx) {
if (err) {
Expand All @@ -37,7 +43,7 @@ ReceiptSerializer.prototype.decode = function(json, done) {
new Receipt(
tx,
result.block,
result.logs,
result.logs.filter((log) => log.transactionHash.toLowerCase() === txHash),
json.gasUsed,
json.cumulativeGasUsed,
json.contractAddress,
Expand Down
82 changes: 82 additions & 0 deletions test/requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,88 @@ const tests = function(web3) {
assert.strictEqual(to.number(result), 25);
});

it("should get only receipts relevant to the transaction (eth_getTransactionReceipt)", async function() {
const txData = contract.transaction_data;
txData.to = contractAddress;
txData.from = accounts[0];

// this test uses the provider's send instead of web3's sendTransaction
// because web3's sendTransaction has a bug when instamining that can
// cause it to subscribe to the `newHeads` event after the event has been
// sent by the provider, which is obviously too late.
const provider = web3.currentProvider;
const hasSubscriptions = typeof provider.on === "function";
const send = (() => {
let id = 1;
return (method, params) => {
return pify(provider.send.bind(provider))({
id: id++,
jsonrpc: "2.0",
method,
params
});
};
})();

// stop mining
await send("miner_stop");

// queue some transactions
const pendingTransactionHashes = [send("eth_sendTransaction", [txData]), send("eth_sendTransaction", [txData])];

const pendingNextBlockNumber = new Promise(async(resolve) => {
if (hasSubscriptions) {
// Ganache.provider and WebSocket servers can use the EventEmitter
provider.on("data", function newHeads(_, newHead) {
if (newHead == null) {
// When ganache is used as a provider _ is for errors,
// when it is used as a websocket server _ is the data.
newHead = _;
}
if (newHead.params.subscription === subscriptionId) {
resolve(to.number(newHead.params.result.number));
provider.removeListener("data", newHeads);
}
});
} else {
// for the HttpServer tests we need to poll for the next block
const startingBlockNumber = await web3.eth.getBlockNumber();
let currrentBlockNumber;
do {
currrentBlockNumber = await web3.eth.getBlockNumber();
} while (currrentBlockNumber === startingBlockNumber);

resolve(currrentBlockNumber);
}
});

// subscribe to `newHeads` if subscriptions are supported
const subscriptionId = hasSubscriptions ? (await send("eth_subscribe", ["newHeads"])).result : null;
const transactionHashes = (await Promise.all(pendingTransactionHashes)).map((response) => response.result);

// start the miner again
await send("miner_start");

// wait for the pending transactions to be mining in the next block
const blockNumber = await pendingNextBlockNumber;

// Now get the receipts
const pendingReceipts = transactionHashes.map((txHash) => send("eth_getTransactionReceipt", [txHash]));
const receipts = (await Promise.all(pendingReceipts)).map((response) => response.result);

assert.strictEqual(receipts.length, 2, "Not enough receipts");
receipts.forEach((receipt) => {
const logs = receipt.logs;
const receiptBlockNumber = to.number(receipt.blockNumber);
// What we are testing is that two transactions that "log" in the same block have receipts with only
// their own logs, and not each others. We only checking the blockNumber here to make sure they are the same.
assert.strictEqual(receiptBlockNumber, blockNumber, "Receipt blockNumber doesn't match expected block number");
assert.strictEqual(logs.length, 1, "Receipt had wrong amount of logs");
assert(logs.every((l) => l.transactionHash === receipt.transactionHash), "Receipt log isn't valid");
assert(logs.every((l) => l.blockHash === receipt.blockHash), "Logs blockhash doesn't match block blockhash");
});
});

// NB: relies on the previous test setting value to 25 and the contract deployment setting
// original value to 5. `contractCreationBlockNumber` is set in the first test of this
// describe block.
Expand Down