Skip to content

Commit

Permalink
Merge pull request #651 from sarvesh-ost/Fix_Merkel_Patricia_Proof
Browse files Browse the repository at this point in the history
Fixed bug: For extension node condition should use current path pointer
  • Loading branch information
schemar authored Feb 20, 2019
2 parents cc5b42f + 7c24ec1 commit 81d4e29
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 28 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ branches:
- develop
- /^feature\/.*/
- /^release-.*/
- /^hotfix-.*/
notifications:
email:
recipients:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The current release uses a "anchors" to provide state roots from remote chains.

### Notable Changes

* MerklePatriciaProof library can now verify valid proof with extension nodes ([#651](https://github.com/OpenSTFoundation/mosaic-contracts/pull/651)).
* Contracts' ABIs and BINs are provided via npm package [@openstfoundation/mosaic-contracts](https://www.npmjs.com/package/@openstfoundation/mosaic-contracts) ([#619](https://github.com/OpenSTFoundation/mosaic-contracts/pull/619), [#637](https://github.com/OpenSTFoundation/mosaic-contracts/pull/637)).
* Contracts are now separated into "Gateway" and "Core" contracts ([#221](https://github.com/OpenSTFoundation/mosaic-contracts/pull/221)).
* Core logic is now cleanly split into a "mosaic core" and an "anchor" ([#522](https://github.com/OpenSTFoundation/mosaic-contracts/pull/522), [#549](https://github.com/OpenSTFoundation/mosaic-contracts/pull/549)).
Expand Down
16 changes: 9 additions & 7 deletions contracts/lib/MerklePatriciaProof.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,23 @@ library MerklePatriciaProof {
nodeKey = RLP.toBytes32(currentNodeList[nextPathNibble]);
pathPtr += 1;
} else if(currentNodeList.length == 2) {
pathPtr += _nibblesToTraverse(RLP.toData(currentNodeList[0]), path, pathPtr);

if(pathPtr == path.length) {//leaf node
// Count of matching node key nibbles in path starting from pathPtr.
uint traverseLength = _nibblesToTraverse(RLP.toData(currentNodeList[0]), path, pathPtr);

if(pathPtr + traverseLength == path.length) { //leaf node
if(keccak256(abi.encodePacked(RLP.toData(currentNodeList[1]))) == value) {
return true;
} else {
return false;
}
}
//extension node ... test if means that it is empty value
if(_nibblesToTraverse(RLP.toData(currentNodeList[0]), path, pathPtr) == 0) {
return (keccak256(abi.encodePacked()) == value);
} else if (traverseLength == 0) { // error: couldn't traverse path
return false;
} else { // extension node
pathPtr += traverseLength;
nodeKey = RLP.toBytes32(currentNodeList[1]);
}

nodeKey = RLP.toBytes32(currentNodeList[1]);
} else {
return false;
}
Expand Down
14 changes: 14 additions & 0 deletions test/data/proof.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,19 @@
"mappingKey": "0x8f019f771e744709d6aaf3cf36adfb5285af696d06f532e94f78d85f334240e6",
"expectedPath": "0x4b1b424b6d4b5ad19b3035c344e9b2ba553dfa46fb1a023e028502d49ae6c34e"
}
},
"storageProofWithExtensionNode" : {
"dataSet1" : {
"value":"0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2",
"root":"0xa506e7e444fc636360bb593a808f41f126a8813ea71410d4515987eacc594380",
"path":"0x40769c9cc6db1aff35e37923897950227d2a87c95ef0101b2a0233740f2ce00d",
"rlpParentNodes":"0xf9022cf9019180a0320ce86e317f7e61e7890a6671338aa79374e5ec3206fc38496b5b3719788fc4a07642cb37fbbc28c4c2e8367a93bb491013223d266543c0e42db6a580cc274beea0ad8732b76b5befaa3c523633abdafb8b410b8e1140526255c839a7225e2b2f3ea060b926526a0a95873151f96f0c4aa7746dd1cc51242b4893d43554acd2716cda80a03e819a317d72fa7445677a4104b74899547b424aec05ae303b605f7d78aa9dc4a0c9980a15de3258be2f1dcf5d0657c256f7e7aebe83b736aada89aa02dee3853ea0747f61b4309342612ee827a5930678c61bd54a2c85e207144c265912351d46f4a070256c377a27b7c7cdf605559f21f20f54d147abce914c3b1ac36815fd8f2b8080a0b7a25a664618fb58b0754dd3c9665402787704d11169240bccf8e7c34feb0167a0e78c6966deeb91437d0c2cbf66bde64fd4dbb76d22a9885679a3cf9e5fd84394a02a74dc63db6f6f5f79d6cadbb2b348771d3bcd1913f9319cfa0c5fea4353db47a0e53cc96cd3fe0910d63b48800ac824d7f0e094548d259d6342da2189ca3c0e238080e210a0094e521c10bbb9291d38d4aebd6c6722a4763451011f97f24709b28611831699f8518080808080a0460b29d4cbb31e3680769b8ece024e6e35e9c9c3d9c7c207873e93b8c43cff1980a0fe9627f9a9fb3323465824fc6fdfd87f907332a17f6ed39bfdcd94c76b548eee808080808080808080e19f369c9cc6db1aff35e37923897950227d2a87c95ef0101b2a0233740f2ce00d01"
},
"dataSet2" : {
"value":"0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2",
"root":"0xf0912a1d8b0ec9c0ffbd9a67c41c41218141f9312a43b98d42fdff9f9318a0e2",
"path":"0x05d1f431c2d6ca07553a2a1c7994580de926e9472539029ce8d0650805c5de3c",
"rlpParentNodes":"0xf9020cf90171a05d75023b42a320356169a535352448227aad73b1b884a4f3cbf75aec8399e451a0a68beb9bb60c3542e0e79bfb93ccfb18a84b97f6e1deb133c5ddcb577aec0289a0af4694d88ff60f5048603085d4eef4862697652115560f18182ae927b3bbe01b80a058563817668d198a2fac4613f38a695ec0afd385295ec64e0c7be7909e672852a080dd89a242f8ba180df9e32fe155fcc903636e6969ef528c118f6f9fc3fff0dd80a0186657dbed585fc9c8c6c9eb22fec05d5f020f49f2f815b480530ec34126aa79a0a4ad033ba45782ba4f41e2829820550a38e2fc97439d9fcfeb94a879c23e219f8080a0a4b1cad573b6d300d001b84fc21b3abc301160cb0f07e481123a927a8483ff6da07b2bb2a0db48ba5e08fab3dad59ab1ec59950a0a02959cbf5dfcab09379bc707a071902aa1d226bb4e5187dc1e6fd2be9c16388c5acea8074dc798ace960445925a02cef2457e726924bf6b5ae4b5edc8cc47445ecc3eb68bace0e53996d1e5018658080e215a00e43ff48208da7a6b63be3a9b0966e0ddb9e8f3240c6eb114deeebdefd923974f851a0e8de87b73f756e7dc16f2bcb4c7fbf3ec8276b4da2d9d9fba4c7aec798676278808080808080808080808080a03d715e3f784c608167a224cc99731b38bb0fccc86df28e0f2744fe3ac317eec7808080e19f31f431c2d6ca07553a2a1c7994580de926e9472539029ce8d0650805c5de3c01"
}
}
}
88 changes: 67 additions & 21 deletions test/lib/merkle_patricia_proof.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
const MerklePatriciaProofTest = artifacts.require(
'./MerklePatriciaProofTest.sol',
);
// Copyright 2019 OpenST Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ----------------------------------------------------------------------------
//
// http://www.simpletoken.org/
//
// ----------------------------------------------------------------------------

const MerklePatriciaProof = artifacts.require('./MerklePatriciaProof.sol');
const proofJson = require('../data/proof.json');
const Utils = require('../test_lib/utils.js');

contract('MerklePatriciaProof', (accounts) => {
let merkleMock = null;
contract('MerklePatriciaProof.verify()', (accounts) => {
let merklePatriciaLib;

describe('Test Cases for Account proof verification', async () => {
before(async () => {
merkleMock = await MerklePatriciaProofTest.new();
merklePatriciaLib = await MerklePatriciaProof.deployed();
});

it('Should pass when all the parameters are valid', async () => {
const proofStatus = await merkleMock.verifyAccount.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.account.sha3EncodedAccount,
proofJson.account.hashedPath,
proofJson.account.rlpParentNodes,
Expand All @@ -23,7 +41,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail when path is incorrect', async () => {
const proofStatus = await merkleMock.verifyAccount.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.account.sha3EncodedAccount,
'0x01dB94fdCa0FFeDc40A6965DE97790085d71b413',
proofJson.account.rlpParentNodes,
Expand All @@ -33,7 +51,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail when state root is incorrect', async () => {
const proofStatus = await merkleMock.verifyAccount.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.account.sha3EncodedAccount,
proofJson.account.hashedPath,
proofJson.account.rlpParentNodes,
Expand All @@ -43,7 +61,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail when rlp parent nodes is incorrect', async () => {
const proofStatus = await merkleMock.verifyAccount.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.account.sha3EncodedAccount,
proofJson.account.hashedPath,
'0xf908cef90211a0f113fc83479a9dcb7a7b5c98cdb42f2426',
Expand All @@ -53,7 +71,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail when encoded value is incorrect', async () => {
const proofStatus = await merkleMock.verifyAccount.call(
const proofStatus = await merklePatriciaLib.verify.call(
'0xf8468206fb80a036ed801abf5678f1506f1fa61e5ccda1f4de53cc7c',
proofJson.account.hashedPath,
proofJson.account.rlpParentNodes,
Expand All @@ -65,7 +83,7 @@ contract('MerklePatriciaProof', (accounts) => {

describe('Test Cases for Storage proof', async () => {
it('Should pass for valid variable storage', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storage.variable.sha3ValueAtStorage,
proofJson.storage.variable.path,
proofJson.storage.variable.rlpParentNodes,
Expand All @@ -75,7 +93,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail for variable storage when encoded value is incorrect', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
'0x468206fb80a036ed801abf5678f1506f1fa61e5ccda1f4de53cc7c',
proofJson.storage.variable.path,
proofJson.storage.variable.rlpParentNodes,
Expand All @@ -85,7 +103,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail for variable storage when encoded path is incorrect', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storage.variable.sha3ValueAtStorage,
'0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e522',
proofJson.storage.variable.rlpParentNodes,
Expand All @@ -96,7 +114,7 @@ contract('MerklePatriciaProof', (accounts) => {

it('Should fail for variable storage when rlp parent nodes is incorrect', async () => {
await Utils.expectRevert(
merkleMock.verifyStorage.call(
merklePatriciaLib.verify.call(
proofJson.storage.variable.sha3ValueAtStorage,
proofJson.storage.variable.path,
'0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e524',
Expand All @@ -106,7 +124,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail for variable storage when storage root is incorrect', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storage.variable.sha3ValueAtStorage,
proofJson.storage.variable.path,
proofJson.storage.variable.rlpParentNodes,
Expand All @@ -116,7 +134,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should pass for valid mapping storage value', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storage.mapping.sha3ValueAtStorage,
proofJson.storage.mapping.path,
proofJson.storage.mapping.rlpParentNodes,
Expand All @@ -126,7 +144,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail for mapping storage value when encoded value is incorrect', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
'0x468206fb80a036ed801abf5678f1506f1fa61e5ccda1f4de53cc7c',
proofJson.storage.mapping.path,
proofJson.storage.mapping.rlpParentNodes,
Expand All @@ -136,7 +154,7 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail for mapping storage value when encoded path is incorrect', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storage.mapping.sha3ValueAtStorage,
'0x4d6e9a4b1a3576f692c1333198a77a5fb8b72c326f2a4c35eeeaab187773da7a',
proofJson.storage.mapping.rlpParentNodes,
Expand All @@ -147,7 +165,7 @@ contract('MerklePatriciaProof', (accounts) => {

it('Should fail for mapping storage value when rlp parent nodes is incorrect', async () => {
await Utils.expectRevert(
merkleMock.verifyStorage.call(
merklePatriciaLib.verify.call(
proofJson.storage.mapping.sha3ValueAtStorage,
proofJson.storage.mapping.path,
'0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e522',
Expand All @@ -157,13 +175,41 @@ contract('MerklePatriciaProof', (accounts) => {
});

it('Should fail for mapping storage value when storage root is incorrect', async () => {
const proofStatus = await merkleMock.verifyStorage.call(
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storage.mapping.sha3ValueAtStorage,
proofJson.storage.mapping.path,
proofJson.storage.mapping.rlpParentNodes,
'0xa078cef90211a0f113fc83479a9dcb7a7b5c98cdb42f2426',
);
assert.equal(proofStatus, false);
});

it('should pass for storage proof containing extension node(dataSet1)', async () => {
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storageProofWithExtensionNode.dataSet1.value,
proofJson.storageProofWithExtensionNode.dataSet1.path,
proofJson.storageProofWithExtensionNode.dataSet1.rlpParentNodes,
proofJson.storageProofWithExtensionNode.dataSet1.root,
);
assert.strictEqual(
proofStatus,
true,
'Proof verification failed for storage proof containing extension node',
);
});

it('should pass for storage proof containing extension node(dataSet2)', async () => {
const proofStatus = await merklePatriciaLib.verify.call(
proofJson.storageProofWithExtensionNode.dataSet2.value,
proofJson.storageProofWithExtensionNode.dataSet2.path,
proofJson.storageProofWithExtensionNode.dataSet2.rlpParentNodes,
proofJson.storageProofWithExtensionNode.dataSet2.root,
);
assert.strictEqual(
proofStatus,
true,
'Proof verification failed for storage proof containing extension node',
);
});
});
});

0 comments on commit 81d4e29

Please sign in to comment.