Skip to content
This repository has been archived by the owner on Mar 14, 2019. It is now read-only.

Sarvesh/merkel patricia proof generator #139

Merged
merged 24 commits into from
Jul 9, 2018
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7d8b228
Adding proof generation service, account_proof_generation, storage_pr…
Jun 22, 2018
d593df3
Corrected path generation and storage root generation
Jun 25, 2018
9c65ba2
Added service for proof generation
Jun 25, 2018
91bd438
Added unit tests for storage proof and file restructuring
Jun 25, 2018
a28ff4b
Generating storage proof in batches
Jun 25, 2018
8146b37
Corrected constants file error
Jun 25, 2018
b33d3ae
Added logic for case insensitivity of levedb
Jun 26, 2018
cd6960b
Added general exception handling in service layer
Jun 26, 2018
42132f2
Converted proof.js to a class and prototype structure, enhanced comments
Jun 26, 2018
b87985e
Added unit test cases of helper and proof lib
Jun 26, 2018
28dc10d
Returning RLP encoded data as proof and using response help successWi…
Jun 27, 2018
96db709
Increasing mocha test timeout time
Jun 28, 2018
f669407
Seperated unit and integration tests folder as they were impacting ea…
Jun 29, 2018
3731ecb
Added test scripts for unit tests and integration tests, updated trav…
Jun 29, 2018
90f9aca
Refactoring: changing multiple let to single, and oThis to const type
Jul 3, 2018
0cc155f
Merge branch 'feature/proof' into sarvesh/merkel-patricia-proof-gener…
Jul 3, 2018
83a5159
Corrected travis entry for feature/proof trigger
Jul 3, 2018
7d5d03d
Update return type of storage proof generation, returing map of key V…
Jul 5, 2018
5a6781d
Corrected return type of storage proof and service in manifest
Jul 5, 2018
816b72a
Added read me details for proof generation service in configure.md
Jul 5, 2018
d3c6676
Freezing version of dependency in package.json
Jul 5, 2018
0705ba4
Renamed variable StorageProof to storageProof
Jul 6, 2018
2d4fa0d
renamed variable AccountProof to AccountProofKlass
Jul 6, 2018
caaeff9
Merge branch 'feature/proof' into sarvesh/merkel-patricia-proof-gener…
Jul 9, 2018
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
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ branches:
- master
- develop
- release-0.9
- balances_model_and_cache
- feature/proof
notifications:
email:
recipients:
Expand Down Expand Up @@ -45,7 +45,8 @@ script:
- source $HOME/openst-setup/openst_env_vars.sh
- node tools/setup/branded_token/register.js "ACME Coin" "ACME" 10
- node tools/setup/branded_token/mint.js "ACME" 1000000000000000000000
- mocha --timeout 120000 test/* --exit
- npm run unit_test
- npm run integration_test
after_script:
- kill $(ps aux | grep 'tools/setup/start_services.js' | awk '{print $2}')
- kill $(ps aux | grep 'DynamoDBLocal.jar' | awk '{print $2}')
Expand Down
7 changes: 6 additions & 1 deletion config/core_constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ CoreConstants.prototype = {
*/
STANDALONE_MODE: process.env.OST_STANDALONE_MODE || 0,

AUTO_SCALE_DYNAMO: process.env.AUTO_SCALE_DYNAMO
AUTO_SCALE_DYNAMO: process.env.AUTO_SCALE_DYNAMO,

/**
* PROOF BATCH SIZE
*/
PROOF_BATCH_SIZE: 10
};

module.exports = new CoreConstants();
45 changes: 45 additions & 0 deletions config/error/general.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,5 +189,50 @@
"http_code": "422",
"code": "UNPROCESSABLE_ENTITY",
"message": "No new blocks on chain."
},
"exception": {
"http_code": "500",
"code": "INTERNAL_SERVER_ERROR",
"message": "Something went wrong exception."
},
"db_path_undefined": {
"http_code": "422",
"code": "REQUIRED_ARGUMENT_MISSING",
"message": "Required argument db path is missing"
},
"state_root_undefined": {
"http_code": "422",
"code": "REQUIRED_ARGUMENT_MISSING",
"message": "Required argument state root is missing"
},
"account_address_undefined": {
"http_code": "422",
"code": "REQUIRED_ARGUMENT_MISSING",
"message": "Required argument account address is missing"
},
"account_node_not_found": {
"http_code": "422",
"code": "ACCOUNT_NOT_FOUND",
"message": "Account not found"
},
"storage_node_not_found": {
"http_code": "422",
"code": "STORAGE_NOT_FOUND",
"message": "Storage node not found"
},
"storage_index_undefined": {
"http_code": "422",
"code": "REQUIRED_ARGUMENT_MISSING",
"message": "Required argument storage index is missing"
},
"tree_not_initialized": {
"http_code": "422",
"code": "TRIE_UNINITIALIZED",
"message": "Trie not initialized"
},
"proof_batch_size_exceeds": {
"http_code": "422",
"code": "BATCH_SIZE_EXCEEDS",
"message": "Tree batch size exceeds"
}
}
38 changes: 38 additions & 0 deletions lib/db/leveldb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const leveldown = require('leveldown')
, levelup = require('levelup')
;

/**
* @constructor
*/
function LevelDBFactory() {
this.instanceMap = {};
}

LevelDBFactory.prototype = {
/**
* Returns leveldb instance, it creates new in not already exists otherwise returns existing instance
* @param dbPath
* @return leveldb instance
*/
getInstance: function (dbPath) {
const oThis = this;

let lowerCasePath = dbPath.toLowerCase();

if (!oThis.instanceMap[lowerCasePath]) {
oThis.instanceMap[lowerCasePath] = oThis.create(lowerCasePath);
}
return oThis.instanceMap[lowerCasePath];
},

/**
*
* @param dbPath
* @return leveldb instance
*/
create: function (dbPath) {
return levelup(leveldown(dbPath));
},
};
module.exports = new LevelDBFactory();
73 changes: 73 additions & 0 deletions lib/proof/account_proof.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const Trie = require('merkle-patricia-tree');

const rootPrefix = "../.."
, logger = require(rootPrefix + '/helpers/custom_console_logger')
, responseHelper = require(rootPrefix + '/lib/formatter/response')
, proof = require(rootPrefix + "/lib/proof/proof")
, basicHelper = require(rootPrefix + '/helpers/basic_helper')
;

/**
* @constructor
* @param stateRoot
* @param db
*/
function AccountProof(stateRoot, db) {
const oThis = this;

oThis.trie = new Trie(db, stateRoot);
}

AccountProof.prototype = {

/**
* Validate and _build proof
* @param address
* @return {Promise<proof>}
*/
perform: async function (address) {
const oThis = this;

await oThis._validate(address);
return oThis._build(address);
},
/**
* Delegates call to _build account proof to lib
* @param address
* @return {Promise<proof>}
*/
_build: async function (address) {
const oThis = this;

return proof.accountProof(address, oThis.trie);
},
/**
* Validate input
* @param address
* @private
*/
_validate: async function (address) {
const oThis = this;

let errorConf = {
internal_error_identifier: "s_p_ap_validate_2",
debug_options: {},
error_config: basicHelper.fetchErrorConfig()
};

if (!oThis.trie || oThis.trie.root === oThis.trie.EMPTY_TRIE_ROOT) {
errorConf.api_error_identifier = "tree_not_initialized";
let errorResponse = responseHelper.error(errorConf);
return Promise.reject(errorResponse);
}
if (address === undefined) {
logger.error("Account address is invalid");
errorConf.api_error_identifier = "account_address_undefined";
let errorResponse = responseHelper.error(errorConf);
return Promise.reject(errorResponse);
}
}
};

module.exports = AccountProof;

64 changes: 64 additions & 0 deletions lib/proof/helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const ethUtils = require('ethereumjs-util');

const rootPrefix = '../..'
, AccountProof = require(rootPrefix + '/lib/proof/account_proof')
;

/**
* Constructor for helper methods class - ProofHelperKlass
* @constructor
*/

const ProofHelperKlass = function () {
};

ProofHelperKlass.prototype = {

/**
* @notice left-pad value to make length 32 bytes
* @param value
* @return {string} padded value of length 32 bytes i.e. 64 nibbles
* @private
*/
_leftPad: function (value) {
return ("0000000000000000000000000000000000000000000000000000000000000000" + value).substring(value.length)
},
/**
*@notice generates storagePath of a variable in the storage
* @param storageIndex
* @param mappings, key of mapping variable
* @return {Buffer2}
*/
storagePath: function (storageIndex, mappings) {

let path = Buffer.from(this._leftPad(storageIndex), 'hex');
if (mappings && mappings.length > 0) {
mappings.map(mapping => {
path = Buffer.concat([Buffer.from(this._leftPad(mapping), 'hex'), path])
});
path = Buffer.from(ethUtils.sha3(path), 'hex')
}
path = Buffer.from(ethUtils.sha3(path), 'hex');
return path;
},

/**
* @notice generates storage root of a contract
* @param stateRoot
* @param contractAddress
* @param db level db instace
* @return {Promise<string>}
* @private
*/
fetchStorageRoot: async function (stateRoot, contractAddress, db) {

let accountProofInstance = new AccountProof(stateRoot, db)
, accountProof = await accountProofInstance.perform(contractAddress)
, accountValue = accountProof.toHash().data.value
, decodedValue = ethUtils.rlp.decode('0x' + accountValue);

return '0x' + decodedValue[2].toString('hex');
}
};

module.exports = new ProofHelperKlass();
87 changes: 87 additions & 0 deletions lib/proof/proof.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const rootPrefix = "../.."
, ethUtils = require('ethereumjs-util')
, logger = require(rootPrefix + '/helpers/custom_console_logger')
, responseHelper = require(rootPrefix + '/lib/formatter/response')
, basicHelper = require(rootPrefix + '/helpers/basic_helper')
;


function Proof() {

}


Proof.prototype = {
/**
* Generate Account proof for give address in merkel patricia tree
* @param address
* @param trie
* @return {Promise<proof>}
*/
accountProof: function (address, trie) {

let errorConf = {
internal_error_identifier: "l_p_accountProof_1",
debug_options: {},
error_config: basicHelper.fetchErrorConfig()
}
, path = Buffer.from(ethUtils.sha3(Buffer.from(address, 'hex')), 'hex');

logger.info('Generating account proof');
return new Promise(function (resolve, reject) {

return trie.findPath(path, function (error, accountNode, keyRemainder, rootToLeafPath) {
if (error || !accountNode || keyRemainder.length > 0) {
errorConf.api_error_identifier = "account_node_not_found";
errorConf.debug_options = {error};
let errorResponse = responseHelper.error(errorConf);
return reject(errorResponse);
}
let parentNodes = rootToLeafPath.map(node => node.raw)
, proof = {
address: address,
parentNodes: ethUtils.rlp.encode(parentNodes).toString('hex'),
value: accountNode.value.toString('hex')
};
return resolve(responseHelper.successWithData(proof));
});
});
},

/**
* Generate Storage proof for give storagePath in merkel patricia tree
* @param storagePath
* @param trie
* @return {Promise<proof>}
*/
storageProof: function (storagePath, trie) {

let errorConf = {
internal_error_identifier: "l_p_accountProof_2",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not so critical but like to mention here.

General observation: we follow a pattern to create internal_error_identifier.
Lets take this example, file path is lib/proof/proof.js, function name is storageProof
So the internal_error_identifier will be in this case: l_p_p_storageProof_1

If we are still following pattern then we need to update internal_error_identifier at multiple locations.

debug_options: {},
error_config: basicHelper.fetchErrorConfig()
};
return new Promise(function (resolve, reject) {
return trie.findPath(storagePath, function (error, storageNode, keyRemainder, rootToLeafPath) {

if (error || !storageNode || keyRemainder.length > 0) {
logger.error(error || 'Unable to find storage node in the tree');
errorConf.api_error_identifier = "storage_node_not_found";
errorConf.debug_options = {error};
let errorResponse = responseHelper.error(errorConf);
return reject(errorResponse);
}
let parentNodes = rootToLeafPath.map(node => node.raw)
, proof = {
parentNodes: ethUtils.rlp.encode(parentNodes).toString('hex'),
value: storageNode.value.toString('hex')
};
return resolve(responseHelper.successWithData(proof));
})
});
}

};

module.exports = new Proof();

Loading