@ethereumjs/mpt
Version:
Implementation of the modified merkle patricia tree as specified in Ethereum's yellow paper.
119 lines • 5.88 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyMerkleProof = verifyMerkleProof;
exports.createMerkleProof = createMerkleProof;
exports.updateMPTFromMerkleProof = updateMPTFromMerkleProof;
exports.verifyMPTWithMerkleProof = verifyMPTWithMerkleProof;
const util_1 = require("@ethereumjs/util");
const index_ts_1 = require("../index.js");
/**
* An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof contains the encoded trie nodes
* from the root node to the leaf node storing state data.
* @param rootHash Root hash of the trie that this proof was created from and is being verified for
* @param key Key that is being verified and that the proof is created for
* @param proof An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof contains the encoded trie nodes from the root node to the leaf node storing state data.
* @param opts optional, the opts may include a custom hashing function to use with the trie for proof verification
* @throws If proof is found to be invalid.
* @returns The value from the key, or null if valid proof of non-existence.
*/
async function verifyMerkleProof(key, proof, opts) {
try {
const proofTrie = await (0, index_ts_1.createMPTFromProof)(proof, opts);
const value = await proofTrie.get(key, true);
return value;
}
catch {
throw (0, util_1.EthereumJSErrorWithoutCode)('Invalid proof provided');
}
}
/**
* Creates a proof from a trie and key that can be verified using {@link verifyMPTWithMerkleProof}. An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof contains
* the encoded trie nodes from the root node to the leaf node storing state data. The returned proof will be in the format of an array that contains Uint8Arrays of
* serialized branch, extension, and/or leaf nodes.
* @param key key to create a proof for
*/
async function createMerkleProof(trie, key) {
trie['DEBUG'] && trie['debug'](`Creating Proof for Key: ${(0, util_1.bytesToHex)(key)}`, ['create_proof']);
const { stack } = await trie.findPath(trie['appliedKey'](key));
const p = stack.map((stackElem) => {
return stackElem.serialize();
});
trie['DEBUG'] && trie['debug'](`Proof created with (${stack.length}) nodes`, ['create_proof']);
return p;
}
/**
* Updates a trie from a proof by putting all the nodes in the proof into the trie. Pass {@param shouldVerifyRoot} as true to check
* that root key of proof matches root of trie and throw if not.
* An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof contains the encoded trie nodes from the root node to the leaf node storing state data.
* @param trie The trie to update from the proof.
* @param proof An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof to update the trie from.
* @param shouldVerifyRoot - defaults to false. If `true`, verifies that the root key of the proof matches the trie root and throws if not (i.e invalid proof).
* @returns The root of the proof
*/
async function updateMPTFromMerkleProof(trie, proof, shouldVerifyRoot = false) {
trie['DEBUG'] && trie['debug'](`Saving (${proof.length}) proof nodes in DB`, ['from_proof']);
const opStack = proof.map((nodeValue) => {
let key = Uint8Array.from(trie['hash'](nodeValue));
key = trie['_opts'].keyPrefix ? (0, util_1.concatBytes)(trie['_opts'].keyPrefix, key) : key;
return {
type: 'put',
key,
value: nodeValue,
};
});
if (shouldVerifyRoot) {
if (opStack[0] !== undefined && opStack[0] !== null) {
if (!(0, util_1.equalsBytes)(trie.root(), opStack[0].key)) {
throw (0, util_1.EthereumJSErrorWithoutCode)('The provided proof does not have the expected trie root');
}
}
}
await trie['_db'].batch(opStack);
if (opStack[0] !== undefined) {
return opStack[0].key;
}
}
/**
* Verifies a proof by putting all of its nodes into a trie and attempting to get the proven key. An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof
* contains the encoded trie nodes from the root node to the leaf node storing state data.
* @param trie The trie to verify the proof against
* @param rootHash Root hash of the trie that this proof was created from and is being verified for
* @param key Key that is being verified and that the proof is created for
* @param proof an EIP-1186 proof to verify the key against
* @throws If proof is found to be invalid.
* @returns The value from the key, or null if valid proof of non-existence.
*/
async function verifyMPTWithMerkleProof(trie, rootHash, key, proof) {
trie['DEBUG'] &&
trie['debug'](`Verifying Proof:\n|| Key: ${(0, util_1.bytesToHex)(key)}\n|| Root: ${(0, util_1.bytesToHex)(rootHash)}\n|| Proof: (${proof.length}) nodes
`, ['VERIFY_PROOF']);
const proofTrie = new index_ts_1.MerklePatriciaTrie({
root: rootHash,
useKeyHashingFunction: trie['_opts'].useKeyHashingFunction,
common: trie['_opts'].common,
});
try {
await updateMPTFromMerkleProof(proofTrie, proof, true);
}
catch {
throw (0, util_1.EthereumJSErrorWithoutCode)('Invalid proof nodes given');
}
try {
trie['DEBUG'] &&
trie['debug'](`Verifying proof by retrieving key: ${(0, util_1.bytesToHex)(key)} from proof trie`, [
'VERIFY_PROOF',
]);
const value = await proofTrie.get(trie['appliedKey'](key), true);
trie['DEBUG'] && trie['debug'](`PROOF VERIFIED`, ['VERIFY_PROOF']);
return value;
}
catch (err) {
if (err.message === 'Missing node in DB') {
throw (0, util_1.EthereumJSErrorWithoutCode)('Invalid proof provided');
}
else {
throw err;
}
}
}
//# sourceMappingURL=proof.js.map