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

Incorrect tx receipt trie root #231

Merged
merged 22 commits into from
Feb 20, 2019
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3e50229
WIP tests are skipped - not prod ready
nicholasjpaterno Nov 19, 2018
895c45f
Update block_tags async await
nicholasjpaterno Nov 20, 2018
968f7fe
Update test to include determinate config
nicholasjpaterno Nov 21, 2018
1f07d7b
Fix blockchain_double::processBlock to include tx and rcpt tries
nicholasjpaterno Nov 21, 2018
f29f1fa
Run formatter
nicholasjpaterno Nov 21, 2018
226c14f
Merge branch 'develop' into incorrect-tx-rcpt-trie-root
nicholasjpaterno Dec 11, 2018
82e9fac
Update block_tags cleanup tests
nicholasjpaterno Dec 11, 2018
b856c6b
WIP - breaks some tests
nicholasjpaterno Dec 20, 2018
8d5d985
WIP breaks forking tests
nicholasjpaterno Jan 3, 2019
01b092d
WIP cleanup eachOfSeries
nicholasjpaterno Jan 3, 2019
6fc29ab
Merge 'develop' into branch incorrect-tx-rcpt-trie-root
nicholasjpaterno Jan 3, 2019
15edee7
WIP Cleanup processBlock - forking tests not working
nicholasjpaterno Jan 3, 2019
eb432e7
Refactor forking.js to async/await
nicholasjpaterno Jan 3, 2019
52a11ec
Merge branch 'develop' into incorrect-tx-rcpt-trie-root
nicholasjpaterno Feb 15, 2019
64180a6
Fix forking.js tests
nicholasjpaterno Feb 17, 2019
4789f63
Update process block, remove validation
nicholasjpaterno Feb 17, 2019
bbade92
Update blockchain double, await tx/rcpt trie.put
nicholasjpaterno Feb 18, 2019
4a9e86d
Merge branch 'develop' into incorrect-tx-rcpt-trie-root
nicholasjpaterno Feb 18, 2019
a26cfe0
Update blockchain_double.js cleanup logic/readability
nicholasjpaterno Feb 19, 2019
b368c1c
Update test/block_tags.js, remove var
nicholasjpaterno Feb 19, 2019
cf4d423
Update blockchain_double, promisify putInTrie
nicholasjpaterno Feb 19, 2019
2dd2929
Fix block_tags, remove unnecessary logic
nicholasjpaterno Feb 19, 2019
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
42 changes: 32 additions & 10 deletions lib/blockchain_double.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ BlockchainDouble.prototype.processBlock = function(vm, block, commit, callback)
generate: true,
skipBlockValidation: true
},
function(vmerr, results) {
async function(vmerr, results) {
// This is a check that has been in there for awhile. I'm unsure if it's required, but it can't hurt.
if (vmerr && vmerr instanceof Error === false) {
vmerr = new Error("VM error: " + vmerr);
Expand Down Expand Up @@ -583,6 +583,21 @@ BlockchainDouble.prototype.processBlock = function(vm, block, commit, callback)

block.header.gasUsed = utils.toBuffer(to.hex(totalBlockGasUsage));

const txTrie = new Trie();
const rcptTrie = new Trie();
const promises = [];
const putInTrie = function(trie, key, val) {
return new Promise((resolve, reject) => {
try {
trie.put(key, val, (...args) => {
nicholasjpaterno marked this conversation as resolved.
Show resolved Hide resolved
resolve();
});
} catch (error) {
reject(error);
}
});
};

for (var v = 0; v < results.receipts.length; v++) {
var result = results.results[v];
var receipt = results.receipts[v];
Expand Down Expand Up @@ -630,24 +645,31 @@ BlockchainDouble.prototype.processBlock = function(vm, block, commit, callback)
to.hex(result.bloom.bitvector)
);
receipts.push(rcpt);

const rcptBuffer = Buffer.from(JSON.stringify(rcpt.toJSON()));
const key = utils.rlp.encode(v);
promises.push(putInTrie(txTrie, key, tx.serialize()));
promises.push(putInTrie(rcptTrie, key, rcptBuffer));
}
await Promise.all(promises);

function commmitIfNeeded(cb) {
if (commit === true) {
// Put that block on the end of the chain
self.putBlock(block, logs, receipts, cb);
} else {
cb();
}
block.header.transactionsTrie = utils.toBuffer(txTrie.root);
block.header.receiptTrie = utils.toBuffer(rcptTrie.root);

if (commit) {
// Put that block on the end of the chain
self.putBlock(block, logs, receipts, done);
} else {
done();
}

commmitIfNeeded(function(e) {
function done(e) {
if (e) {
return callback(e);
}
// Note we return the vm err here too, if it exists.
callback(vmerr, block.transactions, results);
});
}
}
);
};
Expand Down
182 changes: 65 additions & 117 deletions test/block_tags.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
var Web3 = require("web3");
var assert = require("assert");
var Ganache = require(process.env.TEST_BUILD
const Ganache = require(process.env.TEST_BUILD
? "../build/ganache.core." + process.env.TEST_BUILD + ".js"
: "../index.js");
var fs = require("fs");
var solc = require("solc");
var async = require("async");
var to = require("../lib/utils/to.js");
const to = require("../lib/utils/to.js");
const { readFileSync } = require("fs");
const { compile } = require("solc");
const assert = require("assert");
const Web3 = require("web3");

var source = fs.readFileSync("./test/contracts/examples/Example.sol", { encoding: "utf8" });
var result = solc.compile(source, 1);
const source = readFileSync("./test/contracts/examples/Example.sol", { encoding: "utf8" });
const result = compile(source, 1);

// Note: Certain properties of the following contract data are hardcoded to
// maintain repeatable tests. If you significantly change the solidity code,
// make sure to update the resulting contract data with the correct values.
var contract = {
const contract = {
solidity: source,
abi: result.contracts[":Example"].interface,
binary: "0x" + result.contracts[":Example"].bytecode,
Expand All @@ -35,128 +34,77 @@ var contract = {
};

describe("Block Tags", function() {
var accounts;
var web3 = new Web3(Ganache.provider());
var contractAddress;

var initialBlockNumber;
var initial = {};

before("Gather accounts", function(done) {
web3.eth.getAccounts(function(err, accs) {
if (err) {
return done(err);
}
accounts = accs;
done();
});
const options = {
mnemonic: "candy maple velvet cake sugar cream honey rich smooth crumble sweet treat",
time: new Date(0) // Testing features that rely on determinate conditions
};
const web3 = new Web3(Ganache.provider(options));
const initial = {};
let accounts;
let contractAddress;
let initialBlockNumber;

before("Gather accounts", async function() {
accounts = await web3.eth.getAccounts();
});

before("Get initial block number", function(done) {
web3.eth.getBlockNumber(function(err, n) {
if (err) {
return done(err);
}
initialBlockNumber = to.number(n);
done();
});
before("Get initial block number", async function() {
initialBlockNumber = to.number(await web3.eth.getBlockNumber());
});

before("Get initial balance and nonce", function(done) {
async.parallel(
{
balance: web3.eth.getBalance.bind(web3.eth, accounts[0]),
nonce: web3.eth.getTransactionCount.bind(web3.eth, accounts[0])
},
function(err, result) {
if (err) {
return done(err);
}
initial = result;
initial.nonce = to.number(initial.nonce);
done();
}
);
before("Get initial balance and nonce", async function() {
const [balance, nonce] = [
nicholasjpaterno marked this conversation as resolved.
Show resolved Hide resolved
...(await Promise.all([
nicholasjpaterno marked this conversation as resolved.
Show resolved Hide resolved
web3.eth.getBalance.bind(web3.eth, accounts[0])(),
web3.eth.getTransactionCount.bind(web3.eth, accounts[0])()
]))
];
initial.balance = balance;
initial.nonce = to.number(nonce);
});

before("Make transaction that changes balance, nonce and code", function(done) {
web3.eth.sendTransaction(
{
from: accounts[0],
data: contract.binary,
gas: 3141592
},
function(err, tx) {
if (err) {
return done(err);
}
before("Make transaction that changes balance, nonce and code", async function() {
const { transactionHash } = await web3.eth.sendTransaction({
from: accounts[0],
data: contract.binary,
gas: 3141592
});
({ contractAddress } = await web3.eth.getTransactionReceipt(transactionHash));
});

web3.eth.getTransactionReceipt(tx, function(err, receipt) {
if (err) {
return done(err);
}
it("should return the initial nonce at the previous block number", async function() {
const nonce = await web3.eth.getTransactionCount(accounts[0], initialBlockNumber);
assert.strictEqual(nonce, initial.nonce);

contractAddress = receipt.contractAddress;
done();
});
}
);
// Check that the nonce incremented with the block number, just to be sure.
const newNonce = await web3.eth.getTransactionCount(accounts[0], initialBlockNumber + 1);
assert.strictEqual(newNonce, initial.nonce + 1);
});

it("should return the initial nonce at the previous block number", function(done) {
web3.eth.getTransactionCount(accounts[0], initialBlockNumber, function(err, nonce) {
if (err) {
return done(err);
}
assert.strictEqual(nonce, initial.nonce);
it("should return the initial balance at the previous block number", async function() {
const balance = await web3.eth.getBalance(accounts[0], initialBlockNumber);
assert.strictEqual(balance, initial.balance);

// Check that the nonce incremented with the block number, just to be sure.
web3.eth.getTransactionCount(accounts[0], initialBlockNumber + 1, function(err, nonce) {
if (err) {
return done(err);
}
assert.strictEqual(nonce, initial.nonce + 1);
done();
});
});
// Check that the balance incremented with the block number, just to be sure.
const newBalance = await web3.eth.getBalance(accounts[0], initialBlockNumber + 1);
const initialBalanceInEther = parseFloat(web3.utils.fromWei(initial.balance, "ether"));
const balanceInEther = parseFloat(web3.utils.fromWei(newBalance, "ether"));
assert(balanceInEther < initialBalanceInEther);
});

it("should return the initial balance at the previous block number", function(done) {
web3.eth.getBalance(accounts[0], initialBlockNumber, function(err, balance) {
if (err) {
return done(err);
}
assert.strictEqual(balance, initial.balance);
it("should return the no code at the previous block number", async function() {
const code = await web3.eth.getCode(contractAddress, initialBlockNumber);
assert.strictEqual(code, "0x");

// Check that the balance incremented with the block number, just to be sure.
web3.eth.getBalance(accounts[0], initialBlockNumber + 1, function(err, balance) {
if (err) {
return done(err);
}
var initialBalanceInEther = parseFloat(web3.utils.fromWei(initial.balance, "ether"));
var balanceInEther = parseFloat(web3.utils.fromWei(balance, "ether"));
assert(balanceInEther < initialBalanceInEther);
done();
});
});
// Check that the code incremented with the block number, just to be sure.
const newCode = await web3.eth.getCode(contractAddress, initialBlockNumber + 1);
assert.notStrictEqual(newCode, "0x");
assert(newCode.length > 20); // Just because we don't know the actual code we're supposed to get back
});

it("should return the no code at the previous block number", function(done) {
web3.eth.getCode(contractAddress, initialBlockNumber, function(err, code) {
if (err) {
return done(err);
}
assert.strictEqual(code, "0x");

// Check that the code incremented with the block number, just to be sure.
web3.eth.getCode(contractAddress, initialBlockNumber + 1, function(err, code) {
if (err) {
return done(err);
}
assert.notStrictEqual(code, "0x");
assert(code.length > 20); // Just because we don't know the actual code we're supposed to get back
done();
});
});
it("should not have the same tx and receipt root when the block contains 1 (or more) tx's", async function() {
const block = await web3.eth.getBlock(initialBlockNumber + 1, false);
assert.strictEqual(block.transactions.length, 1, "should have one tx in the block.");
assert.notStrictEqual(block.transactionsRoot, block.receiptsRoot, "Trie roots should not be equal.");
});
});