diff --git a/README.md b/README.md index 9083b670a..99a98cf8a 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,10 @@ MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 DEBUG=false STAKE=250 CONSOLE_LOG=true ENAB You can override default port numbering system by setting `PORT` and `P2P_PORT` environment variables. Before starting node jobs, remove existing blockchain files and logs if necessary: ``` -rm -rf chains logs +rm -rf /path/to/data/dir logs ``` +The default blockchain data directory is ~/ain_blockchain_data (e.g. chain data will be at ~/ain_blockchain_data/chains). You can use a different directory by specifying the `BLOCKCHAIN_DATA_DIR` environment variable. + The default minimum size of the validator whitelist is 3. Change MIN_NUM_VALIDATORS parameter in the genesis-configs/base/genesis.json to change this value. You may also need to modify the GENESIS_WHITELIST and GENESIS_VALIDATORS accordingly. The genesis configs directory used is `genesis-configs/base` by default and it can be altered using `GENESIS_CONFIGS_DIR` env variable. For example, afan shard cluster can use the following command line: diff --git a/client/index.js b/client/index.js index 9788ba2af..9228d6eef 100755 --- a/client/index.js +++ b/client/index.js @@ -1,6 +1,5 @@ 'use strict'; -const process = require('process'); const express = require('express'); const jayson = require('jayson'); const _ = require('lodash'); diff --git a/client/protocol_versions.json b/client/protocol_versions.json index a204a4f6c..76a8660de 100644 --- a/client/protocol_versions.json +++ b/client/protocol_versions.json @@ -34,5 +34,14 @@ }, "0.7.3": { "min": "0.7.0" + }, + "0.7.4": { + "min": "0.7.0" + }, + "0.7.5": { + "min": "0.7.0" + }, + "0.7.6": { + "min": "0.7.0" } } \ No newline at end of file diff --git a/common/constants.js b/common/constants.js index 2040e5dca..8fb2e6b24 100644 --- a/common/constants.js +++ b/common/constants.js @@ -1,3 +1,4 @@ +const os = require('os'); const fs = require('fs'); const path = require('path'); const semver = require('semver'); @@ -63,7 +64,11 @@ if (!semver.valid(CONSENSUS_PROTOCOL_VERSION)) { throw Error('Wrong data version format is specified for CONSENSUS_PROTOCOL_VERSION'); } const LOGS_DIR = path.resolve(__dirname, '../logs'); -const CHAINS_DIR = path.resolve(__dirname, '../chains'); +const BLOCKCHAIN_DATA_DIR = process.env.BLOCKCHAIN_DATA_DIR || path.resolve(__dirname, '../ain_blockchain_data'); +if (!fs.existsSync(BLOCKCHAIN_DATA_DIR)) { + fs.mkdirSync(BLOCKCHAIN_DATA_DIR, { recursive: true }); +} +const CHAINS_DIR = path.resolve(BLOCKCHAIN_DATA_DIR, 'chains'); const CHAINS_N2B_DIR_NAME = 'n2b'; // NOTE: Block number to block. const CHAINS_H2N_DIR_NAME = 'h2n'; // NOTE: Block hash to block number. const HASH_DELIMITER = '#'; @@ -237,6 +242,7 @@ const AccountProperties = { const OwnerProperties = { ANYONE: '*', BRANCH_OWNER: 'branch_owner', + FID_PREFIX: 'fid:', OWNER: '.owner', OWNERS: 'owners', WRITE_FUNCTION: 'write_function', @@ -312,6 +318,7 @@ const NativeFunctionIds = { PAY: '_pay', RELEASE: '_release', SAVE_LAST_TX: '_saveLastTx', + SET_OWNER_CONFIG: '_setOwnerConfig', STAKE: '_stake', TRANSFER: '_transfer', UNSTAKE: '_unstake', @@ -645,6 +652,10 @@ function buildOwnerPermissions(branchOwner, writeFunction, writeOwner, writeRule }; } +function buildRulePermission(rule) { + return { [RuleProperties.WRITE]: rule }; +} + module.exports = { FeatureFlags, CURRENT_PROTOCOL_VERSION, @@ -700,6 +711,7 @@ module.exports = { GenesisOwners, GasFeeConstants, buildOwnerPermissions, + buildRulePermission, ...GenesisParams.blockchain, ...GenesisParams.consensus, ...GenesisParams.resource, diff --git a/common/network-util.js b/common/network-util.js new file mode 100644 index 000000000..084f5273c --- /dev/null +++ b/common/network-util.js @@ -0,0 +1,92 @@ +const _ = require('lodash'); +const axios = require('axios'); +const logger = require('../logger')('NETWORK-UTIL'); +const { + CURRENT_PROTOCOL_VERSION +} = require('../common/constants'); +const ChainUtil = require('../common/chain-util'); + + +async function _waitUntilTxFinalize(endpoint, txHash) { + while (true) { + const confirmed = await sendGetRequest( + endpoint, + 'ain_getTransactionByHash', + { hash: txHash } + ) + .then((resp) => { + return (_.get(resp, 'data.result.result.is_finalized', false) === true); + }) + .catch((err) => { + logger.error(`Failed to confirm transaction: ${err}`); + return false; + }); + if (confirmed) { + return true; + } + await ChainUtil.sleep(1000); + } +} + +async function sendTxAndWaitForFinalization(endpoint, tx, privateKey) { + const res = await signAndSendTx(endpoint, tx, privateKey); + if (_.get(res, 'errMsg', false) || !_.get(res, 'success', false)) { + throw Error(`Failed to sign and send tx: ${res.errMsg}`); + } + if (!(await _waitUntilTxFinalize(endpoint, _.get(res, 'txHash', null)))) { + throw Error('Transaction did not finalize in time.' + + 'Try selecting a different parent_chain_poc.'); + } +} + +async function sendSignedTx(endpoint, params) { + return await axios.post( + endpoint, + { + method: 'ain_sendSignedTransaction', + params, + jsonrpc: '2.0', + id: 0 + } + ).then((resp) => { + const result = _.get(resp, 'data.result.result.result', {}); + const success = !ChainUtil.isFailedTx(result); + return { success, errMsg: result.error_message }; + }).catch((err) => { + logger.error(`Failed to send transaction: ${err}`); + return { success: false, errMsg: err.message }; + }); +} + +// FIXME(minsulee2): this is duplicated function see: ./tools/util.js +async function signAndSendTx(endpoint, tx, privateKey) { + const { txHash, signedTx } = ChainUtil.signTransaction(tx, privateKey); + const result = await sendSignedTx(endpoint, signedTx); + return Object.assign(result, { txHash }); +} + +function sendGetRequest(endpoint, method, params) { + // NOTE(platfowner): .then() was used here to avoid some unexpected behavior of axios.post() + // (see https://github.com/ainblockchain/ain-blockchain/issues/101) + return axios.post( + endpoint, + { + method, + params: Object.assign(params, { protoVer: CURRENT_PROTOCOL_VERSION }), + jsonrpc: '2.0', + id: 0 + } + ).then((resp) => { + return resp; + }).catch((err) => { + logger.error(`Failed to send get request: ${err}`); + return null; + }); +} + +module.exports = { + sendTxAndWaitForFinalization, + sendSignedTx, + signAndSendTx, + sendGetRequest +}; diff --git a/common/path-util.js b/common/path-util.js index 172beffb5..d2b3a04a7 100644 --- a/common/path-util.js +++ b/common/path-util.js @@ -54,6 +54,10 @@ class PathUtil { return ChainUtil.formatPath([PredefinedDbPaths.MANAGE_APP, appName, PredefinedDbPaths.MANAGE_APP_CONFIG]); } + static getAppPath(appName) { + return ChainUtil.formatPath([PredefinedDbPaths.APPS, appName]); + } + static getAppAdminPathFromServiceAccountName(accountName) { return ruleUtil.getAppAdminPath(accountName); } diff --git a/common/version-util.js b/common/version-util.js index 1c9e70e4f..af2cef975 100644 --- a/common/version-util.js +++ b/common/version-util.js @@ -4,6 +4,14 @@ const { } = require('../common/constants'); class VersionUtil { + static isValidProtocolVersion(version) { + if (!version || !semver.valid(version)) { + return false; + } else { + return true; + } + } + static isValidVersionMatch(ver) { return ver && semver.valid(semver.coerce(ver.min)) && (!ver.max || semver.valid(semver.coerce(ver.max))); diff --git a/consensus/index.js b/consensus/index.js index ef68125f8..981884ced 100644 --- a/consensus/index.js +++ b/consensus/index.js @@ -34,7 +34,7 @@ const { const { signAndSendTx, sendGetRequest -} = require('../p2p/util'); +} = require('../common/network-util'); const PathUtil = require('../common/path-util'); const DB = require('../db'); const VersionUtil = require('../common/version-util'); diff --git a/db/functions.js b/db/functions.js index 313ca7732..3461f3fe8 100644 --- a/db/functions.js +++ b/db/functions.js @@ -15,15 +15,18 @@ const { AccountProperties, TokenExchangeSchemes, FunctionProperties, + OwnerProperties, GasFeeConstants, REST_FUNCTION_CALL_TIMEOUT_MS, + buildOwnerPermissions, + buildRulePermission, } = require('../common/constants'); const ChainUtil = require('../common/chain-util'); const PathUtil = require('../common/path-util'); const { sendSignedTx, signAndSendTx -} = require('../p2p/util'); +} = require('../common/network-util'); const Transaction = require('../tx-pool/transaction'); const parentChainEndpoint = GenesisSharding[ShardingProperties.PARENT_CHAIN_POC] + '/json-rpc'; @@ -38,47 +41,57 @@ const EventListenerWhitelist = { * Built-in functions with function paths. */ // NOTE(platfowner): ownerOnly means that the function can be set only by the blockchain owner. -// NOTE(platfowner): execGasAmount means the amount of gas required to execute the function, which -// reflects the number of database write operations and external RPC calls. +// NOTE(platfowner): extraGasAmount means the extra gas amount required to execute the function, +// which often reflects the external RPC calls needed. class Functions { constructor(db, tp) { this.db = db; this.tp = tp; this.nativeFunctionMap = { [NativeFunctionIds.CLAIM]: { - func: this._claim.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._claim.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.CLOSE_CHECKIN]: { - func: this._closeCheckin.bind(this), ownerOnly: true, execGasAmount: 10 }, + func: this._closeCheckin.bind(this), ownerOnly: true, extraGasAmount: 10 }, [NativeFunctionIds.COLLECT_FEE]: { - func: this._collectFee.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._collectFee.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.CREATE_APP]: { - func: this._createApp.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._createApp.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.DISTRIBUTE_FEE]: { - func: this._distributeFee.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._distributeFee.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.ERASE_VALUE]: { - func: this._eraseValue.bind(this), ownerOnly: false, execGasAmount: 0 }, + func: this._eraseValue.bind(this), ownerOnly: false, extraGasAmount: 0 }, [NativeFunctionIds.HOLD]: { - func: this._hold.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._hold.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.OPEN_CHECKIN]: { - func: this._openCheckin.bind(this), ownerOnly: true, execGasAmount: 60 }, + func: this._openCheckin.bind(this), ownerOnly: true, extraGasAmount: 60 }, [NativeFunctionIds.PAY]: { - func: this._pay.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._pay.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.RELEASE]: { - func: this._release.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._release.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.SAVE_LAST_TX]: { - func: this._saveLastTx.bind(this), ownerOnly: false, execGasAmount: 0 }, + func: this._saveLastTx.bind(this), ownerOnly: false, extraGasAmount: 0 }, + [NativeFunctionIds.SET_OWNER_CONFIG]: { + func: this._setOwnerConfig.bind(this), ownerOnly: false, extraGasAmount: 0 }, [NativeFunctionIds.STAKE]: { - func: this._stake.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._stake.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.UNSTAKE]: { - func: this._unstake.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._unstake.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.TRANSFER]: { - func: this._transfer.bind(this), ownerOnly: true, execGasAmount: 0 }, + func: this._transfer.bind(this), ownerOnly: true, extraGasAmount: 0 }, [NativeFunctionIds.UPDATE_LATEST_SHARD_REPORT]: { - func: this._updateLatestShardReport.bind(this), ownerOnly: false, execGasAmount: 0 }, + func: this._updateLatestShardReport.bind(this), ownerOnly: false, extraGasAmount: 0 }, }; this.callStack = []; } + static isNativeFunctionId(fid) { + if (!fid) { + return false; + } + const fidList = Object.values(NativeFunctionIds); + return fidList.find((elem) => elem === fid) !== undefined; + } + /** * Runs functions of function paths matched with given database path. * @@ -90,7 +103,6 @@ class Functions { */ // NOTE(platfowner): Validity checks on individual addresses are done by .write rules. // TODO(platfowner): Trigger subtree functions. - // TODO(platfowner): Add account registration gas amount. triggerFunctions(parsedValuePath, value, auth, timestamp, transaction) { // NOTE(platfowner): It is assumed that the given transaction is in an executable form. const executedAt = transaction.extra.executed_at; @@ -219,7 +231,7 @@ class Functions { const fidList = topCall ? Array.from(topCall.fidList) : []; fidList.push(fid); const callDepth = this.callStackSize(); - const gasAmount = nativeFunction.execGasAmount; + const gasAmount = nativeFunction.extraGasAmount; this.callStack.push({ fid, fidList, @@ -315,45 +327,6 @@ class Functions { return null; } - /** - * Returns a new function created by applying the function change to the current function. - * - * @param {Object} curFunction current function (to be modified and returned by this function) - * @param {Object} functionChange function change - */ - static applyFunctionChange(curFunction, functionChange) { - if (curFunction === null) { - // Just write the function change. - return functionChange; - } - if (functionChange === null) { - // Just delete the existing value. - return null; - } - const funcChangeMap = ChainUtil.getJsObject(functionChange, [FunctionProperties.FUNCTION]); - if (!funcChangeMap || Object.keys(funcChangeMap).length === 0) { - return curFunction; - } - const newFunction = - ChainUtil.isDict(curFunction) ? JSON.parse(JSON.stringify(curFunction)) : {}; - let newFuncMap = ChainUtil.getJsObject(newFunction, [FunctionProperties.FUNCTION]); - if (!newFuncMap || !ChainUtil.isDict(newFunction)) { - // Add a place holder. - ChainUtil.setJsObject(newFunction, [FunctionProperties.FUNCTION], {}); - newFuncMap = ChainUtil.getJsObject(newFunction, [FunctionProperties.FUNCTION]); - } - for (const functionKey in funcChangeMap) { - const functionValue = funcChangeMap[functionKey]; - if (functionValue === null) { - delete newFuncMap[functionKey]; - } else { - newFuncMap[functionKey] = functionValue; - } - } - - return newFunction; - } - static convertPathVars2Params(pathVars) { const params = {}; if (ChainUtil.isDict(pathVars)) { @@ -416,10 +389,32 @@ class Functions { return this.setValueOrLog(transferPath, value, context); } + setOwnerOrLog(ownerPath, owner, context) { + const auth = context.auth; + const result = this.db.setOwner(ownerPath, owner, auth); + if (ChainUtil.isFailedTx(result)) { + logger.error( + ` ==> Failed to setOwner on '${ownerPath}' with error: ${JSON.stringify(result)}`); + } + Functions.addToOpResultList(ownerPath, result, context); + return result; + } + + setRuleOrLog(rulePath, rule, context) { + const auth = context.auth; + const result = this.db.setRule(rulePath, rule, auth); + if (ChainUtil.isFailedTx(result)) { + logger.error( + ` ==> Failed to setRule on '${rulePath}' with error: ${JSON.stringify(result)}`); + } + Functions.addToOpResultList(rulePath, result, context); + return result; + } + buildFuncResultToReturn(context, code, extraGasAmount = 0) { const result = { code, - gas_amount: this.nativeFunctionMap[context.fid].execGasAmount + gas_amount: this.nativeFunctionMap[context.fid].extraGasAmount }; if (ChainUtil.isNumber(extraGasAmount) && extraGasAmount > 0) { result.gas_amount += extraGasAmount; @@ -494,6 +489,28 @@ class Functions { } } + /** + * Sets owner config on the path. + * This is often used for testing purposes. + */ + _setOwnerConfig(value, context) { + const parsedValuePath = context.valuePath; + const auth = context.auth; + const owner = { + [OwnerProperties.OWNER]: { + [OwnerProperties.OWNERS]: { + [auth.addr]: buildOwnerPermissions(false, true, true, true), + [OwnerProperties.ANYONE]: buildOwnerPermissions(false, true, true, true), + } + } + }; + const result = this.setOwnerOrLog(ChainUtil.formatPath(parsedValuePath), owner, context); + if (!ChainUtil.isFailedTx(result)) { + return this.returnFuncResult(context, FunctionResultCode.SUCCESS); + } else { + return this.returnFuncResult(context, FunctionResultCode.FAILURE); + } + } _transfer(value, context) { const from = context.params.from; @@ -540,6 +557,18 @@ class Functions { } if (adminConfig) { sanitizedVal[PredefinedDbPaths.MANAGE_APP_CONFIG_ADMIN] = adminConfig; + const appPath = PathUtil.getAppPath(appName); + const owner = {}; + let rule = ''; + const adminAddrList = Object.keys(adminConfig); + for (let i = 0; i < adminAddrList.length; i++) { + const addr = adminAddrList[i]; + ChainUtil.setJsObject(owner, [OwnerProperties.OWNER, OwnerProperties.OWNERS, addr], + buildOwnerPermissions(true, true, true, true)); + rule += `auth.addr === '${addr}'` + (i < adminAddrList.length - 1 ? ' || ' : ''); + } + this.setRuleOrLog(appPath, buildRulePermission(rule), context); + this.setOwnerOrLog(appPath, owner, context); } if (billingConfig) { sanitizedVal[PredefinedDbPaths.MANAGE_APP_CONFIG_BILLING] = billingConfig; diff --git a/db/index.js b/db/index.js index f10a8c845..c556f80aa 100644 --- a/db/index.js +++ b/db/index.js @@ -32,6 +32,10 @@ const { isWritablePathWithSharding, isValidPathForStates, isValidJsObjectForStates, + isValidRuleTree, + isValidFunctionTree, + isValidOwnerTree, + applyFunctionChange, setProofHashForStateTree, updateProofHashForAllRootPaths, } = require('./state-util'); @@ -692,6 +696,10 @@ class DB { if (!isValidObj.isValid) { return ChainUtil.returnTxResult(401, `Invalid object for states: ${isValidObj.invalidPath}`); } + const isValidFunction = isValidFunctionTree(functionChange); + if (!isValidFunction.isValid) { + return ChainUtil.returnTxResult(405, `Invalid function tree: ${isValidFunction.invalidPath}`); + } const parsedPath = ChainUtil.parsePath(functionPath); const isValidPath = isValidPathForStates(parsedPath); if (!isValidPath.isValid) { @@ -714,7 +722,7 @@ class DB { return ChainUtil.returnTxResult(404, `No write_function permission on: ${functionPath}`); } const curFunction = this.getFunction(functionPath, isGlobal); - const newFunction = Functions.applyFunctionChange(curFunction, functionChange); + const newFunction = applyFunctionChange(curFunction, functionChange); const fullPath = DB.getFullPath(localPath, PredefinedDbPaths.FUNCTIONS_ROOT); this.writeDatabase(fullPath, newFunction); return ChainUtil.returnTxResult(0, null, 1); @@ -727,6 +735,10 @@ class DB { if (!isValidObj.isValid) { return ChainUtil.returnTxResult(501, `Invalid object for states: ${isValidObj.invalidPath}`); } + const isValidRule = isValidRuleTree(rule); + if (!isValidRule.isValid) { + return ChainUtil.returnTxResult(504, `Invalid rule tree: ${isValidRule.invalidPath}`); + } const parsedPath = ChainUtil.parsePath(rulePath); const isValidPath = isValidPathForStates(parsedPath); if (!isValidPath.isValid) { @@ -746,12 +758,15 @@ class DB { return ChainUtil.returnTxResult(0, null, 1); } - // TODO(platfowner): Add owner config sanitization logic. setOwner(ownerPath, owner, auth, isGlobal) { const isValidObj = isValidJsObjectForStates(owner); if (!isValidObj.isValid) { return ChainUtil.returnTxResult(601, `Invalid object for states: ${isValidObj.invalidPath}`); } + const isValidOwner = isValidOwnerTree(owner); + if (!isValidOwner.isValid) { + return ChainUtil.returnTxResult(604, `Invalid owner tree: ${isValidOwner.invalidPath}`); + } const parsedPath = ChainUtil.parsePath(ownerPath); const isValidPath = isValidPathForStates(parsedPath); if (!isValidPath.isValid) { @@ -1387,7 +1402,11 @@ class DB { let permissions = null; // Step 1: Check if the given address or fid exists in owners. if (auth) { - if (auth.addr) { + // Step 1.1: Try to use the auth fid first. + if (auth.fid) { + permissions = owners[OwnerProperties.FID_PREFIX + auth.fid]; + // Step 1.2: Try to use the auth address then. + } else if (auth.addr) { permissions = owners[auth.addr]; } else { return null; diff --git a/db/rule-util.js b/db/rule-util.js index 053bd6101..1314f45ec 100644 --- a/db/rule-util.js +++ b/db/rule-util.js @@ -30,6 +30,7 @@ class RuleUtil { isEmpty(value) { return value === null || value === undefined || + (this.isArray(value) && value.length === 0) || (this.isDict(value) && Object.keys(value).length === 0); } diff --git a/db/state-util.js b/db/state-util.js index b6ed797c1..f9bc16181 100644 --- a/db/state-util.js +++ b/db/state-util.js @@ -1,13 +1,17 @@ /* eslint guard-for-in: "off" */ const logger = require('../logger')('STATE_UTIL'); +const _ = require('lodash'); +const validUrl = require('valid-url'); const ChainUtil = require('../common/chain-util'); const { FunctionProperties, + FunctionTypes, RuleProperties, OwnerProperties, ShardingProperties, } = require('../common/constants'); +const Functions = require('./functions'); function isEmptyNode(node) { return node.getIsLeaf() && node.getValue() === null; @@ -85,13 +89,13 @@ function isWritablePathWithSharding(fullPath, root) { } function hasReservedChar(label) { - const reservedCharRegex = /[\/\.\$\*#\{\}\[\]\x00-\x1F\x7F]/gm; + const reservedCharRegex = /[\/\.\$\*#\{\}\[\]<>'"` \x00-\x1F\x7F]/gm; return ChainUtil.isString(label) ? reservedCharRegex.test(label) : false; } function hasAllowedPattern(label) { const wildCardPatternRegex = /^\*$/gm; - const configPatternRegex = /^[\.\$]{1}[^\/\.\$\*#\{\}\[\]\x00-\x1F\x7F]+$/gm; + const configPatternRegex = /^[\.\$]{1}[^\/\.\$\*#\{\}\[\]<>'"` \x00-\x1F\x7F]+$/gm; return ChainUtil.isString(label) ? (wildCardPatternRegex.test(label) || configPatternRegex.test(label)) : false; } @@ -115,37 +119,290 @@ function isValidPathForStates(fullPath) { break; } } - return {isValid, invalidPath: isValid ? '' : ChainUtil.formatPath(path)}; + return { isValid, invalidPath: isValid ? '' : ChainUtil.formatPath(path) }; } function isValidJsObjectForStatesRecursive(obj, path) { if (ChainUtil.isDict(obj)) { if (ChainUtil.isEmpty(obj)) { - return false; + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; } for (const key in obj) { path.push(key); if (!isValidStateLabel(key)) { - return false; + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; } const childObj = obj[key]; const isValidChild = isValidJsObjectForStatesRecursive(childObj, path); - if (!isValidChild) { - return false; + if (!isValidChild.isValid) { + return isValidChild; } path.pop(); } - return true; } else { - return ChainUtil.isBool(obj) || ChainUtil.isNumber(obj) || ChainUtil.isString(obj) || - obj === null; + if (!ChainUtil.isBool(obj) && !ChainUtil.isNumber(obj) && !ChainUtil.isString(obj) && + obj !== null) { + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; + } } + + return { isValid: true, invalidPath: '' }; } function isValidJsObjectForStates(obj) { + return isValidJsObjectForStatesRecursive(obj, []); +} + +/** + * Checks the validity of the given rule configuration. + */ + function isValidRuleConfig(ruleConfig) { + if (!ChainUtil.isBool(ruleConfig) && !ChainUtil.isString(ruleConfig)) { + return { isValid: false, invalidPath: ChainUtil.formatPath([]) }; + } + + return { isValid: true, invalidPath: '' }; +} + +function sanitizeFunctionInfo(functionInfo) { + if (!functionInfo) { + return null; + } + + const functionType = functionInfo[FunctionProperties.FUNCTION_TYPE]; + const sanitized = {}; + if (functionType === FunctionTypes.NATIVE) { + sanitized[FunctionProperties.FUNCTION_TYPE] = functionType; + sanitized[FunctionProperties.FUNCTION_ID] = + ChainUtil.stringOrEmpty(functionInfo[FunctionProperties.FUNCTION_ID]); + } else if (functionType === FunctionTypes.REST) { + sanitized[FunctionProperties.FUNCTION_TYPE] = functionType; + sanitized[FunctionProperties.FUNCTION_ID] = + ChainUtil.stringOrEmpty(functionInfo[FunctionProperties.FUNCTION_ID]); + sanitized[FunctionProperties.EVENT_LISTENER] = + ChainUtil.stringOrEmpty(functionInfo[FunctionProperties.EVENT_LISTENER]); + sanitized[FunctionProperties.SERVICE_NAME] = + ChainUtil.stringOrEmpty(functionInfo[FunctionProperties.SERVICE_NAME]); + } + + return sanitized; +} + +function isValidFunctionInfo(functionInfo) { + if (ChainUtil.isEmpty(functionInfo)) { + return false; + } + const sanitized = sanitizeFunctionInfo(functionInfo); + const isIdentical = + _.isEqual(JSON.parse(JSON.stringify(sanitized)), functionInfo, { strict: true }); + if (!isIdentical) { + return false; + } + const eventListener = functionInfo[FunctionProperties.EVENT_LISTENER]; + if (eventListener !== undefined && + !validUrl.isUri(functionInfo[FunctionProperties.EVENT_LISTENER])) { + return false; + } + return true; +} + +/** + * Checks the validity of the given function configuration. + */ +function isValidFunctionConfig(functionConfig) { + if (!ChainUtil.isDict(functionConfig)) { + return { isValid: false, invalidPath: ChainUtil.formatPath([]) }; + } + const fidList = Object.keys(functionConfig); + if (ChainUtil.isEmpty(fidList)) { + return { isValid: false, invalidPath: ChainUtil.formatPath([]) }; + } + for (const fid of fidList) { + const invalidPath = ChainUtil.formatPath([fid]); + const functionInfo = functionConfig[fid]; + if (functionInfo === null) { + // Function deletion. + continue; + } + if (!isValidFunctionInfo(functionInfo)) { + return { isValid: false, invalidPath }; + } + if (functionInfo[FunctionProperties.FUNCTION_ID] !== fid) { + return { + isValid: false, + invalidPath: ChainUtil.formatPath([fid, FunctionProperties.FUNCTION_ID]) + }; + } + } + + return { isValid: true, invalidPath: '' }; +} + +function sanitizeOwnerPermissions(ownerPermissions) { + if (!ownerPermissions) { + return null; + } + return { + [OwnerProperties.BRANCH_OWNER]: + ChainUtil.boolOrFalse(ownerPermissions[OwnerProperties.BRANCH_OWNER]), + [OwnerProperties.WRITE_FUNCTION]: + ChainUtil.boolOrFalse(ownerPermissions[OwnerProperties.WRITE_FUNCTION]), + [OwnerProperties.WRITE_OWNER]: + ChainUtil.boolOrFalse(ownerPermissions[OwnerProperties.WRITE_OWNER]), + [OwnerProperties.WRITE_RULE]: + ChainUtil.boolOrFalse(ownerPermissions[OwnerProperties.WRITE_RULE]), + }; +} + +function isValidOwnerPermissions(ownerPermissions) { + if (ChainUtil.isEmpty(ownerPermissions)) { + return false; + } + const sanitized = sanitizeOwnerPermissions(ownerPermissions); + const isIdentical = + _.isEqual(JSON.parse(JSON.stringify(sanitized)), ownerPermissions, { strict: true }); + return isIdentical; +} + +/** + * Checks the validity of the given owner configuration. + */ +function isValidOwnerConfig(ownerConfig) { + if (!ChainUtil.isDict(ownerConfig)) { + return { isValid: false, invalidPath: ChainUtil.formatPath([]) }; + } const path = []; - const isValid = isValidJsObjectForStatesRecursive(obj, path); - return {isValid, invalidPath: isValid ? '' : ChainUtil.formatPath(path)}; + const ownersProp = ownerConfig[OwnerProperties.OWNERS]; + if (ownersProp === undefined) { + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; + } + path.push(OwnerProperties.OWNERS); + if (!ChainUtil.isDict(ownersProp)) { + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; + } + const ownerList = Object.keys(ownersProp); + if (ChainUtil.isEmpty(ownerList)) { + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; + } + for (const owner of ownerList) { + const invalidPath = ChainUtil.formatPath([...path, owner]); + if (owner !== OwnerProperties.ANYONE && !ChainUtil.isCksumAddr(owner)) { + if (!owner.startsWith(OwnerProperties.FID_PREFIX)) { + return { isValid: false, invalidPath }; + } + const fid = owner.substring(OwnerProperties.FID_PREFIX.length); + if (!Functions.isNativeFunctionId(fid)) { + return { isValid: false, invalidPath }; + } + } + const ownerPermissions = ChainUtil.getJsObject(ownerConfig, [...path, owner]); + if (!isValidOwnerPermissions(ownerPermissions)) { + return { isValid: false, invalidPath }; + } + } + + return { isValid: true, invalidPath: '' }; +} + +function isValidConfigTreeRecursive(stateTree, path, configLabel, stateConfigValidator) { + if (!ChainUtil.isDict(stateTree) || ChainUtil.isEmpty(stateTree)) { + return { isValid: false, invalidPath: ChainUtil.formatPath(path) }; + } + + for (const label in stateTree) { + path.push(label); + const subtree = stateTree[label]; + if (label === configLabel) { + const isValidConfig = stateConfigValidator(subtree); + if (!isValidConfig.isValid) { + return { + isValid: false, + invalidPath: ChainUtil.appendPath(ChainUtil.formatPath(path), isValidConfig.invalidPath) + }; + } + } else { + const isValidSubtree = + isValidConfigTreeRecursive(subtree, path, configLabel, stateConfigValidator); + if (!isValidSubtree.isValid) { + return isValidSubtree; + } + } + path.pop(); + } + + return { isValid: true, invalidPath: '' }; +} + +/** + * Checks the validity of the given rule tree. + */ +function isValidRuleTree(ruleTree) { + if (ruleTree === null) { + return { isValid: true, invalidPath: '' }; + } + + return isValidConfigTreeRecursive(ruleTree, [], RuleProperties.WRITE, isValidRuleConfig); +} + +/** + * Checks the validity of the given function tree. + */ +function isValidFunctionTree(functionTree) { + if (functionTree === null) { + return { isValid: true, invalidPath: '' }; + } + + return isValidConfigTreeRecursive( + functionTree, [], FunctionProperties.FUNCTION, isValidFunctionConfig); +} + +/** + * Checks the validity of the given owner tree. + */ +function isValidOwnerTree(ownerTree) { + if (ownerTree === null) { + return { isValid: true, invalidPath: '' }; + } + + return isValidConfigTreeRecursive(ownerTree, [], OwnerProperties.OWNER, isValidOwnerConfig); +} + +/** + * Returns a new function created by applying the function change to the current function. + * @param {Object} curFunction current function (to be modified and returned by this function) + * @param {Object} functionChange function change + */ +function applyFunctionChange(curFunction, functionChange) { + if (curFunction === null) { + // Just write the function change. + return functionChange; + } + if (functionChange === null) { + // Just delete the existing value. + return null; + } + const funcChangeMap = ChainUtil.getJsObject(functionChange, [FunctionProperties.FUNCTION]); + if (!funcChangeMap || Object.keys(funcChangeMap).length === 0) { + return curFunction; + } + const newFunction = + ChainUtil.isDict(curFunction) ? JSON.parse(JSON.stringify(curFunction)) : {}; + let newFuncMap = ChainUtil.getJsObject(newFunction, [FunctionProperties.FUNCTION]); + if (!newFuncMap || !ChainUtil.isDict(newFunction)) { + // Add a place holder. + ChainUtil.setJsObject(newFunction, [FunctionProperties.FUNCTION], {}); + newFuncMap = ChainUtil.getJsObject(newFunction, [FunctionProperties.FUNCTION]); + } + for (const functionKey in funcChangeMap) { + const functionValue = funcChangeMap[functionKey]; + if (functionValue === null) { + delete newFuncMap[functionKey]; + } else { + newFuncMap[functionKey] = functionValue; + } + } + + return newFunction; } /** @@ -338,6 +595,13 @@ module.exports = { isValidStateLabel, isValidPathForStates, isValidJsObjectForStates, + isValidRuleConfig, + isValidRuleTree, + isValidFunctionConfig, + isValidFunctionTree, + isValidOwnerConfig, + isValidOwnerTree, + applyFunctionChange, setStateTreeVersion, renameStateTreeVersion, deleteStateTree, diff --git a/deploy_blockchain_gcp.sh b/deploy_blockchain_gcp.sh index b5e9a2d29..f798bfdf7 100644 --- a/deploy_blockchain_gcp.sh +++ b/deploy_blockchain_gcp.sh @@ -39,9 +39,6 @@ fi FILES_FOR_TRACKER="blockchain/ client/ common/ consensus/ db/ genesis-configs/ logger/ tracker-server/ package.json setup_tracker_gcp.sh setup_blockchain_ubuntu.sh start_tracker_gcp.sh" FILES_FOR_NODE="blockchain/ client/ common/ consensus/ db/ json_rpc/ genesis-configs/ logger/ node/ tx-pool/ p2p/ package.json setup_node_gcp.sh setup_blockchain_ubuntu.sh start_node_gcp.sh" -printf "\nRemoving redundant files..." -rm -rf chains logs - TRACKER_TARGET_ADDR="${GCP_USER}@${SEASON}-tracker-taiwan" NODE_0_TARGET_ADDR="${GCP_USER}@${SEASON}-node-0-taiwan" NODE_1_TARGET_ADDR="${GCP_USER}@${SEASON}-node-1-oregon" @@ -49,13 +46,20 @@ NODE_2_TARGET_ADDR="${GCP_USER}@${SEASON}-node-2-singapore" NODE_3_TARGET_ADDR="${GCP_USER}@${SEASON}-node-3-iowa" NODE_4_TARGET_ADDR="${GCP_USER}@${SEASON}-node-4-netherlands" +TRACKER_ZONE="asia-east1-b" +NODE_0_ZONE="asia-east1-b" +NODE_1_ZONE="us-west1-b" +NODE_2_ZONE="asia-southeast1-b" +NODE_3_ZONE="us-central1-a" +NODE_4_ZONE="europe-west4-a" + # kill any processes still alive -gcloud compute ssh $TRACKER_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_0_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_1_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_2_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_3_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_4_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID +gcloud compute ssh $TRACKER_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $TRACKER_ZONE +gcloud compute ssh $NODE_0_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_0_ZONE +gcloud compute ssh $NODE_1_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_1_ZONE +gcloud compute ssh $NODE_2_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_2_ZONE +gcloud compute ssh $NODE_3_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_3_ZONE +gcloud compute ssh $NODE_4_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_4_ZONE if [ "$NUM_SHARDS" -gt 0 ]; then for i in $(seq $NUM_SHARDS) @@ -67,60 +71,60 @@ if [ "$NUM_SHARDS" -gt 0 ]; then SHARD_NODE_1_TARGET_ADDR="${GCP_USER}@${SEASON}-shard-${i}-node-1-oregon" SHARD_NODE_2_TARGET_ADDR="${GCP_USER}@${SEASON}-shard-${i}-node-2-singapore" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command "killall node" --project $PROJECT_ID - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command "killall node" --project $PROJECT_ID - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command "killall node" --project $PROJECT_ID - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command "killall node" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $TRACKER_ZONE + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_0_ZONE + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_1_ZONE + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command "sudo killall node" --project $PROJECT_ID --zone $NODE_2_ZONE done fi # deploy files to GCP instances printf "\nDeploying parent blockchain..." printf "\nDeploying files to ${TRACKER_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_TRACKER ${TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_TRACKER ${TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $TRACKER_ZONE printf "\nDeploying files to ${NODE_0_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_0_ZONE printf "\nDeploying files to ${NODE_1_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_1_ZONE printf "\nDeploying files to ${NODE_2_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_2_ZONE printf "\nDeploying files to ${NODE_3_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_3_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_3_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_3_ZONE printf "\nDeploying files to ${NODE_4_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_4_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_4_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_4_ZONE # ssh into each instance, set up the ubuntu VM instance (ONLY NEEDED FOR THE FIRST TIME) if [ "$4" == "--setup" ]; then printf "\n\n##########################\n# Setting up parent tracker #\n###########################\n\n" - gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Setting up parent node 0 #\n##########################\n\n" - gcloud compute ssh $NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n##########################\n# Setting up parent node 1 #\n##########################\n\n" - gcloud compute ssh $NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Setting up parent node 2 #\n##########################\n\n" - gcloud compute ssh $NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_2_ZONE printf "\n\n##########################\n# Setting up parent node 3 #\n##########################\n\n" - gcloud compute ssh $NODE_3_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_3_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_3_ZONE printf "\n\n##########################\n# Setting up parent node 4 #\n##########################\n\n" - gcloud compute ssh $NODE_4_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_4_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_4_ZONE fi # ssh into each instance, install packages and start up the server printf "\n\n############################\n# Running parent tracker #\n############################\n\n" -gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID +gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n###########################\n# Running parent node 0 #\n###########################\n\n" -gcloud compute ssh $NODE_0_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 0" --project $PROJECT_ID +gcloud compute ssh $NODE_0_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 0" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n#########################\n# Running parent node 1 #\n#########################\n\n" -gcloud compute ssh $NODE_1_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 1" --project $PROJECT_ID +gcloud compute ssh $NODE_1_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 1" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n#########################\n# Running parent node 2 #\n#########################\n\n" -gcloud compute ssh $NODE_2_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 2" --project $PROJECT_ID +gcloud compute ssh $NODE_2_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 2" --project $PROJECT_ID --zone $NODE_2_ZONE printf "\n\n#########################\n# Running parent node 3 #\n#########################\n\n" -gcloud compute ssh $NODE_3_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 3" --project $PROJECT_ID +gcloud compute ssh $NODE_3_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 3" --project $PROJECT_ID --zone $NODE_3_ZONE printf "\n\n#########################\n# Running parent node 4 #\n#########################\n\n" -gcloud compute ssh $NODE_4_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 4" --project $PROJECT_ID +gcloud compute ssh $NODE_4_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON 0 4" --project $PROJECT_ID --zone $NODE_4_ZONE -printf "\nDeploying shard blockchains..." if [ "$NUM_SHARDS" -gt 0 ]; then + printf "\nDeploying shard blockchains..." for i in $(seq $NUM_SHARDS) do echo "shard #$i" @@ -137,34 +141,34 @@ if [ "$NUM_SHARDS" -gt 0 ]; then # deploy files to GCP instances printf "\nDeploying files to ${SHARD_TRACKER_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_TRACKER ${SHARD_TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_TRACKER ${SHARD_TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $TRACKER_ZONE printf "\nDeploying files to ${SHARD_NODE_0_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_0_ZONE printf "\nDeploying files to ${SHARD_NODE_1_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_1_ZONE printf "\nDeploying files to ${SHARD_NODE_2_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_2_ZONE # ssh into each instance, set up the ubuntu VM instance (ONLY NEEDED FOR THE FIRST TIME) if [ "$4" == "--setup" ]; then printf "\n\n###########################\n# Setting up shard_$i tracker #\n###########################\n\n" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Setting up shard_$i node 0 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n##########################\n# Setting up shard_$i node 1 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Setting up shard_$i node 2 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_2_ZONE fi # ssh into each instance, install packages and start up the server printf "\n\n###########################\n# Running shard_$i tracker #\n###########################\n\n" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Running shard_$i node 0 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON $i 0" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON $i 0" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n##########################\n# Running shard_$i node 1 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON $i 1" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON $i 1" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Running shard_$i node 2 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON $i 2" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". setup_node_gcp.sh && . start_node_gcp.sh $SEASON $i 2" --project $PROJECT_ID --zone $NODE_2_ZONE done fi diff --git a/deploy_blockchain_incremental_gcp.sh b/deploy_blockchain_incremental_gcp.sh index 60c7867c7..55e4b8e5b 100644 --- a/deploy_blockchain_incremental_gcp.sh +++ b/deploy_blockchain_incremental_gcp.sh @@ -39,9 +39,6 @@ fi FILES_FOR_TRACKER="blockchain/ client/ common/ consensus/ db/ genesis-configs/ logger/ tracker-server/ package.json setup_tracker_gcp.sh setup_blockchain_ubuntu.sh start_tracker_gcp.sh" FILES_FOR_NODE="blockchain/ client/ common/ consensus/ db/ json_rpc/ genesis-configs/ logger/ node/ tx-pool/ p2p/ package.json setup_blockchain_ubuntu.sh start_node_incremental_gcp.sh" -printf "\nRemoving redundant files..." -rm -rf chains logs - TRACKER_TARGET_ADDR="${GCP_USER}@${SEASON}-tracker-taiwan" NODE_0_TARGET_ADDR="${GCP_USER}@${SEASON}-node-0-taiwan" NODE_1_TARGET_ADDR="${GCP_USER}@${SEASON}-node-1-oregon" @@ -49,53 +46,61 @@ NODE_2_TARGET_ADDR="${GCP_USER}@${SEASON}-node-2-singapore" NODE_3_TARGET_ADDR="${GCP_USER}@${SEASON}-node-3-iowa" NODE_4_TARGET_ADDR="${GCP_USER}@${SEASON}-node-4-netherlands" +TRACKER_ZONE="asia-east1-b" +NODE_0_ZONE="asia-east1-b" +NODE_1_ZONE="us-west1-b" +NODE_2_ZONE="asia-southeast1-b" +NODE_3_ZONE="us-central1-a" +NODE_4_ZONE="europe-west4-a" + # 1. Copy files to gcp printf "\nDeploying parent blockchain..." printf "\nCopying files to ${TRACKER_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_TRACKER ${TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_TRACKER ${TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $TRACKER_ZONE printf "\nCopying files to ${NODE_0_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_0_ZONE printf "\nCopying files to ${NODE_1_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_1_ZONE printf "\nCopying files to ${NODE_2_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_2_ZONE printf "\nCopying files to ${NODE_3_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_3_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_3_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_3_ZONE printf "\nCopying files to ${NODE_4_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_4_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_NODE ${NODE_4_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_4_ZONE # ssh into each instance, set up the ubuntu VM instance (ONLY NEEDED FOR THE FIRST TIME) if [ "$4" == "--setup" ]; then printf "\n\n##########################\n# Setting up parent tracker #\n###########################\n\n" - gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Setting up parent node 0 #\n##########################\n\n" - gcloud compute ssh $NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n##########################\n# Setting up parent node 1 #\n##########################\n\n" - gcloud compute ssh $NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Setting up parent node 2 #\n##########################\n\n" - gcloud compute ssh $NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_2_ZONE printf "\n\n##########################\n# Setting up parent node 3 #\n##########################\n\n" - gcloud compute ssh $NODE_3_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_3_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_3_ZONE printf "\n\n##########################\n# Setting up parent node 4 #\n##########################\n\n" - gcloud compute ssh $NODE_4_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $NODE_4_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_4_ZONE fi # 2. Set up parent chain printf "\n\n############################\n# Running parent tracker #\n############################\n\n" -gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID +gcloud compute ssh $TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n###########################\n# Running parent node 0 #\n###########################\n\n" -gcloud compute ssh $NODE_0_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 0" --project $PROJECT_ID +gcloud compute ssh $NODE_0_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 0" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n#########################\n# Running parent node 1 #\n#########################\n\n" -gcloud compute ssh $NODE_1_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 1" --project $PROJECT_ID +gcloud compute ssh $NODE_1_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 1" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n#########################\n# Running parent node 2 #\n#########################\n\n" -gcloud compute ssh $NODE_2_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 2" --project $PROJECT_ID +gcloud compute ssh $NODE_2_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 2" --project $PROJECT_ID --zone $NODE_2_ZONE printf "\n\n#########################\n# Running parent node 3 #\n#########################\n\n" -gcloud compute ssh $NODE_3_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 3" --project $PROJECT_ID +gcloud compute ssh $NODE_3_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 3" --project $PROJECT_ID --zone $NODE_3_ZONE printf "\n\n#########################\n# Running parent node 4 #\n#########################\n\n" -gcloud compute ssh $NODE_4_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 4" --project $PROJECT_ID +gcloud compute ssh $NODE_4_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON 0 4" --project $PROJECT_ID --zone $NODE_4_ZONE # 3. Shards if [ "$NUM_SHARDS" -gt 0 ]; then + printf "\nDeploying shard blockchains..." for i in $(seq $NUM_SHARDS) do echo "shard #$i" @@ -112,34 +117,34 @@ if [ "$NUM_SHARDS" -gt 0 ]; then # deploy files to GCP instances printf "\nDeploying files to ${SHARD_TRACKER_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_TRACKER ${SHARD_TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_TRACKER ${SHARD_TRACKER_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $TRACKER_ZONE printf "\nDeploying files to ${SHARD_NODE_0_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_0_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_0_ZONE printf "\nDeploying files to ${SHARD_NODE_1_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_1_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_1_ZONE printf "\nDeploying files to ${SHARD_NODE_2_TARGET_ADDR}..." - gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID + gcloud compute scp --recurse $FILES_FOR_NODE ${SHARD_NODE_2_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $NODE_2_ZONE # ssh into each instance, set up the ubuntu VM instance (ONLY NEEDED FOR THE FIRST TIME) if [ "$4" == "--setup" ]; then printf "\n\n###########################\n# Setting up shard_$i tracker #\n###########################\n\n" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Setting up shard_$i node 0 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n##########################\n# Setting up shard_$i node 1 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Setting up shard_$i node 2 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". setup_blockchain_ubuntu.sh" --project $PROJECT_ID --zone $NODE_2_ZONE fi # ssh into each instance, install packages and start up the server printf "\n\n###########################\n# Running shard_$i tracker #\n###########################\n\n" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command ". setup_tracker_gcp.sh && . start_tracker_gcp.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Running shard_$i node 0 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON $i 0" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON $i 0" --project $PROJECT_ID --zone $NODE_0_ZONE printf "\n\n##########################\n# Running shard_$i node 1 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON $i 1" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON $i 1" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Running shard_$i node 2 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON $i 2" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command ". start_node_incremental_gcp.sh $SEASON $i 2" --project $PROJECT_ID --zone $NODE_2_ZONE done fi diff --git a/deploy_monitoring_gcp.sh b/deploy_monitoring_gcp.sh index fdaa5e6d2..8f68f001f 100644 --- a/deploy_monitoring_gcp.sh +++ b/deploy_monitoring_gcp.sh @@ -36,15 +36,16 @@ fi FILES_FOR_MONITORING="monitoring/ setup_monitoring_gcp.sh setup_monitoring_ubuntu.sh start_monitoring_gcp.sh" MONITORING_TARGET_ADDR="${GCP_USER}@${SEASON}-monitoring-taiwan" +MONITORING_ZONE="asia-east1-b" # kill any processes still alive -gcloud compute ssh $MONITORING_TARGET_ADDR --command "sudo killall prometheus" --project $PROJECT_ID -gcloud compute ssh $MONITORING_TARGET_ADDR --command "sudo killall grafana-server" --project $PROJECT_ID +gcloud compute ssh $MONITORING_TARGET_ADDR --command "sudo killall prometheus" --project $PROJECT_ID --zone $MONITORING_ZONE +gcloud compute ssh $MONITORING_TARGET_ADDR --command "sudo killall grafana-server" --project $PROJECT_ID --zone $MONITORING_ZONE # deploy files to GCP instances printf "\nDeploying monitoring..." printf "\nDeploying files to ${MONITORING_TARGET_ADDR}..." -gcloud compute scp --recurse $FILES_FOR_MONITORING ${MONITORING_TARGET_ADDR}:~/ --project $PROJECT_ID +gcloud compute scp --recurse $FILES_FOR_MONITORING ${MONITORING_TARGET_ADDR}:~/ --project $PROJECT_ID --zone $MONITORING_ZONE # ssh into each instance, set up the ubuntu VM instance (ONLY NEEDED FOR THE FIRST TIME) # printf "\n\n##########################\n# Setting up monitoring #\n###########################\n\n" @@ -52,4 +53,4 @@ gcloud compute scp --recurse $FILES_FOR_MONITORING ${MONITORING_TARGET_ADDR}:~/ # ssh into each instance, install packages and start up the server printf "\n\n############################\n# Running monitoring #\n############################\n\n" -gcloud compute ssh $MONITORING_TARGET_ADDR --command ". setup_monitoring_gcp.sh ${SEASON} && . start_monitoring_gcp.sh" --project $PROJECT_ID +gcloud compute ssh $MONITORING_TARGET_ADDR --command ". setup_monitoring_gcp.sh ${SEASON} && . start_monitoring_gcp.sh" --project $PROJECT_ID --zone $MONITORING_ZONE diff --git a/genesis-configs/base/genesis_owners.json b/genesis-configs/base/genesis_owners.json index 10005a48c..91003d9e9 100644 --- a/genesis-configs/base/genesis_owners.json +++ b/genesis-configs/base/genesis_owners.json @@ -2,11 +2,11 @@ "apps": { ".owner": { "owners": { - "*": { + "fid:_createApp": { "branch_owner": true, - "write_function": true, + "write_function": false, "write_owner": false, - "write_rule": false + "write_rule": true } } } diff --git a/integration/afan_dapp.test.js b/integration/afan_dapp.test.js index cbe6a33b0..ece3ba530 100644 --- a/integration/afan_dapp.test.js +++ b/integration/afan_dapp.test.js @@ -54,64 +54,42 @@ function startServer(application, serverName, envVars, stdioInherit = false) { } async function setUp() { - const addr = parseOrLog(syncRequest( + const server1Addr = parseOrLog(syncRequest( 'GET', server1 + '/get_address').body.toString('utf-8')).result; - const createAppRes = parseOrLog(syncRequest('POST', server1 + '/set', { + const server2Addr = parseOrLog(syncRequest( + 'GET', server2 + '/get_address').body.toString('utf-8')).result; + const server3Addr = parseOrLog(syncRequest( + 'GET', server3 + '/get_address').body.toString('utf-8')).result; + const server4Addr = parseOrLog(syncRequest( + 'GET', server4 + '/get_address').body.toString('utf-8')).result; + + const appStakingRes = parseOrLog(syncRequest('POST', server1 + '/set_value', { json: { - op_list: [ - { - type: 'SET_VALUE', - ref: `/manage_app/afan/create/${Date.now()}`, - value: { - admin: { [addr]: true } - } - }, - { - type: 'SET_VALUE', - ref: `/staking/afan/${addr}/0/stake/${Date.now()}/value`, - value: 1 - } - ] + ref: `/staking/afan/${server1Addr}/0/stake/${Date.now()}/value`, + value: 1 } }).body.toString('utf-8')).result; - assert.deepEqual(ChainUtil.isFailedTx(_.get(createAppRes, 'result')), false); - if (!(await waitUntilTxFinalized(serverList, createAppRes.tx_hash))) { - console.log(`setUp(): Failed to check finalization of create app tx.`) + assert.deepEqual(ChainUtil.isFailedTx(_.get(appStakingRes, 'result')), false); + if (!(await waitUntilTxFinalized(serverList, appStakingRes.tx_hash))) { + console.log(`setUp(): Failed to check finalization of app staking tx.`) } - // TODO(lia): set owner & rule at apps/ in _createApp, and remove this setup tx - const legacySetupRes = parseOrLog(syncRequest('POST', server1 + '/set', { + + const createAppRes = parseOrLog(syncRequest('POST', server1 + '/set_value', { json: { - op_list: [ - { - type: 'SET_OWNER', - ref: '/apps/afan', - value: { - ".owner": { - "owners": { - "*": { - "branch_owner": false, - "write_function": false, - "write_owner": true, - "write_rule": true, - } - } - } - } - }, - { - type: 'SET_RULE', - ref: '/apps/afan', - value: { - ".write": true - } - }, - ], - nonce: -1, + ref: `/manage_app/afan/create/${Date.now()}`, + value: { + admin: { + [server1Addr]: true, + [server2Addr]: true, + [server3Addr]: true, + [server4Addr]: true, + } + } } }).body.toString('utf-8')).result; - assert.deepEqual(ChainUtil.isFailedTx(_.get(legacySetupRes, 'result')), false); - if (!(await waitUntilTxFinalized(serverList, legacySetupRes.tx_hash))) { - console.log(`setUp(): Failed to check finalization of legacy setup tx.`) + assert.deepEqual(ChainUtil.isFailedTx(_.get(createAppRes, 'result')), false); + if (!(await waitUntilTxFinalized(serverList, createAppRes.tx_hash))) { + console.log(`setUp(): Failed to check finalization of create app tx.`) } } @@ -129,11 +107,6 @@ async function cleanUp() { ref: '/apps/afan', value: null }, - { - type: 'SET_OWNER', - ref: '/apps/afan', - value: null - }, ], nonce: -1, } diff --git a/integration/blockchain.test.js b/integration/blockchain.test.js index 9c68d8649..2c3a347d4 100644 --- a/integration/blockchain.test.js +++ b/integration/blockchain.test.js @@ -98,7 +98,12 @@ RANDOM_OPERATION = [ ['set_rule', {ref: 'test/test_rule/', value: { ".write": "some rule config"}}], ['set_function', {ref: 'test/test_function/', value: { ".function": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } }}], ['set_owner', {ref: 'test/test_owner/', value: { diff --git a/integration/node.test.js b/integration/node.test.js index 8c85b4ca3..63d3cc806 100644 --- a/integration/node.test.js +++ b/integration/node.test.js @@ -105,7 +105,12 @@ async function setUp() { ref: '/test/test_function/some/path', value: { ".function": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } } }, @@ -224,7 +229,12 @@ describe('Blockchain Node', () => { code: 0, result: { ".function": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } } }); @@ -281,7 +291,12 @@ describe('Blockchain Node', () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path" }, @@ -415,7 +430,12 @@ describe('Blockchain Node', () => { 100, { ".function": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } }, { @@ -504,7 +524,12 @@ describe('Blockchain Node', () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path" }, @@ -715,8 +740,8 @@ describe('Blockchain Node', () => { const body = parseOrLog(syncRequest( 'POST', server1 + '/set_value', {json: request}).body.toString('utf-8')); assert.deepEqual(_.get(body, 'result.result.code'), 0); - expect(body.code).to.equal(0); expect(_.get(body, 'result.tx_hash')).to.not.equal(null); + expect(body.code).to.equal(0); // Confirm that the value is set properly. if (!(await waitUntilTxFinalized(serverList, _.get(body, 'result.tx_hash')))) { @@ -736,8 +761,8 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest( 'POST', server1 + '/set_value', {json: request}).body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); expect(_.get(body, 'result.tx_hash')).to.not.equal(null); // Confirm that the value is set properly. @@ -758,8 +783,8 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest( 'POST', server1 + '/set_value', {json: request}).body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); expect(_.get(body, 'result.tx_hash')).to.not.equal(null); // Confirm that the value is set properly. @@ -782,8 +807,8 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest( 'POST', server1 + '/set_value', {json: request}).body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); expect(_.get(body, 'result.tx_hash')).to.not.equal(null); // Confirm that the value is set properly. @@ -806,7 +831,6 @@ describe('Blockchain Node', () => { const request = {ref: 'some/wrong/path', value: "some other value"}; const body = parseOrLog(syncRequest('POST', server1 + '/set_value', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "code": 103, "error_message": "No .write permission on: some/wrong/path", @@ -817,6 +841,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original value is not altered. const resultAfter = parseOrLog(syncRequest( @@ -837,8 +862,8 @@ describe('Blockchain Node', () => { const request = {ref: "test/test_value/some/path2", value: 10}; const body = parseOrLog(syncRequest('POST', server1 + '/inc_value', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); // Confirm that the value is set properly. expect(_.get(body, 'result.tx_hash')).to.not.equal(null); @@ -861,7 +886,6 @@ describe('Blockchain Node', () => { const request = {ref: "some/wrong/path2", value: 10}; const body = parseOrLog(syncRequest('POST', server1 + '/inc_value', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "code": 103, "error_message": "No .write permission on: some/wrong/path2", @@ -872,6 +896,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original value is not altered. const resultAfter = parseOrLog(syncRequest( @@ -892,8 +917,8 @@ describe('Blockchain Node', () => { const request = {ref: "test/test_value/some/path3", value: 10}; const body = parseOrLog(syncRequest('POST', server1 + '/dec_value', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); // Confirm that the value is set properly. expect(_.get(body, 'result.tx_hash')).to.not.equal(null); @@ -916,7 +941,6 @@ describe('Blockchain Node', () => { const request = {ref: "some/wrong/path3", value: 10}; const body = parseOrLog(syncRequest('POST', server1 + '/dec_value', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "code": 103, "error_message": "No .write permission on: some/wrong/path3", @@ -927,6 +951,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original value is not altered. const resultAfter = parseOrLog(syncRequest( @@ -944,7 +969,12 @@ describe('Blockchain Node', () => { .body.toString('utf-8')).result; assert.deepEqual(resultBefore, { ".function": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }); @@ -952,7 +982,12 @@ describe('Blockchain Node', () => { ref: "/test/test_function/some/path", value: { ".function": { - "fid": "some other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", // Listener 2 + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } }; @@ -972,7 +1007,12 @@ describe('Blockchain Node', () => { .body.toString('utf-8')).result; assert.deepEqual(resultAfter, { ".function": { - "fid": "some other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", // Listener 2 + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }); }) @@ -988,14 +1028,18 @@ describe('Blockchain Node', () => { ref: "/some/wrong/path", value: { ".function": { - "fid": "some other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } }; const body = parseOrLog(syncRequest( 'POST', server1 + '/set_function', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "code": 404, "error_message": "No write_function permission on: /some/wrong/path", @@ -1006,6 +1050,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original function is not altered. const resultAfter = parseOrLog(syncRequest( @@ -1033,8 +1078,8 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest('POST', server1 + '/set_rule', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); // Confirm that the value is set properly. expect(_.get(body, 'result.tx_hash')).to.not.equal(null); @@ -1064,7 +1109,6 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest('POST', server1 + '/set_rule', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "code": 503, "error_message": "No write_rule permission on: /some/wrong/path", @@ -1075,6 +1119,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original rule is not altered. const resultAfter = parseOrLog(syncRequest( @@ -1120,8 +1165,8 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest('POST', server1 + '/set_owner', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + expect(body.code).to.equal(0); // Confirm that the value is set properly. expect(_.get(body, 'result.tx_hash')).to.not.equal(null); @@ -1155,12 +1200,20 @@ describe('Blockchain Node', () => { const request = { ref: "/some/wrong/path", value: { - ".owner": "some other owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } }; const body = parseOrLog(syncRequest('POST', server1 + '/set_owner', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "code": 603, "error_message": "No write_owner or branch_owner permission on: /some/wrong/path", @@ -1171,6 +1224,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original owner is not altered. const resultAfter = parseOrLog(syncRequest( @@ -1209,7 +1263,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other100/path", value: { - ".function": "some other100 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, { @@ -1223,14 +1284,22 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other100/path", value: { - ".owner": "some other100 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } } ] }; const body = parseOrLog(syncRequest('POST', server1 + '/set', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(0); assert.deepEqual(_.get(body, 'result.result'), { "result_list": [ { @@ -1264,6 +1333,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(0); // Confirm that the original value is set properly. expect(_.get(body, 'result.tx_hash')).to.not.equal(null); @@ -1309,7 +1379,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other101/path", value: { - ".function": "some other101 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, { @@ -1330,7 +1407,6 @@ describe('Blockchain Node', () => { }; const body = parseOrLog(syncRequest('POST', server1 + '/set', {json: request}) .body.toString('utf-8')); - expect(body.code).to.equal(1); assert.deepEqual(_.get(body, 'result.result'), { "result_list": [ { @@ -1357,6 +1433,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0 }); + expect(body.code).to.equal(1); // Confirm that the original value is not altered. const resultAfter = parseOrLog(syncRequest( @@ -1416,7 +1493,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other200/path", value: { - ".function": "some other200 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, timestamp: Date.now(), @@ -1438,7 +1522,16 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other200/path", value: { - ".owner": "some other200 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } }, timestamp: Date.now(), @@ -1467,7 +1560,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other201/path", value: { - ".function": "some other201 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, { @@ -1481,7 +1581,16 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other201/path", value: { - ".owner": "some other201 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } } ] @@ -1494,7 +1603,6 @@ describe('Blockchain Node', () => { const body = parseOrLog(syncRequest('POST', server1 + '/batch', {json: request}) .body.toString('utf-8')); expect(body).to.not.equal(null); - expect(body.code).to.equal(0); expect(ChainUtil.isArray(body.result)).to.equal(true); for (let i = 0; i < body.result.length; i++) { const result = body.result[i]; @@ -1610,9 +1718,10 @@ describe('Blockchain Node', () => { "tx_hash": "erased" } ]); + expect(body.code).to.equal(0); // Confirm that the value is set properly. - await ChainUtil.sleep(3); + await ChainUtil.sleep(6); const resultAfter = parseOrLog(syncRequest( 'GET', server1 + '/get_value?ref=test/test_value/some200/path') .body.toString('utf-8')).result; @@ -1681,7 +1790,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other202/path", value: { - ".function": "some other202 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, timestamp: Date.now(), @@ -1703,7 +1819,16 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other202/path", value: { - ".owner": "some other202 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } }, timestamp: Date.now(), @@ -1732,7 +1857,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other203/path", value: { - ".function": "some other203 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, { @@ -1746,7 +1878,16 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other203/path", value: { - ".owner": "some other203 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } } ] @@ -1759,7 +1900,6 @@ describe('Blockchain Node', () => { const body = parseOrLog(syncRequest('POST', server1 + '/batch', {json: request}) .body.toString('utf-8')); expect(body).to.not.equal(null); - expect(body.code).to.equal(0); expect(ChainUtil.isArray(body.result)).to.equal(true); for (let i = 0; i < body.result.length; i++) { const result = body.result[i]; @@ -1888,9 +2028,10 @@ describe('Blockchain Node', () => { "tx_hash": "erased" } ]); + expect(body.code).to.equal(0); // Confirm that the value is set properly. - await ChainUtil.sleep(3); + await ChainUtil.sleep(6); const resultAfter = parseOrLog(syncRequest( 'GET', server1 + '/get_value?ref=test/test_value/some202/path') .body.toString('utf-8')).result; @@ -2153,7 +2294,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other300/path", value: { - ".function": "some other300 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, timestamp: Date.now(), @@ -2175,7 +2323,16 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other300/path", value: { - ".owner": "some other300 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } }, timestamp: Date.now(), @@ -2204,7 +2361,14 @@ describe('Blockchain Node', () => { type: 'SET_FUNCTION', ref: "/test/test_function/other301/path", value: { - ".function": "some other301 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, { @@ -2218,7 +2382,16 @@ describe('Blockchain Node', () => { type: 'SET_OWNER', ref: "/test/test_owner/other301/path", value: { - ".owner": "some other301 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } } ] @@ -2544,11 +2717,13 @@ describe('Blockchain Node', () => { }) describe('Function triggering', () => { - const saveLastTxAllowedPath = '/test/test_function_triggering/allowed_path'; - const saveLastTxNotAllowedPath = '/test/test_function_triggering/not_allowed_path'; + const setFunctionWithOwnerOnlyPath = '/test/test_function_triggering/owner_only'; + const saveLastTxAllowedPath = '/test/test_function_triggering/allowed_path_with_fid'; + const saveLastTxNotAllowedPath = '/test/test_function_triggering/not_allowed_path_with_fid'; const saveLastTxAllowedPathWithFids = '/test/test_function_triggering/allowed_path_with_fids'; const saveLastTxNotAllowedPathWithFids = '/test/test_function_triggering/not_allowed_path_with_fids'; - const setFunctionWithOwnerOnlyPath = '/test/test_function_triggering/owner_only'; + const setOwnerConfigAllowedPath = '/test/test_function_triggering/set_owner_allowed_path_with_fid'; + const setOwnerConfigNotAllowedPath = '/test/test_function_triggering/set_owner_not_allowed_path_with_fid'; const triggerRestFunctionPath = '/test/test_function_triggering/rest_function_path'; let transferFrom; // = server1 @@ -2608,7 +2783,7 @@ describe('Blockchain Node', () => { op_list: [ { type: 'SET_FUNCTION', - ref: '/test/test_function_triggering/allowed_path/value', + ref: '/test/test_function_triggering/allowed_path_with_fid/value', value: { ".function": { "_saveLastTx": { @@ -2620,21 +2795,21 @@ describe('Blockchain Node', () => { }, { type: 'SET_RULE', - ref: '/test/test_function_triggering/allowed_path/value', + ref: '/test/test_function_triggering/allowed_path_with_fid/value', value: { ".write": true, } }, { type: 'SET_RULE', - ref: '/test/test_function_triggering/allowed_path/.last_tx/value', + ref: '/test/test_function_triggering/allowed_path_with_fid/.last_tx/value', value: { ".write": "auth.fid === '_saveLastTx'", } }, { type: 'SET_FUNCTION', - ref: '/test/test_function_triggering/not_allowed_path/value', + ref: '/test/test_function_triggering/not_allowed_path_with_fid/value', value: { ".function": { "_saveLastTx": { @@ -2646,14 +2821,14 @@ describe('Blockchain Node', () => { }, { type: 'SET_RULE', - ref: '/test/test_function_triggering/not_allowed_path/value', + ref: '/test/test_function_triggering/not_allowed_path_with_fid/value', value: { ".write": true, } }, { type: 'SET_RULE', - ref: '/test/test_function_triggering/not_allowed_path/.last_tx/value', + ref: '/test/test_function_triggering/not_allowed_path_with_fid/.last_tx/value', value: { ".write": "auth.fid === 'some function id'", } @@ -2710,6 +2885,74 @@ describe('Blockchain Node', () => { ".write": "util.includes(auth.fids, 'some function id')", } }, + { + type: 'SET_FUNCTION', + ref: '/test/test_function_triggering/set_owner_allowed_path_with_fid/value', + value: { + ".function": { + "_setOwnerConfig": { + "function_type": "NATIVE", + "function_id": "_setOwnerConfig" + } + } + } + }, + { + type: 'SET_OWNER', + ref: '/test/test_function_triggering/set_owner_allowed_path_with_fid', + value: { + ".owner": { + "owners": { + "fid:_setOwnerConfig": { + "branch_owner": true, // allow branching + "write_function": false, + "write_owner": false, + "write_rule": false, + }, + "*": { + "branch_owner": false, // not allow branching + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + } + } + }, + { + type: 'SET_FUNCTION', + ref: '/test/test_function_triggering/set_owner_not_allowed_path_with_fid/value', + value: { + ".function": { + "_setOwnerConfig": { + "function_type": "NATIVE", + "function_id": "_setOwnerConfig" + } + } + } + }, + { + type: 'SET_OWNER', + ref: '/test/test_function_triggering/set_owner_not_allowed_path_with_fid', + value: { + ".owner": { + "owners": { + "fid:_setOwnerConfig": { + "branch_owner": false, // not allow branching + "write_function": false, + "write_owner": false, + "write_rule": false, + }, + "*": { + "branch_owner": false, // not allow branching + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + } + } + }, { type: 'SET_FUNCTION', ref: '/test/test_function_triggering/rest_function_path', @@ -2747,22 +2990,22 @@ describe('Blockchain Node', () => { op_list: [ { type: 'SET_FUNCTION', - ref: '/test/test_function_triggering/allowed_path/value', + ref: '/test/test_function_triggering/allowed_path_with_fid/value', value: null }, { type: 'SET_RULE', - ref: '/test/test_function_triggering/allowed_path/value', + ref: '/test/test_function_triggering/allowed_path_with_fid/value', value: null }, { type: 'SET_FUNCTION', - ref: '/test/test_function_triggering/not_allowed_path/value', + ref: '/test/test_function_triggering/not_allowed_path_with_fid/value', value: null }, { type: 'SET_RULE', - ref: '/test/test_function_triggering/not_allowed_path/value', + ref: '/test/test_function_triggering/not_allowed_path_with_fid/value', value: null }, { @@ -2785,6 +3028,36 @@ describe('Blockchain Node', () => { ref: '/test/test_function_triggering/not_allowed_path_with_fids/value', value: null }, + { + type: 'SET_FUNCTION', + ref: '/test/test_function_triggering/set_owner_allowed_path_with_fid/value', + value: null + }, + { + type: 'SET_OWNER', + ref: '/test/test_function_triggering/set_owner_allowed_path_with_fid', + value: null + }, + { + type: 'SET_FUNCTION', + ref: '/test/test_function_triggering/set_owner_not_allowed_path_with_fid/value', + value: null + }, + { + type: 'SET_OWNER', + ref: '/test/test_function_triggering/set_owner_not_allowed_path_with_fid', + value: null + }, + { + type: 'SET_FUNCTION', + ref: '/test/test_function_triggering/rest_function_path', + value: null, + }, + { + type: 'SET_RULE', + ref: '/test/test_function_triggering/rest_function_path', + value: null, + }, ], nonce: -1, } @@ -2826,8 +3099,8 @@ describe('Blockchain Node', () => { timestamp: Date.now(), nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + assert.deepEqual(body.code, 0); if (!(await waitUntilTxFinalized([server2], _.get(body, 'result.tx_hash')))) { console.error(`Failed to check finalization of tx.`); } @@ -2878,7 +3151,6 @@ describe('Blockchain Node', () => { timestamp: Date.now(), nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 1); assert.deepEqual(_.get(body, 'result.result'), { "code": 0, "func_results": { @@ -2887,10 +3159,10 @@ describe('Blockchain Node', () => { "gas_amount": 0, "op_results": [ { - "path": "/test/test_function_triggering/not_allowed_path/.last_tx/value", + "path": "/test/test_function_triggering/not_allowed_path_with_fid/.last_tx/value", "result": { "code": 103, - "error_message": "No .write permission on: /test/test_function_triggering/not_allowed_path/.last_tx/value", + "error_message": "No .write permission on: /test/test_function_triggering/not_allowed_path_with_fid/.last_tx/value", "gas_amount": 0, } } @@ -2904,6 +3176,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 1); const lastTx = parseOrLog(syncRequest('GET', server2 + `/get_value?ref=${saveLastTxNotAllowedPath + '/.last_tx/value'}`) .body.toString('utf-8')).result @@ -2918,7 +3191,6 @@ describe('Blockchain Node', () => { timestamp: Date.now(), nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "code": 0, "func_results": { @@ -2927,7 +3199,7 @@ describe('Blockchain Node', () => { "gas_amount": 0, "op_results": [ { - "path": "/test/test_function_triggering/allowed_path/.last_tx/value", + "path": "/test/test_function_triggering/allowed_path_with_fid/.last_tx/value", "result": { "code": 0, "gas_amount": 1, @@ -2943,6 +3215,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); if (!(await waitUntilTxFinalized([server2], _.get(body, 'result.tx_hash')))) { console.error(`Failed to check finalization of tx.`); } @@ -2962,7 +3235,6 @@ describe('Blockchain Node', () => { timestamp: Date.now(), nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 1); assert.deepEqual(_.get(body, 'result.result'), { "code": 0, "func_results": { @@ -2988,6 +3260,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 1); const lastTx = parseOrLog(syncRequest('GET', server2 + `/get_value?ref=${saveLastTxNotAllowedPathWithFids + '/.last_tx/value'}`) .body.toString('utf-8')).result @@ -3002,7 +3275,6 @@ describe('Blockchain Node', () => { timestamp: Date.now(), nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "code": 0, "func_results": { @@ -3027,6 +3299,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); if (!(await waitUntilTxFinalized([server2], _.get(body, 'result.tx_hash')))) { console.error(`Failed to check finalization of tx.`); } @@ -3037,6 +3310,90 @@ describe('Blockchain Node', () => { assert.deepEqual(_.get(lastTx, 'tx_hash', null), body.result.tx_hash); }); }); + + describe('Owner rule: auth.fid', () => { + it('owner rule: auth.fid: without function permission', () => { + const body = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { + ref: setOwnerConfigNotAllowedPath + '/value', + value: 'some value', + timestamp: Date.now(), + nonce: -1, + }}).body.toString('utf-8')); + assert.deepEqual(_.get(body, 'result.result'), { + "code": 0, + "func_results": { + "_setOwnerConfig": { + "code": "FAILURE", + "gas_amount": 0, + "op_results": [ + { + "path": "/test/test_function_triggering/set_owner_not_allowed_path_with_fid/value", + "result": { + "code": 603, + "error_message": "No write_owner or branch_owner permission on: /test/test_function_triggering/set_owner_not_allowed_path_with_fid/value", + "gas_amount": 0, + } + } + ] + } + }, + "gas_amount": 1, + "gas_amount_total": { + "app": {}, + "service": 1 + }, + "gas_cost_total": 0, + }); + assert.deepEqual(body.code, 1); + const ownerConfig = parseOrLog(syncRequest('GET', + server2 + `/get_owner?ref=${setOwnerConfigNotAllowedPath + '/value'}`) + .body.toString('utf-8')).result + // Should be null. + expect(ownerConfig).to.equal(null); + }); + + it('owner rule: auth.fid: with function permission', async () => { + const body = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { + ref: setOwnerConfigAllowedPath + '/value', + value: 'some value', + timestamp: Date.now(), + nonce: -1, + }}).body.toString('utf-8')); + assert.deepEqual(_.get(body, 'result.result'), { + "code": 0, + "func_results": { + "_setOwnerConfig": { + "code": "SUCCESS", + "gas_amount": 0, + "op_results": [ + { + "path": "/test/test_function_triggering/set_owner_allowed_path_with_fid/value", + "result": { + "code": 0, + "gas_amount": 1, + } + } + ] + } + }, + "gas_amount": 1, + "gas_amount_total": { + "app": {}, + "service": 2 + }, + "gas_cost_total": 0, + }); + assert.deepEqual(body.code, 0); + if (!(await waitUntilTxFinalized([server2], _.get(body, 'result.tx_hash')))) { + console.error(`Failed to check finalization of tx.`); + } + const ownerConfig = parseOrLog(syncRequest('GET', + server2 + `/get_owner?ref=${setOwnerConfigAllowedPath + '/value'}`) + .body.toString('utf-8')).result + // Should be not null. + expect(ownerConfig).to.not.equal(null); + }); + }); }); describe('Function execution', () => { @@ -3358,15 +3715,22 @@ describe('Blockchain Node', () => { describe('Gas fee', () => { before(async () => { + const appStakingPath = `/staking/test_service_gas_fee/${serviceAdmin}/0/stake/${Date.now()}/value` + const appStakingRes = parseOrLog(syncRequest('POST', server1 + '/set_value', {json: { + ref: appStakingPath, + value: 1 + }}).body.toString('utf-8')).result; + if (!(await waitUntilTxFinalized(serverList, appStakingRes.tx_hash))) { + console.error(`Failed to check finalization of tx.`); + } const manageAppPath = '/manage_app/test_service_gas_fee/create/1' - const body = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { + const createAppRes = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { ref: manageAppPath, value: { admin: { [serviceAdmin]: true }, }, - }}).body.toString('utf-8')); - expect(body.code).to.equals(0); - if (!(await waitUntilTxFinalized(serverList, _.get(body, 'result.tx_hash')))) { + }}).body.toString('utf-8')).result; + if (!(await waitUntilTxFinalized(serverList, createAppRes.tx_hash))) { console.error(`Failed to check finalization of tx.`); } }); @@ -3378,7 +3742,6 @@ describe('Blockchain Node', () => { timestamp: 1234567890000, nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "func_results": { "_transfer": { @@ -3417,6 +3780,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); }); it("native function (_transfer) without individual account registration", () => { @@ -3426,7 +3790,6 @@ describe('Blockchain Node', () => { timestamp: 1234567890000, nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "func_results": { "_transfer": { @@ -3465,6 +3828,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); }); it("native function (_transfer) with service account registration", () => { @@ -3474,7 +3838,6 @@ describe('Blockchain Node', () => { timestamp: 1234567890000, nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "func_results": { "_stake": { @@ -3549,6 +3912,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); }); it("native function (_transfer) without service account registration", () => { @@ -3558,7 +3922,6 @@ describe('Blockchain Node', () => { timestamp: 1234567890001, nonce: -1, }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "func_results": { "_stake": { @@ -3633,6 +3996,7 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); }); it("REST function with external RPC call", () => { @@ -3640,7 +4004,6 @@ describe('Blockchain Node', () => { ref: triggerRestFunctionPath, value: 'some value', }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result'), { "func_results": { "0x11111": { @@ -3656,10 +4019,11 @@ describe('Blockchain Node', () => { }, "gas_cost_total": 0, }); + assert.deepEqual(body.code, 0); }); }); - describe('Transfer (_transfer)', () => { + describe('Transfer: _transfer', () => { it('transfer: transfer', async () => { let fromBeforeBalance = parseOrLog(syncRequest('GET', server2 + `/get_value?ref=${transferFromBalancePath}`).body.toString('utf-8')).result; @@ -3669,8 +4033,8 @@ describe('Blockchain Node', () => { ref: transferPath + '/1/value', value: transferAmount }}).body.toString('utf-8')); - assert.deepEqual(body.code, 0); assert.deepEqual(_.get(body, 'result.result.code'), 0); + assert.deepEqual(body.code, 0); if (!(await waitUntilTxFinalized([server2], _.get(body, 'result.tx_hash')))) { console.error(`Failed to check finalization of tx.`); } @@ -3776,6 +4140,14 @@ describe('Blockchain Node', () => { describe('Staking: _stake, _unstake', () => { before(async () => { + const appStakingPath = `/staking/test_service_staking/${serviceAdmin}/0/stake/${Date.now()}/value` + const appStakingRes = parseOrLog(syncRequest('POST', server1 + '/set_value', {json: { + ref: appStakingPath, + value: 1 + }}).body.toString('utf-8')).result; + if (!(await waitUntilTxFinalized(serverList, appStakingRes.tx_hash))) { + console.error(`Failed to check finalization of tx.`); + } const manageAppPath = '/manage_app/test_service_staking/create/1' const body = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { ref: manageAppPath, @@ -3898,7 +4270,7 @@ describe('Blockchain Node', () => { expect(stakeValue).to.equal(stakeAmount); expect(afterStakingAccountBalance).to.equal(beforeStakingAccountBalance + stakeAmount); expect(afterBalance).to.equal(beforeBalance - stakeAmount); - expect(stakingAppBalanceTotal).to.equal(stakeAmount); + expect(stakingAppBalanceTotal).to.equal(stakeAmount + 1); }); it('stake: stake more than account balance', () => { @@ -4159,7 +4531,7 @@ describe('Blockchain Node', () => { expect(resultCode).to.equal(FunctionResultCode.SUCCESS); expect(afterStakingAccountBalance).to.equal(beforeStakingAccountBalance - stakeAmount); expect(afterBalance).to.equal(beforeBalance + stakeAmount); - expect(stakingAppBalanceTotal).to.equal(0); + expect(stakingAppBalanceTotal).to.equal(1); }); it('unstake: stake after unstake', async () => { @@ -4196,6 +4568,14 @@ describe('Blockchain Node', () => { describe('Payments: _pay, _claim', () => { before(async () => { + const appStakingPath = `/staking/test_service_payment/${serviceAdmin}/0/stake/${Date.now()}/value` + const appStakingRes = parseOrLog(syncRequest('POST', server1 + '/set_value', {json: { + ref: appStakingPath, + value: 1 + }}).body.toString('utf-8')).result; + if (!(await waitUntilTxFinalized(serverList, appStakingRes.tx_hash))) { + console.error(`Failed to check finalization of tx.`); + } const manageAppPath = '/manage_app/test_service_payment/create/1' const body = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { ref: manageAppPath, @@ -4602,6 +4982,14 @@ describe('Blockchain Node', () => { describe('Escrow: _hold, _release', () => { before(async () => { + const appStakingPath = `/staking/test_service_escrow/${serviceAdmin}/0/stake/${Date.now()}/value` + const appStakingRes = parseOrLog(syncRequest('POST', server1 + '/set_value', {json: { + ref: appStakingPath, + value: 1 + }}).body.toString('utf-8')).result; + if (!(await waitUntilTxFinalized(serverList, appStakingRes.tx_hash))) { + console.error(`Failed to check finalization of tx.`); + } const manageAppPath = '/manage_app/test_service_escrow/create/1' const body = parseOrLog(syncRequest('POST', server2 + '/set_value', {json: { ref: manageAppPath, @@ -4891,6 +5279,15 @@ describe('Blockchain Node', () => { describe('Escrow: service -> individual', () => { it('escrow: service -> individual: open escrow', async () => { const key = 1234567890000 + 101; + const server4Addr = parseOrLog(syncRequest( + 'GET', server4 + '/get_address').body.toString('utf-8')).result; + const transferBody = parseOrLog(syncRequest('POST', server4 + '/set_value', {json: { + ref: `transfer/${server4Addr}/${serviceAdmin}/${key}/value`, + value: 100 + }}).body.toString('utf-8')); + if (!(await waitUntilTxFinalized(serverList, _.get(transferBody, 'result.tx_hash')))) { + console.error(`Failed to check finalization of tx.`); + } const payRef = `/payments/test_service_escrow/${serviceUser}/0/pay/${key}`; const adminBalanceBefore = parseOrLog(syncRequest('GET', server1 + `/get_value?ref=/accounts/${serviceAdmin}/balance`).body.toString('utf-8')).result; diff --git a/integration/sharding.test.js b/integration/sharding.test.js index c45571146..c62365d71 100644 --- a/integration/sharding.test.js +++ b/integration/sharding.test.js @@ -138,7 +138,12 @@ async function setUp() { ref: '/test/test_function/some/path', value: { ".function": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } }, @@ -244,22 +249,22 @@ describe('Sharding', async () => { ).result; await waitUntilTxFinalized(parentServerList, shardReportRes.tx_hash); // Create app at the parent chain for the shard - const createAppRes = parseOrLog(syncRequest('POST', parentServer + '/set', { + const appStakingRes = parseOrLog(syncRequest('POST', parentServer + '/set_value', { json: { - op_list: [ - { - type: 'SET_VALUE', - ref: `/manage_app/afan/create/${Date.now()}`, - value: { - admin: { [shardOwnerAddr]: true } - } - }, - { - type: 'SET_VALUE', - ref: `/staking/afan/${parentServerAddr}/0/stake/${Date.now()}/value`, - value: 1 - } - ] + ref: `/staking/afan/${parentServerAddr}/0/stake/${Date.now()}/value`, + value: 1 + } + }).body.toString('utf-8')).result; + assert.deepEqual(ChainUtil.isFailedTx(_.get(appStakingRes, 'result')), false); + if (!(await waitUntilTxFinalized(parentServerList, appStakingRes.tx_hash))) { + console.log(`Failed to check finalization of app staking tx.`); + } + const createAppRes = parseOrLog(syncRequest('POST', parentServer + '/set_value', { + json: { + ref: `/manage_app/afan/create/${Date.now()}`, + value: { + admin: { [shardOwnerAddr]: true } + } } }).body.toString('utf-8')).result; assert.deepEqual(ChainUtil.isFailedTx(_.get(createAppRes, 'result')), false); @@ -540,7 +545,12 @@ describe('Sharding', async () => { assert.deepEqual(body.code, 0); assert.deepEqual(body.result, { '.function': { - 'fid': 'some function config' + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }); }) @@ -552,7 +562,12 @@ describe('Sharding', async () => { assert.deepEqual(body.code, 0); assert.deepEqual(body.result, { '.function': { - 'fid': 'some function config' + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }); }) @@ -629,7 +644,12 @@ describe('Sharding', async () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path" }, @@ -650,7 +670,12 @@ describe('Sharding', async () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/apps/afan/test/test_function/some/path" }, @@ -844,7 +869,12 @@ describe('Sharding', async () => { 100, { ".function": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }, { @@ -915,7 +945,12 @@ describe('Sharding', async () => { 100, { ".function": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }, { @@ -997,7 +1032,12 @@ describe('Sharding', async () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path" }, @@ -1019,7 +1059,12 @@ describe('Sharding', async () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/apps/afan/test/test_function/some/path" }, @@ -1265,7 +1310,12 @@ describe('Sharding', async () => { ref: "test/test_function/other/path", value: { ".function": { - "fid": "some other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", // Listener 2 + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }, nonce: -1 @@ -1281,7 +1331,12 @@ describe('Sharding', async () => { ref: "apps/afan/test/test_function/other/path", value: { ".function": { - "fid": "some other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger3", // Listener 3 + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }, is_global: true, @@ -1330,7 +1385,16 @@ describe('Sharding', async () => { const request = { ref: "test/test_owner/other/path", value: { - ".owner": "some other owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } }, nonce: -1 }; @@ -1344,7 +1408,16 @@ describe('Sharding', async () => { const request = { ref: "apps/afan/test/test_owner/other2/path", value: { - ".owner": "some other2 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } }, is_global: true, nonce: -1, @@ -1379,7 +1452,14 @@ describe('Sharding', async () => { type: 'SET_FUNCTION', ref: "/test/test_function/other3/path", value: { - ".function": "some other3 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } } }, { @@ -1393,7 +1473,16 @@ describe('Sharding', async () => { type: 'SET_OWNER', ref: "/test/test_owner/other3/path", value: { - ".owner": "some other3 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } } } ], @@ -1462,7 +1551,14 @@ describe('Sharding', async () => { type: 'SET_FUNCTION', ref: "/test/test_function/other4/path", value: { - ".function": "some other4 function config" + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } }, is_global: true, }, @@ -1478,7 +1574,16 @@ describe('Sharding', async () => { type: 'SET_OWNER', ref: "/test/test_owner/other4/path", value: { - ".owner": "some other4 owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_owner": true, + "write_rule": true, + "write_function": true + } + } + } }, is_global: true, } @@ -1818,22 +1923,22 @@ describe('Sharding', async () => { describe('_updateLatestShardReport', () => { before(async () => { const { shard_owner, shard_reporter, sharding_path } = shardingConfig; - const createAppRes = parseOrLog(syncRequest('POST', parentServer + '/set', { + const appStakingRes = parseOrLog(syncRequest('POST', parentServer + '/set_value', { json: { - op_list: [ - { - type: 'SET_VALUE', - ref: `/manage_app/a_dapp/create/${Date.now()}`, - value: { - admin: { [shard_owner]: true } - } - }, - { - type: 'SET_VALUE', - ref: `/staking/a_dapp/${shard_owner}/0/stake/${Date.now()}/value`, - value: 1 - } - ] + ref: `/staking/a_dapp/${shard_owner}/0/stake/${Date.now()}/value`, + value: 1 + } + }).body.toString('utf-8')).result; + assert.deepEqual(ChainUtil.isFailedTx(_.get(appStakingRes, 'result')), false); + if (!(await waitUntilTxFinalized(parentServerList, appStakingRes.tx_hash))) { + console.log(`Failed to check finalization of app staking tx.`) + } + const createAppRes = parseOrLog(syncRequest('POST', parentServer + '/set_value', { + json: { + ref: `/manage_app/a_dapp/create/${Date.now()}`, + value: { + admin: { [shard_owner]: true } + } } }).body.toString('utf-8')).result; assert.deepEqual(ChainUtil.isFailedTx(_.get(createAppRes, 'result')), false); @@ -1843,25 +1948,6 @@ describe('Sharding', async () => { const res = parseOrLog(syncRequest('POST', parentServer + '/set', { json: { op_list: [ - { - type: WriteDbOperations.SET_OWNER, - ref: sharding_path, - value: { - [OwnerProperties.OWNER]: { - [OwnerProperties.OWNERS]: { - [shard_owner]: buildOwnerPermissions(true ,true, true, true), - [OwnerProperties.ANYONE]: buildOwnerPermissions(false, false, false, false) - } - } - } - }, - { - type: WriteDbOperations.SET_RULE, - ref: sharding_path, - value: { - [RuleProperties.WRITE]: `auth.addr === '${shard_reporter}'` - } - }, { type: WriteDbOperations.SET_RULE, ref: `${sharding_path}/${ShardingProperties.LATEST}`, @@ -1991,7 +2077,6 @@ describe('Sharding', async () => { "_updateLatestShardReport": { "code": "SUCCESS", "gas_amount": 0, - "op_results": [], } }, "gas_amount": 1 diff --git a/p2p/index.js b/p2p/index.js index b2beaa32f..409abd2d7 100644 --- a/p2p/index.js +++ b/p2p/index.js @@ -26,7 +26,6 @@ const { signMessage, getAddressFromMessage, verifySignedMessage, - isValidDataProtoVer, checkTimestamp, closeSocketSafe, encapsulateMessage @@ -49,7 +48,7 @@ class P2pClient { run() { this.server.listen(); - this.setIntervalForTrackerConnection(); + this.connectToTracker(); } // NOTE(minsulee2): The total number of connection is up to more than 5 without limit. @@ -77,16 +76,24 @@ class P2pClient { }; } - setIntervalForTrackerConnection() { - this.connectToTracker(); - this.intervalConnection = setInterval(() => { - this.connectToTracker(); - }, RECONNECT_INTERVAL_MS); - } - - clearIntervalForTrackerConnection() { - clearInterval(this.intervalConnection); - this.intervalConnection = null; + getStatus() { + const blockStatus = this.server.getBlockStatus(); + return { + address: this.server.getNodeAddress(), + updatedAt: Date.now(), + lastBlockNumber: blockStatus.number, + networkStatus: this.getNetworkStatus(), + blockStatus: blockStatus, + txStatus: this.server.getTxStatus(), + consensusStatus: this.server.getConsensusStatus(), + nodeStatus: this.server.getNodeStatus(), + shardingStatus: this.server.getShardingStatus(), + cpuStatus: this.server.getCpuUsage(), + memoryStatus: this.server.getMemoryUsage(), + diskStatus: this.server.getDiskUsage(), + runtimeInfo: this.server.getRuntimeInfo(), + protocolInfo: this.server.getProtocolInfo(), + }; } getNetworkStatus() { @@ -118,27 +125,20 @@ class P2pClient { port: PORT, }, connectionStatus: this.getConnectionStatus() + }; + } + + setIntervalForTrackerConnection() { + if (!this.intervalConnection) { + this.intervalConnection = setInterval(() => { + this.connectToTracker(); + }, RECONNECT_INTERVAL_MS); } } - getStatus() { - const blockStatus = this.server.getBlockStatus(); - return { - address: this.server.getNodeAddress(), - updatedAt: Date.now(), - lastBlockNumber: blockStatus.number, - networkStatus: this.getNetworkStatus(), - blockStatus: blockStatus, - txStatus: this.server.getTxStatus(), - consensusStatus: this.server.getConsensusStatus(), - nodeStatus: this.server.getNodeStatus(), - shardingStatus: this.server.getShardingStatus(), - cpuStatus: this.server.getCpuUsage(), - memoryStatus: this.server.getMemoryUsage(), - diskStatus: this.server.getDiskUsage(), - runtimeInfo: this.server.getRuntimeInfo(), - protocolInfo: this.server.getProtocolInfo(), - }; + clearIntervalForTrackerConnection() { + clearInterval(this.intervalConnection); + this.intervalConnection = null; } updateNodeStatusToTracker() { @@ -158,32 +158,27 @@ class P2pClient { async setTrackerEventHandlers() { const node = this.server.node; this.trackerWebSocket.on('message', async (message) => { - try { - const parsedMsg = JSON.parse(message); - logger.info(`\n << Message from [TRACKER]: ${JSON.stringify(parsedMsg, null, 2)}`); - if (this.connectToPeers(parsedMsg.newManagedPeerInfoList)) { - logger.debug(`Updated MANAGED peers info: ` + - `${JSON.stringify(this.server.managedPeersInfo, null, 2)}`); - } - if (node.state === BlockchainNodeStates.STARTING) { - node.state = BlockchainNodeStates.SYNCING; - if (parsedMsg.numLivePeers === 0) { - const lastBlockWithoutProposal = node.init(true); - await this.server.tryInitializeShard(); - node.state = BlockchainNodeStates.SERVING; - this.server.consensus.init(lastBlockWithoutProposal); - } else { - // Consensus will be initialized after syncing with peers - node.init(false); - } + const parsedMsg = JSON.parse(message); + logger.info(`\n<< Message from [TRACKER]: ${JSON.stringify(parsedMsg, null, 2)}`); + if (this.connectToPeers(parsedMsg.newManagedPeerInfoList)) { + logger.debug(`Updated MANAGED peers info: ` + + `${JSON.stringify(this.server.managedPeersInfo, null, 2)}`); + } + if (node.state === BlockchainNodeStates.STARTING) { + node.state = BlockchainNodeStates.SYNCING; + if (parsedMsg.numLivePeers === 0) { + const lastBlockWithoutProposal = node.init(true); + await this.server.tryInitializeShard(); + node.state = BlockchainNodeStates.SERVING; + this.server.consensus.init(lastBlockWithoutProposal); + } else { + // Consensus will be initialized after syncing with peers + node.init(false); } - } catch (err) { - logger.error(`Error: ${err} ${err.stack}`); } }); - this.trackerWebSocket.on('close', (code) => { - logger.info(`\n Disconnected from [TRACKER] ${TRACKER_WS_ADDR} with code: ${code}`); + logger.info(`\nDisconnected from [TRACKER] ${TRACKER_WS_ADDR} with code: ${code}`); this.clearIntervalForTrackerUpdate(); this.setIntervalForTrackerConnection(); }); @@ -201,6 +196,8 @@ class P2pClient { this.trackerWebSocket.on('error', (error) => { logger.error(`Error in communication with tracker (${TRACKER_WS_ADDR}): ` + `${JSON.stringify(error, null, 2)}`); + this.clearIntervalForTrackerUpdate(); + this.setIntervalForTrackerConnection(); }); } @@ -246,6 +243,10 @@ class P2pClient { timestamp: Date.now(), }; const signature = signMessage(body, this.server.getNodePrivateKey()); + if (!signature) { + logger.error('The signaure is not correctly generated. Discard the message!'); + return; + } const payload = encapsulateMessage(MessageTypes.ADDRESS_REQUEST, { body: body, signature: signature }); if (!payload) { @@ -290,7 +291,7 @@ class P2pClient { socket.on('message', (message) => { const parsedMessage = JSON.parse(message); const dataProtoVer = _.get(parsedMessage, 'dataProtoVer'); - if (!isValidDataProtoVer(dataProtoVer)) { + if (!VersionUtil.isValidProtocolVersion(dataProtoVer)) { const address = getAddressFromSocket(this.outbound, socket); logger.error(`The data protocol version of the node(${address}) is MISSING or ` + `INAPPROPRIATE. Disconnect the connection.`); @@ -489,6 +490,8 @@ class P2pClient { this.server.stop(); // NOTE(minsulee2): The trackerWebsocket should be checked initialized in order not to get error // in case trackerWebsocket is not properly setup. + this.clearIntervalForTrackerConnection(); + this.clearIntervalForTrackerUpdate(); if (this.trackerWebSocket) this.trackerWebSocket.close(); logger.info('Disconnect from tracker server.'); this.stopHeartbeat(); diff --git a/p2p/server.js b/p2p/server.js index 32a5180c4..9b174056d 100644 --- a/p2p/server.js +++ b/p2p/server.js @@ -25,32 +25,34 @@ const { BlockchainNodeStates, PredefinedDbPaths, WriteDbOperations, + ReadDbOperations, GenesisSharding, GenesisAccounts, AccountProperties, - OwnerProperties, RuleProperties, ShardingProperties, FunctionProperties, FunctionTypes, NativeFunctionIds, - buildOwnerPermissions, LIGHTWEIGHT, FeatureFlags } = require('../common/constants'); const ChainUtil = require('../common/chain-util'); const { + sendGetRequest, sendTxAndWaitForFinalization, +} = require('../common/network-util'); +const { getAddressFromSocket, removeSocketConnectionIfExists, signMessage, getAddressFromMessage, verifySignedMessage, - isValidDataProtoVer, checkTimestamp, closeSocketSafe, encapsulateMessage } = require('./util'); +const PathUtil = require('../common/path-util'); const GCP_EXTERNAL_IP_URL = 'http://metadata.google.internal/computeMetadata/v1/instance' + '/network-interfaces/0/access-configs/0/external-ip'; @@ -124,7 +126,8 @@ class P2pServer { CURRENT_PROTOCOL_VERSION: CURRENT_PROTOCOL_VERSION, COMPATIBLE_MIN_PROTOCOL_VERSION: this.minProtocolVersion, COMPATIBLE_MAX_PROTOCOL_VERSION: this.maxProtocolVersion, - DATA_PROTOCOL_VERSION: this.dataProtocolVersion + DATA_PROTOCOL_VERSION: this.dataProtocolVersion, + CONSENSUS_PROTOCOL_VERSION: this.consensus.consensusProtocolVersion, }; } @@ -373,7 +376,7 @@ class P2pServer { const parsedMessage = JSON.parse(message); const dataProtoVer = _.get(parsedMessage, 'dataProtoVer'); const address = getAddressFromSocket(this.inbound, socket); - if (!isValidDataProtoVer(dataProtoVer)) { + if (!VersionUtil.isValidProtocolVersion(dataProtoVer)) { logger.error(`The data protocol version of the node(${address}) is MISSING or ` + `INAPPROPRIATE. Disconnect the connection.`); closeSocketSafe(this.outbound, socket); @@ -421,6 +424,10 @@ class P2pServer { timestamp: Date.now(), }; const signature = signMessage(body, this.getNodePrivateKey()); + if (!signature) { + logger.error('The signaure is not correctly generated. Discard the message!'); + return; + } const payload = encapsulateMessage(MessageTypes.ADDRESS_RESPONSE, { body: body, signature: signature }); if (!payload) { @@ -615,16 +622,56 @@ class P2pServer { const parentChainEndpoint = GenesisSharding[ShardingProperties.PARENT_CHAIN_POC] + '/json-rpc'; const ownerPrivateKey = ChainUtil.getJsObject( GenesisAccounts, [AccountProperties.OWNER, AccountProperties.PRIVATE_KEY]); + const shardOwner = GenesisSharding[ShardingProperties.SHARD_OWNER]; + const shardingPath = GenesisSharding[ShardingProperties.SHARDING_PATH]; + const appName = _.get(ChainUtil.parsePath(shardingPath), 1, null); + if (!appName) { + throw Error(`Invalid appName given for a shard (${shardingPath})`); + } + const shardingAppConfig = await P2pServer.getShardingAppConfig(parentChainEndpoint, appName); + if (shardingAppConfig !== null && _.get(shardingAppConfig, `admin.${shardOwner}`) !== true) { + throw Error(`Shard owner (${shardOwner}) doesn't have the permission to create a shard (${appName})`); + } + if (shardingAppConfig === null) { + // Create app first. Note that the app should have staked some AIN. + const shardAppCreateTxBody = P2pServer.buildShardAppCreateTxBody(appName); + await sendTxAndWaitForFinalization(parentChainEndpoint, shardAppCreateTxBody, ownerPrivateKey); + } const shardInitTxBody = P2pServer.buildShardingSetupTxBody(); await sendTxAndWaitForFinalization(parentChainEndpoint, shardInitTxBody, ownerPrivateKey); logger.info(`setUpDbForSharding success`); } - static buildShardingSetupTxBody() { + static async getShardingAppConfig(parentChainEndpoint, appName) { + const resp = await sendGetRequest(parentChainEndpoint, 'ain_get', { + type: ReadDbOperations.GET_VALUE, + ref: PathUtil.getManageAppConfigPath(appName) + }); + return _.get(resp, 'data.result.result'); + } + + static buildShardAppCreateTxBody(appName) { const shardOwner = GenesisSharding[ShardingProperties.SHARD_OWNER]; + const timestamp = Date.now(); + return { + operation: { + type: WriteDbOperations.SET_VALUE, + ref: PathUtil.getCreateAppRecordPath(appName, timestamp), + value: { + [PredefinedDbPaths.MANAGE_APP_CONFIG_ADMIN]: { + [shardOwner]: true + } + } + }, + timestamp, + nonce: -1, + gas_price: 0, // NOTE(platfowner): A temporary solution. + } + } + + static buildShardingSetupTxBody() { const shardReporter = GenesisSharding[ShardingProperties.SHARD_REPORTER]; const shardingPath = GenesisSharding[ShardingProperties.SHARDING_PATH]; - const shardingPathRules = `auth.addr === '${shardOwner}'`; const proofHashRulesLight = `auth.addr === '${shardReporter}'`; const proofHashRules = `auth.addr === '${shardReporter}' && ` + '((newData === null && ' + @@ -638,25 +685,6 @@ class P2pServer { operation: { type: WriteDbOperations.SET, op_list: [ - { - type: WriteDbOperations.SET_OWNER, - ref: shardingPath, - value: { - [OwnerProperties.OWNER]: { - [OwnerProperties.OWNERS]: { - [shardOwner]: buildOwnerPermissions(false, true, true, true), - [OwnerProperties.ANYONE]: buildOwnerPermissions(false, false, false, false), - } - } - } - }, - { - type: WriteDbOperations.SET_RULE, - ref: shardingPath, - value: { - [RuleProperties.WRITE]: shardingPathRules - } - }, { type: WriteDbOperations.SET_RULE, ref: ChainUtil.appendPath( diff --git a/p2p/util.js b/p2p/util.js index 1cee00b3d..82a122eff 100644 --- a/p2p/util.js +++ b/p2p/util.js @@ -4,9 +4,7 @@ * into a module, or replaced with another protocol for cross-shard communication. */ -const axios = require('axios'); const _ = require('lodash'); -const semver = require('semver'); const ainUtil = require('@ainblockchain/ain-util'); const logger = require('../logger')('SERVER_UTIL'); const { @@ -16,80 +14,18 @@ const { } = require('../common/constants'); const ChainUtil = require('../common/chain-util'); -async function sendTxAndWaitForFinalization(endpoint, tx, privateKey) { - const res = await signAndSendTx(endpoint, tx, privateKey); - if (_.get(res, 'errMsg', false) || !_.get(res, 'success', false)) { - throw Error(`Failed to sign and send tx: ${res.errMsg}`); - } - if (!(await waitUntilTxFinalize(endpoint, _.get(res, 'txHash', null)))) { - throw Error('Transaction did not finalize in time.' + - 'Try selecting a different parent_chain_poc.'); +function _isValidMessage(message) { + const body = _.get(message, 'data.body'); + if (!body || !ChainUtil.isDict(body)) { + logger.error('Data body is not included in the message.'); + return false; } -} - -async function sendSignedTx(endpoint, params) { - return await axios.post( - endpoint, - { - method: 'ain_sendSignedTransaction', - params, - jsonrpc: '2.0', - id: 0 - } - ).then((resp) => { - const result = _.get(resp, 'data.result.result.result', {}); - const success = !ChainUtil.isFailedTx(result); - return { success, errMsg: result.error_message }; - }).catch((err) => { - logger.error(`Failed to send transaction: ${err}`); - return { success: false, errMsg: err.message }; - }); -} - -async function signAndSendTx(endpoint, tx, privateKey) { - const { txHash, signedTx } = ChainUtil.signTransaction(tx, privateKey); - const result = await sendSignedTx(endpoint, signedTx); - return Object.assign(result, { txHash }); -} - -async function waitUntilTxFinalize(endpoint, txHash) { - while (true) { - const confirmed = await sendGetRequest( - endpoint, - 'ain_getTransactionByHash', - {hash: txHash} - ) - .then((resp) => { - return (_.get(resp, 'data.result.result.is_finalized', false) === true); - }) - .catch((err) => { - logger.error(`Failed to confirm transaction: ${err}`); - return false; - }); - if (confirmed) { - return true; - } - await ChainUtil.sleep(1000); + const signature = _.get(message, 'data.signature'); + if (!signature) { + logger.error('Data signature is not included in the message.'); + return false; } -} - -function sendGetRequest(endpoint, method, params) { - // NOTE(platfowner): .then() was used here to avoid some unexpected behavior of axios.post() - // (see https://github.com/ainblockchain/ain-blockchain/issues/101) - return axios.post( - endpoint, - { - method, - params: Object.assign(params, { protoVer: CURRENT_PROTOCOL_VERSION }), - jsonrpc: '2.0', - id: 0 - } - ).then((resp) => { - return resp; - }).catch((err) => { - logger.error(`Failed to send get request: ${err}`); - return null; - }); + return true; } function getAddressFromSocket(connectionObj, socket) { @@ -103,39 +39,54 @@ function removeSocketConnectionIfExists(connectionObj, address) { } } +function closeSocketSafe(connections, socket) { + const address = getAddressFromSocket(connections, socket); + removeSocketConnectionIfExists(connections, address); + socket.close(); +} + function signMessage(messageBody, privateKey) { - return ainUtil.ecSignMessage(JSON.stringify(messageBody), Buffer.from(privateKey, 'hex')); + if (!ChainUtil.isDict(messageBody)) { + logger.error('The message body must be the object type.'); + return null; + } + let privateKeyBuffer; + try { + privateKeyBuffer = Buffer.from(privateKey, 'hex'); + } catch { + logger.error('The private key is not correctly set on the buffer to sign a message.'); + return null; + } + if (!privateKey || !ainUtil.isValidPrivate(privateKeyBuffer)) { + logger.error('The private key is not optional but mandatory or worng private key is typed.'); + return null; + } + return ainUtil.ecSignMessage(JSON.stringify(messageBody), privateKeyBuffer); } function getAddressFromMessage(message) { - const hashedMessage = ainUtil.hashMessage(JSON.stringify(message.data.body)); - return ChainUtil.getAddressFromSignature(hashedMessage, message.data.signature); + if (!_isValidMessage(message)) { + return null; + } else { + const hashedMessage = ainUtil.hashMessage(JSON.stringify(message.data.body)); + return ChainUtil.getAddressFromSignature(hashedMessage, message.data.signature); + } } function verifySignedMessage(message, address) { - return ainUtil.ecVerifySig(JSON.stringify(message.data.body), message.data.signature, address); -} - -function closeSocketSafe(connections, socket) { - const address = getAddressFromSocket(connections, socket); - removeSocketConnectionIfExists(connections, address); - socket.close(); -} - -function isValidDataProtoVer(version) { - if (!version || !semver.valid(version)) { - return false; + if (!_isValidMessage(message)) { + return null; } else { - return true; + return ainUtil.ecVerifySig(JSON.stringify(message.data.body), message.data.signature, address); } } function encapsulateMessage(type, dataObj) { - if (!type) { + if (!type || !ChainUtil.isString(type)) { logger.error('Type must be specified.'); return null; }; - if (!dataObj) { + if (!dataObj || !ChainUtil.isDict(dataObj)) { logger.error('dataObj cannot be null or undefined.'); return null; } @@ -150,7 +101,7 @@ function encapsulateMessage(type, dataObj) { } function checkTimestamp(timestamp) { - if (!timestamp) { + if (!timestamp || !ChainUtil.isNumber(timestamp)) { return false; } else { const now = Date.now(); @@ -163,17 +114,12 @@ function checkTimestamp(timestamp) { } module.exports = { - sendTxAndWaitForFinalization, - sendSignedTx, - signAndSendTx, - sendGetRequest, getAddressFromSocket, removeSocketConnectionIfExists, signMessage, getAddressFromMessage, verifySignedMessage, closeSocketSafe, - isValidDataProtoVer, checkTimestamp, encapsulateMessage }; diff --git a/package.json b/package.json index 9bbb9ac86..b22ec4063 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ain-blockchain", "description": "AI Network Blockchain", - "version": "0.7.3", + "version": "0.7.6", "private": true, "license": "MIT", "author": "dev@ainetwork.ai", @@ -19,14 +19,16 @@ "test_chain_util": "./node_modules/mocha/bin/mocha --timeout 160000 unittest/chain-util.test.js", "test_state_util": "./node_modules/mocha/bin/mocha --timeout 160000 unittest/state-util.test.js", "test_block_pool": "MIN_NUM_VALIDATORS=1 ./node_modules/mocha/bin/mocha --timeout 160000 unittest/block-pool.test.js", - "test_db": "./node_modules/mocha/bin/mocha --timeout 160000 unittest/db.test.js", + "test_db": "MIN_NUM_VALIDATORS=1 ./node_modules/mocha/bin/mocha --timeout 160000 unittest/db.test.js", + "test_tx_pool": "MIN_NUM_VALIDATORS=1 ./node_modules/mocha/bin/mocha --timeout 160000 unittest/tx-pool.test.js", + "test_p2p_util": "./node_modules/mocha/bin/mocha --timeout 160000 unittest/p2p-util.test.js", + "test_p2p": "./node_modules/mocha/bin/mocha --timeout 320000 unittest/p2p.test.js", "test_unit": "MIN_NUM_VALIDATORS=1 ./node_modules/mocha/bin/mocha --timeout 160000 \"unittest/*.test.js\"", "test_node": "./node_modules/mocha/bin/mocha --timeout 160000 integration/node.test.js", "test_blockchain": "./node_modules/mocha/bin/mocha --timeout 160000 integration/blockchain.test.js", "test_dapp": "./node_modules/mocha/bin/mocha --timeout 160000 integration/afan_dapp.test.js", "test_sharding": "./node_modules/mocha/bin/mocha --timeout 320000 integration/sharding.test.js", "test_integration": "./node_modules/mocha/bin/mocha --timeout 640000 \"integration/*.test.js\"", - "test_tx_pool": "MIN_NUM_VALIDATORS=1 ./node_modules/mocha/bin/mocha --timeout 160000 unittest/tx-pool.test.js", "loadtest": "sh loadtest/load_tester.sh", "lint": "eslint --ext js -c .eslintrc.json ." }, @@ -61,6 +63,7 @@ "url": "^0.11.0", "util": "^0.11.1", "uuid": "^3.4.0", + "valid-url": "^1.0.9", "winston": "^3.3.3", "winston-daily-rotate-file": "^4.4.2", "ws": "^7.4.6" diff --git a/reset_blockchain_gcp.sh b/reset_blockchain_gcp.sh index 5dbb1aea8..39580aa81 100644 --- a/reset_blockchain_gcp.sh +++ b/reset_blockchain_gcp.sh @@ -29,13 +29,20 @@ NODE_2_TARGET_ADDR="${GCP_USER}@${SEASON}-node-2-singapore" NODE_3_TARGET_ADDR="${GCP_USER}@${SEASON}-node-3-iowa" NODE_4_TARGET_ADDR="${GCP_USER}@${SEASON}-node-4-netherlands" +TRACKER_ZONE="asia-east1-b" +NODE_0_ZONE="asia-east1-b" +NODE_1_ZONE="us-west1-b" +NODE_2_ZONE="asia-southeast1-b" +NODE_3_ZONE="us-central1-a" +NODE_4_ZONE="europe-west4-a" + printf "\nStopping parent blockchain..." -gcloud compute ssh $TRACKER_TARGET_ADDR --command "killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_0_TARGET_ADDR --command "killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_1_TARGET_ADDR --command "killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_2_TARGET_ADDR --command "killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_3_TARGET_ADDR --command "killall node" --project $PROJECT_ID -gcloud compute ssh $NODE_4_TARGET_ADDR --command "killall node" --project $PROJECT_ID +gcloud compute ssh $TRACKER_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $TRACKER_ZONE +gcloud compute ssh $NODE_0_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_0_ZONE +gcloud compute ssh $NODE_1_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_1_ZONE +gcloud compute ssh $NODE_2_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_2_ZONE +gcloud compute ssh $NODE_3_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_3_ZONE +gcloud compute ssh $NODE_4_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_4_ZONE printf "\nStopping shard blockchains..." if [ "$3" -gt 0 ]; then @@ -48,27 +55,27 @@ if [ "$3" -gt 0 ]; then SHARD_NODE_1_TARGET_ADDR="${GCP_USER}@${SEASON}-shard-${i}-node-1-oregon" SHARD_NODE_2_TARGET_ADDR="${GCP_USER}@${SEASON}-shard-${i}-node-2-singapore" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command "killall node" --project $PROJECT_ID - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command "killall node" --project $PROJECT_ID - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command "killall node" --project $PROJECT_ID - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command "killall node" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $TRACKER_ZONE + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_0_ZONE + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_1_ZONE + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command "killall node" --project $PROJECT_ID --zone $NODE_2_ZONE done fi # ssh into each instance, clean up, and start running the nodes printf "\n\n############################\n# Running parent tracker #\n############################\n\n" -gcloud compute ssh $TRACKER_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && . start_tracker_gcp.sh" --project $PROJECT_ID +gcloud compute ssh $TRACKER_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_tracker_gcp.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n###########################\n# Running parent node 0 #\n###########################\n\n" -gcloud compute ssh $NODE_0_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON 0 0" --project $PROJECT_ID +gcloud compute ssh $NODE_0_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON 0 0" --project $PROJECT_ID --zone $NODE_0_ZONE sleep 3 printf "\n\n#########################\n# Running parent node 1 #\n#########################\n\n" -gcloud compute ssh $NODE_1_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON 0 1" --project $PROJECT_ID +gcloud compute ssh $NODE_1_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON 0 1" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n#########################\n# Running parent node 2 #\n#########################\n\n" -gcloud compute ssh $NODE_2_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON 0 2" --project $PROJECT_ID +gcloud compute ssh $NODE_2_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON 0 2" --project $PROJECT_ID --zone $NODE_2_ZONE printf "\n\n#########################\n# Running parent node 3 #\n#########################\n\n" -gcloud compute ssh $NODE_3_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON 0 3" --project $PROJECT_ID +gcloud compute ssh $NODE_3_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON 0 3" --project $PROJECT_ID --zone $NODE_3_ZONE printf "\n\n#########################\n# Running parent node 4 #\n#########################\n\n" -gcloud compute ssh $NODE_4_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON 0 4" --project $PROJECT_ID +gcloud compute ssh $NODE_4_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON 0 4" --project $PROJECT_ID --zone $NODE_4_ZONE sleep 10 @@ -85,13 +92,13 @@ if [ "$3" -gt 0 ]; then # ssh into each instance, clean up, and start running the nodes printf "\n\n###########################\n# Running shard_$i tracker #\n###########################\n\n" - gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && . start_tracker_gcp.sh" --project $PROJECT_ID + gcloud compute ssh $SHARD_TRACKER_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_tracker_gcp.sh" --project $PROJECT_ID --zone $TRACKER_ZONE printf "\n\n##########################\n# Running shard_$i node 0 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON $i 0" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_0_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON $i 0" --project $PROJECT_ID --zone $NODE_0_ZONE sleep 3 printf "\n\n##########################\n# Running shard_$i node 1 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON $i 1" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_1_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON $i 1" --project $PROJECT_ID --zone $NODE_1_ZONE printf "\n\n##########################\n# Running shard_$i node 2 #\n##########################\n\n" - gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf ./chains/ && . start_node_gcp.sh $SEASON $i 2" --project $PROJECT_ID + gcloud compute ssh $SHARD_NODE_2_TARGET_ADDR --command "cd ../ain-blockchain && sudo rm -rf ./logs/ && sudo rm -rf /home/ain_blockchain_data/ && . start_node_gcp.sh $SEASON $i 2" --project $PROJECT_ID --zone $NODE_2_ZONE done -fi \ No newline at end of file +fi diff --git a/setup_node_gcp.sh b/setup_node_gcp.sh index 0b42b0e01..4915bf23d 100644 --- a/setup_node_gcp.sh +++ b/setup_node_gcp.sh @@ -4,7 +4,10 @@ sudo killall node echo 'Setting up working directory..' cd -sudo rm -rf ../ain-blockchain +sudo rm -rf /home/ain_blockchain_data +sudo mkdir /home/ain_blockchain_data +sudo chmod 777 /home/ain_blockchain_data +sudo rm -rf ../ain-blockchain* sudo mkdir ../ain-blockchain sudo chmod 777 ../ain-blockchain mv * ../ain-blockchain diff --git a/setup_tracker_gcp.sh b/setup_tracker_gcp.sh index f870cb2d8..1539eca38 100644 --- a/setup_tracker_gcp.sh +++ b/setup_tracker_gcp.sh @@ -6,7 +6,10 @@ killall node echo 'Setting up working directory..' cd -sudo rm -rf ../ain-blockchain +sudo rm -rf /home/ain_blockchain_data +sudo mkdir /home/ain_blockchain_data +sudo chmod 777 /home/ain_blockchain_data +sudo rm -rf ../ain-blockchain* sudo mkdir ../ain-blockchain sudo chmod 777 ../ain-blockchain mv * ../ain-blockchain diff --git a/start_node_gcp.sh b/start_node_gcp.sh index 0cdac516e..725f80dd7 100644 --- a/start_node_gcp.sh +++ b/start_node_gcp.sh @@ -87,6 +87,7 @@ export ENABLE_TX_SIG_VERIF_WORKAROUND=false export ENABLE_GAS_FEE_WORKAROUND=true export LIGHTWEIGHT=false export STAKE=100000 +export BLOCKCHAIN_DATA_DIR="/home/ain_blockchain_data" echo 'Starting up Node server..' nohup node --async-stack-traces client/index.js >/dev/null 2>error_logs.txt & diff --git a/start_node_incremental_gcp.sh b/start_node_incremental_gcp.sh index 4681b8539..2df4d62ab 100644 --- a/start_node_incremental_gcp.sh +++ b/start_node_incremental_gcp.sh @@ -95,6 +95,7 @@ export ENABLE_TX_SIG_VERIF_WORKAROUND=false export ENABLE_GAS_FEE_WORKAROUND=true export LIGHTWEIGHT=false export STAKE=100000 +export BLOCKCHAIN_DATA_DIR="/home/ain_blockchain_data" date=$(date '+%Y-%m-%dT%H:%M') echo "date=$date" @@ -105,7 +106,7 @@ echo "NEW_DIR_PATH=$NEW_DIR_PATH" OLD_DIR_PATH=$(find ../ain-blockchain* -maxdepth 0 -type d) echo "OLD_DIR_PATH=$OLD_DIR_PATH" -# 3. Kill old node & remove old directory +# 3. Kill old node & remove old directory (but keep the chain data) sudo killall node sudo rm -rf ../ain-blockchain* @@ -113,6 +114,8 @@ sudo rm -rf ../ain-blockchain* sudo mkdir $NEW_DIR_PATH sudo chmod 777 $NEW_DIR_PATH mv * $NEW_DIR_PATH +sudo mkdir -p $BLOCKCHAIN_DATA_DIR +sudo chmod 777 $BLOCKCHAIN_DATA_DIR # 5. Install dependencies cd $NEW_DIR_PATH diff --git a/start_servers.sh b/start_servers.sh index ec84b4c6a..185c6b55d 100755 --- a/start_servers.sh +++ b/start_servers.sh @@ -1,33 +1,33 @@ # PARENT CHAIN node ./tracker-server/index.js & sleep 5 -MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=0 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=0 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=1 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=1 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & +sleep 1 +MIN_NUM_VALIDATORS=5ACCOUNT_INDEX=2 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=2 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=3 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=3 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & -sleep 10 -MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=4 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=5 ACCOUNT_INDEX=4 STAKE=100000 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 # CHILD CHAIN 1 GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9010 P2P_PORT=6010 node ./tracker-server/index.js & sleep 10 -GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9011 P2P_PORT=6011 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9011 P2P_PORT=6011 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9012 P2P_PORT=6012 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9012 P2P_PORT=6012 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9013 P2P_PORT=6013 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9013 P2P_PORT=6013 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 # CHILD CHAIN 2 GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9020 P2P_PORT=6020 node ./tracker-server/index.js & sleep 10 -GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9021 P2P_PORT=6021 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 TRACKER_WS_ADDR=ws://localhost:6020 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9021 P2P_PORT=6021 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 TRACKER_WS_ADDR=ws://localhost:6020 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9022 P2P_PORT=6022 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 TRACKER_WS_ADDR=ws://localhost:6020 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9022 P2P_PORT=6022 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 TRACKER_WS_ADDR=ws://localhost:6020 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 -GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9023 P2P_PORT=6023 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 TRACKER_WS_ADDR=ws://localhost:6020 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/sim-shard PORT=9023 P2P_PORT=6023 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 TRACKER_WS_ADDR=ws://localhost:6020 STAKE=250 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 10 diff --git a/start_servers_afan.sh b/start_servers_afan.sh index ce95a0582..207e969ce 100755 --- a/start_servers_afan.sh +++ b/start_servers_afan.sh @@ -1,21 +1,19 @@ -rm -rf ./chains/ ./logs/ - # PARENT CHAIN CONSOLE_LOG=true node ./tracker-server/index.js & sleep 5 -MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 5 -MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 5 -MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 15 # AFAN CHILD CHAIN GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9010 P2P_PORT=6000 CONSOLE_LOG=true node ./tracker-server/index.js & sleep 5 -GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9011 P2P_PORT=6001 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9011 P2P_PORT=6001 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=0 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 5 -GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9012 P2P_PORT=6002 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9012 P2P_PORT=6002 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=1 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 5 -GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9013 P2P_PORT=6003 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true node ./client/index.js & +GENESIS_CONFIGS_DIR=genesis-configs/afan-shard PORT=9013 P2P_PORT=6003 MIN_NUM_VALIDATORS=3 ACCOUNT_INDEX=2 CONSOLE_LOG=true ENABLE_DEV_SET_CLIENT_API=true ENABLE_TX_SIG_VERIF_WORKAROUND=true ENABLE_GAS_FEE_WORKAROUND=true BLOCKCHAIN_DATA_DIR=~/ain_blockchain_data node ./client/index.js & sleep 15 diff --git a/stop_servers.sh b/stop_servers.sh index da9987613..f9ccbd0c2 100755 --- a/stop_servers.sh +++ b/stop_servers.sh @@ -1,5 +1,4 @@ - +killall -9 node # SIGKILL +rm -rf ~/ain_blockchain_data/ BASEDIR=$(dirname "$0") -rm -rf $BASEDIR/chains/ rm -rf $BASEDIR/logs/ -killall -9 node # SIGKILL diff --git a/tools/util.js b/tools/util.js index efd755ecd..4975c79bb 100644 --- a/tools/util.js +++ b/tools/util.js @@ -3,6 +3,7 @@ const axios = require('axios'); const { CURRENT_PROTOCOL_VERSION } = require('../common/constants'); const ChainUtil = require('../common/chain-util'); +// FIXME(minsulee2): this is duplicated function see: ./common/network-util.js function signAndSendTx(endpointUrl, txBody, privateKey) { console.log('\n*** signAndSendTx():'); const {txHash, signedTx} = ChainUtil.signTransaction(txBody, privateKey); diff --git a/tx-pool/index.js b/tx-pool/index.js index 60e63e3b9..af8098333 100644 --- a/tx-pool/index.js +++ b/tx-pool/index.js @@ -19,12 +19,10 @@ const { StateVersions, } = require('../common/constants'); const ChainUtil = require('../common/chain-util'); -const PathUtil = require('../common/path-util'); const { sendGetRequest, signAndSendTx -} = require('../p2p/util'); -const DB = require('../db'); +} = require('../common/network-util'); const Transaction = require('./transaction'); const parentChainEndpoint = GenesisSharding[ShardingProperties.PARENT_CHAIN_POC] + '/json-rpc'; diff --git a/unittest/db.test.js b/unittest/db.test.js index c50335b61..1097bcb4b 100644 --- a/unittest/db.test.js +++ b/unittest/db.test.js @@ -80,13 +80,32 @@ describe("DB initialization", () => { describe("Rules", () => { it("loading rules properly on initialization", () => { - assert.deepEqual(node.db.getRule("/"), GenesisRules); + const genesisRuleWithConsensusApp = JSON.parse(JSON.stringify(GenesisRules)); + ChainUtil.setJsObject( + genesisRuleWithConsensusApp, + ['apps', 'consensus'], + {".write": "auth.addr === '0xAAAf6f50A0304F12119D218b94bea8082642515B'"} + ); + assert.deepEqual(node.db.getRule("/"), genesisRuleWithConsensusApp); }) }) describe("Owners", () => { it("loading owners properly on initialization", () => { - assert.deepEqual(node.db.getOwner('/'), GenesisOwners); + const genesisOwnerWithConsensusApp = JSON.parse(JSON.stringify(GenesisOwners)); + ChainUtil.setJsObject(genesisOwnerWithConsensusApp, ['apps', 'consensus'], { + ".owner": { + owners: { + "0xAAAf6f50A0304F12119D218b94bea8082642515B": { + branch_owner: true, + write_function: true, + write_owner: true, + write_rule: true + } + } + } + }); + assert.deepEqual(node.db.getOwner('/'), genesisOwnerWithConsensusApp); }) }) }) @@ -141,17 +160,32 @@ describe("DB operations", () => { "some": { "$var_path": { ".function": { - "fid_var": "some function config with var path" + "fid_var": { + "function_type": "REST", + "function_id": "fid_var", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } }, "path": { ".function": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, }, "deeper": { "path": { ".function": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "function_type": "REST", + "function_id": "fid_deeper", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } } } } @@ -190,7 +224,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true, }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -208,7 +242,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true, }, - "ijkl": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -257,12 +291,22 @@ describe("DB operations", () => { it("when retrieving existing function config", () => { assert.deepEqual(node.db.getFunction("/test/test_function/some/path"), { ".function": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "deeper": { "path": { ".function": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } } @@ -303,7 +347,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true, }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -321,7 +365,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true, }, - "ijkl": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -347,7 +391,12 @@ describe("DB operations", () => { }, "matched_config": { "config": { - "fid_var": "some function config with var path" + "fid_var": { + "function_type": "REST", + "function_id": "fid_var", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, }, "path": "/test/test_function/some/$var_path" }, @@ -364,14 +413,24 @@ describe("DB operations", () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path" }, "subtree_configs": [ { "config": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/deeper/path" } @@ -385,7 +444,12 @@ describe("DB operations", () => { }, "matched_config": { "config": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path/deeper/path" }, @@ -407,7 +471,12 @@ describe("DB operations", () => { "subtree_configs": [ { "config": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/path" } @@ -502,7 +571,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -526,7 +595,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "ijkl": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -553,7 +622,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -577,7 +646,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "ijkl": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -604,7 +673,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -653,15 +722,19 @@ describe("DB operations", () => { describe("evalOwner()", () => { it("when evaluating existing owner with matching address", () => { - expect(node.db.evalOwner("/test/test_owner/some/path", 'write_owner', { addr: 'abcd' })) - .to.equal(true); - expect(node.db.evalOwner("/test/test_owner/some/path", 'write_rule', { addr: 'abcd' })) + expect(node.db.evalOwner( + "/test/test_owner/some/path", 'write_owner', + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' })) + .to.equal(true); + expect(node.db.evalOwner("/test/test_owner/some/path", 'write_rule', { addr: '' })) .to.equal(false); expect(node.db.evalOwner( - "/test/test_owner/some/path/deeper/path", 'write_owner', { addr: 'ijkl' })) + "/test/test_owner/some/path/deeper/path", 'write_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true); expect(node.db.evalOwner( - "/test/test_owner/some/path/deeper/path", 'write_rule', { addr: 'ijkl' })) + "/test/test_owner/some/path/deeper/path", 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false); }) @@ -680,10 +753,12 @@ describe("DB operations", () => { it("when evaluating closest owner", () => { expect(node.db.evalOwner( - "/test/test_owner/some/path/deeper", 'write_owner', { addr: 'abcd' })) + "/test/test_owner/some/path/deeper", 'write_owner', + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' })) .to.equal(true); expect(node.db.evalOwner( - "/test/test_owner/some/path/deeper", 'write_rule', { addr: 'abcd' })) + "/test/test_owner/some/path/deeper", 'write_rule', + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' })) .to.equal(false); expect(node.db.evalOwner( "/test/test_owner/some/path/deeper", 'write_owner', { addr: 'other' })) @@ -757,7 +832,12 @@ describe("DB operations", () => { "subtree_configs": [ { "config": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/path" } @@ -793,7 +873,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -850,7 +930,7 @@ describe("DB operations", () => { type: "EVAL_OWNER", ref: "/test/test_owner/some/path", permission: "write_owner", - address: "abcd", + address: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1", timestamp: Date.now(), }, ]), [ @@ -865,12 +945,22 @@ describe("DB operations", () => { }, { ".function": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "deeper": { "path": { ".function": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } } @@ -884,7 +974,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true, }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -902,7 +992,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true, }, - "ijkl": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -921,14 +1011,24 @@ describe("DB operations", () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/test/test_function/some/path" }, "subtree_configs": [ { "config": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "path": "/deeper/path" } @@ -964,7 +1064,7 @@ describe("DB operations", () => { "write_owner": false, "write_rule": true }, - "abcd": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": false, "write_owner": true, @@ -1191,19 +1291,34 @@ describe("DB operations", () => { it("when overwriting existing function config with simple path", () => { const functionConfig = { ".function": { - "fid": "other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }; expect(node.db.setFunction("/test/test_function/some/path", functionConfig).code) .to.equal(0); assert.deepEqual(node.db.getFunction("/test/test_function/some/path"), { ".function": { - "fid": "other function config" // modified + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", // modified + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "deeper": { "path": { ".function": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } } @@ -1213,7 +1328,12 @@ describe("DB operations", () => { it("when writing with variable path", () => { const functionConfig = { ".function": { - "fid_other": "other function config" + "fid_other": { + "event_listener": "https://events.ainetwork.ai/trigger2", + "function_id": "fid_other", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } }; expect(node.db.setFunction("/test/test_function/some/$variable/path", functionConfig).code) @@ -1238,9 +1358,29 @@ describe("DB operations", () => { expect(node.db.getFunction("test/new2/unchartered/nested/path2")).to.equal(null) }) + it("when writing invalid function tree", () => { + const functionTreeBefore = node.db.getOwner("/test/test_function/some/path"); + assert.deepEqual(node.db.setFunction( + "/test/test_function/some/path", { ".function": null }), { + "code": 405, + "error_message": "Invalid function tree: /.function", + "gas_amount": 0 + }); + assert.deepEqual(node.db.getOwner("/test/test_function/some/path"), functionTreeBefore); + }) + it("when writing with invalid path", () => { assert.deepEqual(node.db.setFunction( - "/test/test_function/some/path/.", "some function config"), { + "/test/test_function/some/path/.", { + ".function": { + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } + } + }), { "code": 402, "error_message": "Invalid path: /test/test_function/some/path/.", "gas_amount": 0 @@ -1278,8 +1418,20 @@ describe("DB operations", () => { expect(node.db.getRule("/test/test_rule/some/path2")).to.equal(null) }) + it("when writing invalid rule tree", () => { + const ruleTreeBefore = node.db.getRule("/test/test_rule/some/path"); + assert.deepEqual(node.db.setRule( + "/test/test_rule/some/path", { ".write": null }), { + "code": 504, + "error_message": "Invalid rule tree: /.write", + "gas_amount": 0 + }); + assert.deepEqual(node.db.getRule("/test/test_rule/some/path"), ruleTreeBefore); + }) + it("when writing with invalid path", () => { - assert.deepEqual(node.db.setRule("/test/test_rule/some/path/.", "some rule config"), { + assert.deepEqual(node.db.setRule("/test/test_rule/some/path/.", + { ".write": "some rule config" } ), { "code": 502, "error_message": "Invalid path: /test/test_rule/some/path/.", "gas_amount": 0 @@ -1289,10 +1441,24 @@ describe("DB operations", () => { describe("setOwner()", () => { it("when overwriting existing owner config", () => { - const ownerConfig = {".owner": "other owner config"}; - expect(node.db.setOwner("/test/test_owner/some/path", ownerConfig, { addr: 'abcd' }).code) - .to.equal(0) - assert.deepEqual(node.db.getOwner("/test/test_owner/some/path"), ownerConfig) + const ownerTree = { + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + }}; + assert.deepEqual(node.db.setOwner( + "/test/test_owner/some/path", ownerTree, + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }), { + "code": 0, + "gas_amount": 1 + }); + assert.deepEqual(node.db.getOwner("/test/test_owner/some/path"), ownerTree) }) it("when writing invalid object", () => { @@ -1311,8 +1477,42 @@ describe("DB operations", () => { expect(node.db.getOwner("/test/test_owner/some/path2")).to.equal(null) }) + it("when writing invalid owner tree", () => { + const ownerTreeBefore = node.db.getOwner("/test/test_owner/some/path"); + assert.deepEqual(node.db.setOwner("/test/test_owner/some/path", { + ".owner": "invalid owners config" + }, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }), { + "code": 604, + "error_message": "Invalid owner tree: /.owner", + "gas_amount": 0 + }); + assert.deepEqual(node.db.getOwner("/test/test_owner/some/path"), ownerTreeBefore); + + assert.deepEqual(node.db.setOwner("/test/test_owner/some/path", { + ".owner": { + "owners": "invalid owners config" + } + }, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }), { + "code": 604, + "error_message": "Invalid owner tree: /.owner/owners", + "gas_amount": 0 + }); + assert.deepEqual(node.db.getOwner("/test/test_owner/some/path"), ownerTreeBefore); + }) + it("when writing with invalid path", () => { - assert.deepEqual(node.db.setOwner("/test/test_owner/some/path/.", "some owner config"), { + assert.deepEqual(node.db.setOwner("/test/test_owner/some/path/.", { + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + } + }, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }), { "code": 602, "error_message": "Invalid path: /test/test_owner/some/path/.", "gas_amount": 0 @@ -1575,7 +1775,12 @@ describe("DB operations", () => { ref: "/test/test_function/some/path", value: { ".function": { - "fid": "other function config" + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } }, @@ -1590,10 +1795,19 @@ describe("DB operations", () => { type: "SET_OWNER", ref: "/test/test_owner/some/path", value: { - ".owner": "other owner config" + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + } } } - ], { addr: 'abcd' }, null, { extra: { executed_at: 1234567890000 }}), { + ], { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}), { "result_list": [ { "code": 0, @@ -1626,12 +1840,22 @@ describe("DB operations", () => { expect(node.db.getValue("test/decrement/value")).to.equal(10) assert.deepEqual(node.db.getFunction("/test/test_function/some/path"), { ".function": { - "fid": "other function config" // modiied + "fid": { + "event_listener": "https://events.ainetwork.ai/trigger2", // modified + "function_id": "fid", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } }, "deeper": { "path": { ".function": { - "fid_deeper": "some function config deeper" + "fid_deeper": { + "event_listener": "https://events.ainetwork.ai/trigger", + "function_id": "fid_deeper", + "function_type": "REST", + "service_name": "https://ainetwork.ai" + } } } } @@ -1639,7 +1863,18 @@ describe("DB operations", () => { assert.deepEqual( node.db.getRule("/test/test_rule/some/path"), { ".write": "other rule config" }); assert.deepEqual( - node.db.getOwner("/test/test_owner/some/path"), { ".owner": "other owner config" }); + node.db.getOwner("/test/test_owner/some/path"), { + ".owner": { + "owners": { + "*": { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + } + }); }) it("returning error code and leaving value unchanged when an operation fails", () => { @@ -2031,7 +2266,7 @@ describe("DB operations", () => { const overSizeTx = Transaction.fromTxBody(overSizeTxBody, node.account.private_key); assert.deepEqual(node.db.executeTransaction(overSizeTx, node.bc.lastBlockNumber() + 1), { code: 24, - error_message: "Out of tree size limit (1001521 > 1000000)", + error_message: "Out of tree size limit (1001532 > 1000000)", gas_amount: 0, }); }) @@ -2058,32 +2293,28 @@ describe("DB operations", () => { assert.deepEqual(valueResult.code, 0); emptyRules = { - "terminal_1a": null, - "terminal_1b": null, - "terminal_1c": "", "node_1a": { - "node_2": { - "terminal_3": null, - "node_3": { - ".write": "some rule" + "node_2a": { + "node_3a": { + ".write": "some rule a" } } }, "node_1b": { - "terminal_2": null, + "node_2b": { + "node_3b": { + ".write": "some rule b" + } + } } }; const ruleResult = node.db.setRule("/test/empty_rules/node_0", emptyRules); assert.deepEqual(ruleResult.code, 0); emptyOwners = { - "terminal_1a": null, - "terminal_1b": null, - "terminal_1c": "", "node_1a": { - "node_2": { - "terminal_3": null, - "node_3": { + "node_2a": { + "node_3a": { ".owner": { "owners": { "*": { @@ -2098,7 +2329,20 @@ describe("DB operations", () => { } }, "node_1b": { - "terminal_2": null, + "node_2b": { + "node_3b": { + ".owner": { + "owners": { + "*": { + "branch_owner": false, + "write_owner": false, + "write_rule": false, + "write_function": false + } + } + } + } + } } }; const ownerResult = node.db.setOwner("/test/empty_owners/node_0", emptyOwners); @@ -2148,41 +2392,44 @@ describe("DB operations", () => { it("when setRule() with non-empty rule", () => { expect(node.db.setRule( - "/test/empty_rules/node_0/node_1a/node_2/node_3", { + "/test/empty_rules/node_0/node_1a/node_2a/node_3a", { ".write": "some other rule" }).code).to.equal(0) assert.deepEqual(node.db.getRule("/test/empty_rules/node_0"), { - "terminal_1a": null, - "terminal_1b": null, - "terminal_1c": "", "node_1a": { - "node_2": { - "terminal_3": null, - "node_3": { + "node_2a": { + "node_3a": { ".write": "some other rule" } } }, "node_1b": { - "terminal_2": null, + "node_2b": { + "node_3b": { + ".write": "some rule b" + } + } } }) }) it("when setRule() with 'null' rule", () => { expect(node.db.setRule( - "/test/empty_rules/node_0/node_1a/node_2/node_3", null).code).to.equal(0); + "/test/empty_rules/node_0/node_1a/node_2a/node_3a", null).code).to.equal(0); assert.deepEqual(node.db.getRule("/test/empty_rules/node_0"), { - "terminal_1c": "", "node_1b": { - "terminal_2": null, + "node_2b": { + "node_3b": { + ".write": "some rule b" + } + } } }) }) it("when setOwner() with non-empty owner", () => { expect(node.db.setOwner( - "/test/empty_owners/node_0/node_1a/node_2/node_3", { + "/test/empty_owners/node_0/node_1a/node_2a/node_3a", { ".owner": { "owners": { "*": { @@ -2195,13 +2442,9 @@ describe("DB operations", () => { } }).code).to.equal(0) assert.deepEqual(node.db.getOwner("/test/empty_owners/node_0"), { - "terminal_1a": null, - "terminal_1b": null, - "terminal_1c": "", "node_1a": { - "node_2": { - "terminal_3": null, - "node_3": { + "node_2a": { + "node_3a": { ".owner": { "owners": { "*": { @@ -2216,18 +2459,43 @@ describe("DB operations", () => { } }, "node_1b": { - "terminal_2": null, + "node_2b": { + "node_3b": { + ".owner": { + "owners": { + "*": { + "branch_owner": false, + "write_owner": false, + "write_rule": false, + "write_function": false + } + } + } + } + } } }) }) it("when setOwner() with 'null' owner", () => { expect(node.db.setOwner( - "/test/empty_owners/node_0/node_1a/node_2/node_3", null).code).to.equal(0); + "/test/empty_owners/node_0/node_1a/node_2a/node_3a", null).code).to.equal(0); assert.deepEqual(node.db.getOwner("/test/empty_owners/node_0"), { - "terminal_1c": "", "node_1b": { - "terminal_2": null, + "node_2b": { + "node_3b": { + ".owner": { + "owners": { + "*": { + "branch_owner": false, + "write_owner": false, + "write_rule": false, + "write_function": false + } + } + } + } + } } }) }) @@ -2371,13 +2639,13 @@ describe("DB owner config", () => { "write_rule": false, "write_function": false }, - "aaaa": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": false, "write_owner": false, "write_rule": false, "write_function": false }, - "known_user": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_owner": true, "write_rule": true, @@ -2397,13 +2665,13 @@ describe("DB owner config", () => { "write_rule": false, "write_function": false }, - "aaaa": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_owner": false, "write_rule": false, "write_function": false }, - "known_user": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": false, "write_owner": true, "write_rule": true, @@ -2423,13 +2691,13 @@ describe("DB owner config", () => { "write_rule": false, "write_function": false }, - "aaaa": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": false, "write_owner": true, "write_rule": false, "write_function": false }, - "known_user": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_owner": false, "write_rule": true, @@ -2449,13 +2717,13 @@ describe("DB owner config", () => { "write_rule": true, "write_function": true }, - "aaaa": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": false, "write_owner": false, "write_rule": true, "write_function": true }, - "known_user": { + "0x08Aed7AF9354435c38d52143EE50ac839D20696b": { "branch_owner": true, "write_owner": true, "write_rule": false, @@ -2474,186 +2742,210 @@ describe("DB owner config", () => { // Known user it("branch_owner permission for known user with mixed config", () => { expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/true/branch', 'branch_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/true/branch', 'branch_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/false/true/true/branch', 'branch_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/false/true/true/branch', 'branch_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/false/true/branch', 'branch_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/true/false/true/branch', 'branch_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/false/branch', 'branch_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/false/branch', 'branch_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) }) it("write_owner permission for known user with mixed config", () => { expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/true', 'write_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/true', 'write_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/false/true/true', 'write_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/false/true/true', 'write_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/false/true', 'write_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/true/false/true', 'write_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/false', 'write_owner', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/false', 'write_owner', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) }) it("write_rule permission for known user with mixed config", () => { expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/true', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/true', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/false/true/true', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/false/true/true', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/false/true', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/true/false/true', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/false', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/false', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false) }) it("write_rule permission on deeper path for known user with mixed config", () => { expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/true/deeper_path', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/true/deeper_path', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/false/true/true/deeper_path', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/false/true/true/deeper_path', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/false/true/deeper_path', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/true/false/true/deeper_path', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/false/deeper_path', 'write_rule', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/false/deeper_path', 'write_rule', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false) }) it("write_function permission for known user with mixed config", () => { expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/true', 'write_function', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/true', 'write_function', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/false/true/true', 'write_function', { addr: 'known_user' })) + '/test/test_owner/mixed/false/true/true', 'write_function', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/false/true', 'write_function', { addr: 'known_user' })) + '/test/test_owner/mixed/true/false/true', 'write_function', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/false', 'write_function', { addr: 'known_user' })) + '/test/test_owner/mixed/true/true/false', 'write_function', + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false) }) - it("write_Function permission on deeper path for known user with mixed config", () => { + it("write_function permission on deeper path for known user with mixed config", () => { expect(node.db.evalOwner( '/test/test_owner/mixed/true/true/true/deeper_path', 'write_function', - { addr: 'known_user' })) + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( '/test/test_owner/mixed/false/true/true/deeper_path', 'write_function', - { addr: 'known_user' })) + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( '/test/test_owner/mixed/true/false/true/deeper_path', 'write_function', - { addr: 'known_user' })) + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(true) expect(node.db.evalOwner( '/test/test_owner/mixed/true/true/false/deeper_path', 'write_function', - { addr: 'known_user' })) + { addr: '0x08Aed7AF9354435c38d52143EE50ac839D20696b' })) .to.equal(false) }) // Unknown user it("branch_owner permission for unknown user with mixed config", () => { expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/true/branch', 'branch_owner', { addr: 'unknown_user' })) + '/test/test_owner/mixed/true/true/true/branch', 'branch_owner', + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner( - '/test/test_owner/mixed/false/true/true/branch', 'branch_owner', { addr: 'unknown_user' })) + '/test/test_owner/mixed/false/true/true/branch', 'branch_owner', + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(true) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/false/true/branch', 'branch_owner', { addr: 'unknown_user' })) + '/test/test_owner/mixed/true/false/true/branch', 'branch_owner', + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner( - '/test/test_owner/mixed/true/true/false/branch', 'branch_owner', { addr: 'unknown_user' })) + '/test/test_owner/mixed/true/true/false/branch', 'branch_owner', + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) }) it("write_owner permission for unknown user with mixed config", () => { expect(node.db.evalOwner('/test/test_owner/mixed/true/true/true', 'write_owner', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/false/true/true', 'write_owner', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/false/true', 'write_owner', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(true) expect(node.db.evalOwner('/test/test_owner/mixed/true/true/false', 'write_owner', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) }) it("write_rule permission for unknown user with mixed config", () => { expect(node.db.evalOwner('/test/test_owner/mixed/true/true/true', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/false/true/true', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/false/true', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/true/false', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(true) }) it("write_rule permission on deeper path for unknown user with mixed config", () => { expect(node.db.evalOwner('/test/test_owner/mixed/true/true/true/deeper_path', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/false/true/true/deeper_path', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/false/true/deeper_path', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/true/false/deeper_path', 'write_rule', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(true) }) it("write_function permission for unknown user with mixed config", () => { expect(node.db.evalOwner('/test/test_owner/mixed/true/true/true', 'write_function', - { addr: 'unknown_user' })).to.equal(false) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })).to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/false/true/true', 'write_function', - { addr: 'unknown_user' })).to.equal(false) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })).to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/false/true', 'write_function', - { addr: 'unknown_user' })).to.equal(false) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })).to.equal(false) expect(node.db.evalOwner('/test/test_owner/mixed/true/true/false', 'write_function', - { addr: 'unknown_user' })).to.equal(true) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })).to.equal(true) }) it("write_function permission on deeper path for unknown user with mixed config", () => { expect(node.db.evalOwner( '/test/test_owner/mixed/true/true/true/deeper_path', 'write_function', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner( '/test/test_owner/mixed/false/true/true/deeper_path', 'write_function', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner( '/test/test_owner/mixed/true/false/true/deeper_path', 'write_function', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(false) expect(node.db.evalOwner( '/test/test_owner/mixed/true/true/false/deeper_path', 'write_function', - { addr: 'unknown_user' })) + { addr: '0x07A43138CC760C85A5B1F115aa60eADEaa0bf417' })) .to.equal(true) }) }) @@ -2702,11 +2994,21 @@ describe("DB sharding config", () => { "path": { "to": { ".function": { - "fid": "some function config", + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } }, "deeper": { ".function": { - "fid_deeper": "some deeper function config", + "fid_deeper": { + "function_type": "REST", + "function_id": "fid_deeper", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } } } } @@ -2721,7 +3023,7 @@ describe("DB sharding config", () => { "path": { ".write": "false", "to": { - ".write": "auth.addr === 'known_user'", + ".write": "auth.addr === '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'", "deeper": { ".write": "some deeper rule config", } @@ -2744,7 +3046,7 @@ describe("DB sharding config", () => { "write_owner": false, "write_rule": false, }, - "known_user": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": true, "write_owner": true, @@ -2802,7 +3104,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = false", () => { expect(node.db.setValue( - "test/test_sharding/some/path/to/value", newValue, { addr: 'known_user' }, + "test/test_sharding/some/path/to/value", newValue, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}).code) .to.equal(0); expect(node.db.getValue("test/test_sharding/some/path/to/value")).to.equal(newValue); @@ -2810,7 +3112,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true", () => { expect(node.db.setValue( - "apps/afan/test/test_sharding/some/path/to/value", newValue, { addr: 'known_user' }, + "apps/afan/test/test_sharding/some/path/to/value", newValue, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); expect(node.db.getValue("test/test_sharding/some/path/to/value")).to.equal(newValue); @@ -2818,7 +3120,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and non-existing path", () => { expect(node.db.setValue( - "some/non-existing/path", newValue, { addr: 'known_user' }, + "some/non-existing/path", newValue, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); }) @@ -2833,7 +3135,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and non-writable path with sharding", () => { expect(node.db.setValue( - "apps/afan/test/test_sharding/shards/enabled_shard/path", 20, 'known_user', null, null, + "apps/afan/test/test_sharding/shards/enabled_shard/path", 20, '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1', null, null, true).code) .to.equal(0); expect(node.db.getValue("apps/afan/test/test_sharding/shards/enabled_shard/path", true)) @@ -2848,7 +3150,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and writable path with sharding", () => { expect(node.db.setValue( - "apps/afan/test/test_sharding/shards/disabled_shard/path", 20, { addr: 'known_user' }, + "apps/afan/test/test_sharding/shards/disabled_shard/path", 20, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); expect(node.db.getValue("apps/afan/test/test_sharding/shards/disabled_shard/path", true)) @@ -2857,7 +3159,7 @@ describe("DB sharding config", () => { it("incValue with isGlobal = false", () => { expect(node.db.incValue( - "test/test_sharding/some/path/to/number", incDelta, { addr: 'known_user' }, + "test/test_sharding/some/path/to/number", incDelta, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}).code) .to.equal(0); expect(node.db.getValue("test/test_sharding/some/path/to/number")).to.equal(10 + incDelta); @@ -2865,7 +3167,7 @@ describe("DB sharding config", () => { it("incValue with isGlobal = true", () => { expect(node.db.incValue( - "apps/afan/test/test_sharding/some/path/to/number", incDelta, { addr: 'known_user' }, + "apps/afan/test/test_sharding/some/path/to/number", incDelta, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); expect(node.db.getValue("test/test_sharding/some/path/to/number")).to.equal(10 + incDelta); @@ -2873,7 +3175,7 @@ describe("DB sharding config", () => { it("incValue with isGlobal = true and non-existing path", () => { expect(node.db.incValue( - "some/non-existing/path", incDelta, { addr: 'known_user' }, null, null, true).code) + "some/non-existing/path", incDelta, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, null, true).code) .to.equal(0); }) @@ -2887,7 +3189,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and non-writable path with sharding", () => { expect(node.db.incValue( - "apps/afan/test/test_sharding/shards/enabled_shard/path", 5, { addr: 'known_user' }, + "apps/afan/test/test_sharding/shards/enabled_shard/path", 5, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, null, true).code) .to.equal(0); expect(node.db.getValue("apps/afan/test/test_sharding/shards/enabled_shard/path", true)) @@ -2902,7 +3204,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and writable path with sharding", () => { expect(node.db.incValue( - "apps/afan/test/test_sharding/shards/disabled_shard/path", 5, { addr: 'known_user' }, + "apps/afan/test/test_sharding/shards/disabled_shard/path", 5, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); expect(node.db.getValue("apps/afan/test/test_sharding/shards/disabled_shard/path", true)) @@ -2911,7 +3213,7 @@ describe("DB sharding config", () => { it("decValue with isGlobal = false", () => { expect(node.db.decValue( - "test/test_sharding/some/path/to/number", decDelta, { addr: 'known_user' }, + "test/test_sharding/some/path/to/number", decDelta, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}).code) .to.equal(0); expect(node.db.getValue("test/test_sharding/some/path/to/number")).to.equal(10 - decDelta); @@ -2919,7 +3221,7 @@ describe("DB sharding config", () => { it("decValue with isGlobal = true", () => { expect(node.db.decValue( - "apps/afan/test/test_sharding/some/path/to/number", decDelta, { addr: 'known_user' }, + "apps/afan/test/test_sharding/some/path/to/number", decDelta, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); expect(node.db.getValue("test/test_sharding/some/path/to/number")).to.equal(10 - decDelta); @@ -2927,7 +3229,7 @@ describe("DB sharding config", () => { it("decValue with isGlobal = true and non-existing path", () => { expect(node.db.decValue( - "some/non-existing/path", decDelta, { addr: 'known_user' }, null, null, true).code) + "some/non-existing/path", decDelta, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, null, true).code) .to.equal(0); }) @@ -2941,7 +3243,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and non-writable path with sharding", () => { expect(node.db.decValue( - "apps/afan/test/test_sharding/shards/enabled_shard/path", 5, { addr: 'known_user' }, + "apps/afan/test/test_sharding/shards/enabled_shard/path", 5, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, null, true).code) .to.equal(0); expect(node.db.getValue( @@ -2958,7 +3260,7 @@ describe("DB sharding config", () => { it("setValue with isGlobal = true and writable path with sharding", () => { expect(node.db.decValue( - "apps/afan/test/test_sharding/shards/disabled_shard/path", 5, { addr: 'known_user' }, + "apps/afan/test/test_sharding/shards/disabled_shard/path", 5, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, null, { extra: { executed_at: 1234567890000 }}, true).code) .to.equal(0); expect(node.db.getValue("apps/afan/test/test_sharding/shards/disabled_shard/path", true)) @@ -2970,26 +3272,51 @@ describe("DB sharding config", () => { describe("Function operations", () => { const func = { ".function": { - "fid": "some function config", + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, }, "deeper": { ".function": { - "fid_deeper": "some deeper function config" + "fid_deeper": { + "function_type": "REST", + "function_id": "fid_deeper", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } } }; const funcChange = { ".function": { - "fid": "another function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger2", // Listener 2 + "service_name": "https://ainetwork.ai", + }, } }; const newFunc = { ".function": { - "fid": "another function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger2", // Listener 2 + "service_name": "https://ainetwork.ai", + }, }, "deeper": { ".function": { - "fid_deeper": "some deeper function config" + "fid_deeper": { + "function_type": "REST", + "function_id": "fid_deeper", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, } } }; @@ -3011,14 +3338,14 @@ describe("DB sharding config", () => { it("setFunction with isGlobal = false", () => { expect(node.db.setFunction( - "test/test_sharding/some/path/to", funcChange, { addr: 'known_user' }).code) + "test/test_sharding/some/path/to", funcChange, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }).code) .to.equal(0); assert.deepEqual(node.db.getFunction("test/test_sharding/some/path/to"), newFunc); }) it("setFunction with isGlobal = true", () => { expect(node.db.setFunction( - "apps/afan/test/test_sharding/some/path/to", funcChange, { addr: 'known_user' }, + "apps/afan/test/test_sharding/some/path/to", funcChange, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, true).code) .to.equal(0); assert.deepEqual( @@ -3027,7 +3354,7 @@ describe("DB sharding config", () => { it("setFunction with isGlobal = true and non-existing path", () => { expect(node.db.setFunction( - "some/non-existing/path", funcChange, { addr: 'known_user' }, true).code) + "some/non-existing/path", funcChange, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, true).code) .to.equal(0); }) @@ -3040,14 +3367,24 @@ describe("DB sharding config", () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } }, "path": "/test/test_sharding/some/path/to" }, "subtree_configs": [ { "config": { - "fid_deeper": "some deeper function config" + "fid_deeper": { + "function_type": "REST", + "function_id": "fid_deeper", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, }, "path": "/deeper", } @@ -3064,14 +3401,24 @@ describe("DB sharding config", () => { }, "matched_config": { "config": { - "fid": "some function config" + "fid": { + "function_type": "REST", + "function_id": "fid", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } }, "path": "/apps/afan/test/test_sharding/some/path/to" }, "subtree_configs": [ { "config": { - "fid_deeper": "some deeper function config" + "fid_deeper": { + "function_type": "REST", + "function_id": "fid_deeper", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, }, "path": "/deeper", } @@ -3086,7 +3433,7 @@ describe("DB sharding config", () => { describe("Rule operations", () => { const rule = { - ".write": "auth.addr === 'known_user'", + ".write": "auth.addr === '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'", "deeper": { ".write": "some deeper rule config" } @@ -3111,21 +3458,21 @@ describe("DB sharding config", () => { it("setRule with isGlobal = false", () => { expect(node.db.setRule( - "test/test_sharding/some/path/to", newRule, { addr: 'known_user' }).code) + "test/test_sharding/some/path/to", newRule, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }).code) .to.equal(0); assert.deepEqual(node.db.getRule("test/test_sharding/some/path/to"), newRule); }) it("setRule with isGlobal = true", () => { expect(node.db.setRule( - "apps/afan/test/test_sharding/some/path/to", newRule, { addr: 'known_user' }, true).code) + "apps/afan/test/test_sharding/some/path/to", newRule, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, true).code) .to.equal(0); assert.deepEqual( node.db.getRule("apps/afan/test/test_sharding/some/path/to", true), newRule); }) it("setRule with isGlobal = true and non-existing path", () => { - expect(node.db.setRule("some/non-existing/path", newRule, { addr: 'known_user' }, true).code) + expect(node.db.setRule("some/non-existing/path", newRule, { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, true).code) .to.equal(0); }) @@ -3137,7 +3484,7 @@ describe("DB sharding config", () => { "path_vars": {}, }, "matched_config": { - "config": "auth.addr === 'known_user'", + "config": "auth.addr === '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'", "path": "/test/test_sharding/some/path/to" }, "subtree_configs": [ @@ -3157,7 +3504,7 @@ describe("DB sharding config", () => { "path_vars": {}, }, "matched_config": { - "config": "auth.addr === 'known_user'", + "config": "auth.addr === '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'", "path": "/apps/afan/test/test_sharding/some/path/to" }, "subtree_configs": [ @@ -3174,20 +3521,20 @@ describe("DB sharding config", () => { }) it("evalRule with isGlobal = false", () => { - expect(node.db.evalRule("/test/test_sharding/some/path/to", newValue, { addr: "known_user" })) + expect(node.db.evalRule("/test/test_sharding/some/path/to", newValue, { addr: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1" })) .to.equal(true); }) it("evalRule with isGlobal = true", () => { expect(node.db.evalRule( - "/apps/afan/test/test_sharding/some/path/to", newValue, { addr: "known_user" }, + "/apps/afan/test/test_sharding/some/path/to", newValue, { addr: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1" }, null, true)) .to.equal(true); }) it("evalRule with isGlobal = true and non-existing path", () => { expect(node.db.evalRule( - "/some/non-existing/path", newValue, { addr: "known_user" }, null, true)) + "/some/non-existing/path", newValue, { addr: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1" }, null, true)) .to.equal(null); }) }) @@ -3202,7 +3549,7 @@ describe("DB sharding config", () => { "write_owner": false, "write_rule": false, }, - "known_user": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": true, "write_owner": true, @@ -3241,22 +3588,25 @@ describe("DB sharding config", () => { it("setOwner with isGlobal = false", () => { expect(node.db.setOwner( - "test/test_sharding/some/path/to", newOwner, { addr: 'known_user' }).code) + "test/test_sharding/some/path/to", newOwner, + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }).code) .to.equal(0); assert.deepEqual(node.db.getOwner("test/test_sharding/some/path/to"), newOwner); }) it("setOwner with isGlobal = true", () => { expect(node.db.setOwner( - "apps/afan/test/test_sharding/some/path/to", newOwner, { addr: 'known_user' }, true).code) + "apps/afan/test/test_sharding/some/path/to", newOwner, + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, true).code) .to.equal(0); assert.deepEqual( node.db.getOwner("apps/afan/test/test_sharding/some/path/to", true), newOwner); }) it("setOwner with isGlobal = true and non-existing path", () => { - expect(node.db.setOwner("some/non-existing/path", newOwner, { addr: 'known_user' }, - true).code).to.equal(0); + expect(node.db.setOwner( + "some/non-existing/path", newOwner, + { addr: '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1' }, true).code).to.equal(0); }) it("matchOwner with isGlobal = false", () => { @@ -3273,7 +3623,7 @@ describe("DB sharding config", () => { "write_owner": false, "write_rule": false, }, - "known_user": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": true, "write_owner": true, @@ -3300,7 +3650,7 @@ describe("DB sharding config", () => { "write_owner": false, "write_rule": false, }, - "known_user": { + "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1": { "branch_owner": true, "write_function": true, "write_owner": true, @@ -3319,20 +3669,20 @@ describe("DB sharding config", () => { it("evalOwner with isGlobal = false", () => { expect(node.db.evalOwner( - "/test/test_sharding/some/path/to", "write_rule", { addr: "known_user" })) - .to.equal(true); + "/test/test_sharding/some/path/to", "write_rule", + { addr: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1" })).to.equal(true); }) it("evalOwner with isGlobal = true", () => { expect(node.db.evalOwner( - "/apps/afan/test/test_sharding/some/path/to", "write_rule", { addr: "known_user" }, true)) - .to.equal(true); + "/apps/afan/test/test_sharding/some/path/to", "write_rule", + { addr: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1" }, true)).to.equal(true); }) it("evalOwner with isGlobal = true and non-existing path", () => { expect(node.db.evalOwner( - "/some/non-existing/path", "write_rule", { addr: "known_user" }, true)) - .to.equal(null); + "/some/non-existing/path", "write_rule", + { addr: "0x09A0d53FDf1c36A131938eb379b98910e55EEfe1" }, true)).to.equal(null); }) }) }) diff --git a/unittest/functions.test.js b/unittest/functions.test.js index 6acdf06ef..d2ecb96c6 100644 --- a/unittest/functions.test.js +++ b/unittest/functions.test.js @@ -542,133 +542,4 @@ describe("Functions", () => { assert.deepEqual(Functions.convertPathVars2Params(pathVars), params); }) }); - - describe("applyFunctionChange()", () => { - const curFunction = { - ".function": { - "0x111": { - "function_type": "NATIVE", - "function_id": "0x111" - }, - "0x222": { - "function_type": "NATIVE", - "function_id": "0x222" - }, - "0x333": { - "function_type": "NATIVE", - "function_id": "0x333" - } - } - }; - - it("add / delete / modify non-existing function", () => { - assert.deepEqual(Functions.applyFunctionChange(null, { - ".function": { // function - "0x111": null, - "0x222": { - "function_type": "REST", - "function_id": "0x222" - }, - }, - "deeper": { - ".function": { // deeper function - "0x999": { - "function_type": "REST", - "function_id": "0x999" - } - } - } - }), { // the same as the given function change. - ".function": { - "0x111": null, - "0x222": { - "function_type": "REST", - "function_id": "0x222" - }, - }, - "deeper": { - ".function": { - "0x999": { - "function_type": "REST", - "function_id": "0x999" - } - } - } - }); - }); - - it("add / delete / modify existing function", () => { - assert.deepEqual(Functions.applyFunctionChange(curFunction, { - ".function": { - "0x111": null, // delete - "0x222": { // modify - "function_type": "REST", - "function_id": "0x222" - }, - "0x444": { // add - "function_type": "REST", - "function_id": "0x444" - } - } - }), { - ".function": { - "0x222": { // modified - "function_type": "REST", - "function_id": "0x222" - }, - "0x333": { // untouched - "function_type": "NATIVE", - "function_id": "0x333" - }, - "0x444": { // added - "function_type": "REST", - "function_id": "0x444" - } - } - }); - }); - - it("add / delete / modify existing function with deeper function", () => { - assert.deepEqual(Functions.applyFunctionChange(curFunction, { - ".function": { - "0x111": null, // delete - "0x222": { // modify - "function_type": "REST", - "function_id": "0x222" - }, - "0x444": { // add - "function_type": "REST", - "function_id": "0x444" - } - }, - "deeper": { - ".function": { // deeper function - "0x999": { - "function_type": "REST", - "function_id": "0x999" - } - } - } - }), { - ".function": { // deeper function has no effect - "0x222": { // modified - "function_type": "REST", - "function_id": "0x222" - }, - "0x333": { // untouched - "function_type": "NATIVE", - "function_id": "0x333" - }, - "0x444": { // added - "function_type": "REST", - "function_id": "0x444" - } - } - }); - }); - - it("with null function change", () => { - assert.deepEqual(Functions.applyFunctionChange(curFunction, null), null); - }); - }); }) diff --git a/unittest/p2p-util.test.js b/unittest/p2p-util.test.js new file mode 100644 index 000000000..6a99a621d --- /dev/null +++ b/unittest/p2p-util.test.js @@ -0,0 +1,321 @@ +const Websocket = require('ws'); +const util = require('../p2p/util'); +const chai = require('chai'); +const expect = chai.expect; +const assert = chai.assert; +const { + CURRENT_PROTOCOL_VERSION, + DATA_PROTOCOL_VERSION +} = require('../common/constants'); + +describe("P2P Util", () => { + const mockAddress = '0x012345678abcdef'; + let webServer; + let mockSocket; + let comparingSocket; + let connectionObj; + before(() => { + webServer = new Websocket.Server({ port: 5000 }); + mockSocket = new Websocket('ws://127.0.0.1:5000'); + comparingSocket = new Websocket('ws://127.0.0.1:5000'); + connectionObj = { + [mockAddress]: { + socket: mockSocket + } + }; + }); + + after(() => { + if (mockSocket.readyState !== 3) { + mockSocket.close(); + } + comparingSocket.close(); + webServer.close(); + }); + + describe("getAddressFromSocket", () => { + it("finds nothing", () => { + expect(util.getAddressFromSocket(connectionObj, '0xdeadbeef')).to.equal(undefined); + }); + + it("finds the socket successfully", () => { + expect(util.getAddressFromSocket(connectionObj, mockSocket)).to.equal(mockAddress); + }); + }); + + describe("removeSocketConnectionIfExists", () => { + it("removes nothing", () => { + const clonedConnectionObj = JSON.parse(JSON.stringify(connectionObj)); + util.removeSocketConnectionIfExists(clonedConnectionObj, '0xdeadbeef'); + assert.deepEqual(clonedConnectionObj, clonedConnectionObj); + }); + + it("removes the socket successfully", () => { + const clonedConnectionObj = JSON.parse(JSON.stringify(connectionObj)); + util.removeSocketConnectionIfExists(clonedConnectionObj, mockAddress); + expect(clonedConnectionObj[mockAddress]).to.equal(undefined); + }); + }); + + describe("closeSocketSafe", () => { + it("closes nothing", () => { + util.closeSocketSafe(connectionObj, comparingSocket); + assert.deepEqual(connectionObj, connectionObj); + }); + + it("closes the socket successfully", () => { + util.closeSocketSafe(connectionObj, mockSocket); + expect(connectionObj[mockAddress]).to.equal(undefined); + }); + }); + + describe("encapsulateMessage", () => { + const mockType = 'mockType'; + const wrongType1 = 1; + const wrongType2 = undefined; + const wrongType3 = ['a', 1]; + const wrongType4 = { foo: 'bar' }; + const wrongType5 = true; + + const mockDataObj = { test: 'encapsulation' }; + const wrongData1 = -5; + const wrongData2 = null; + const wrongData3 = 'wrongData'; + const wrongData4 = []; + const wrongData5 = false; + + it("cannot encapsulate messages with wrong types", () => { + expect(util.encapsulateMessage(wrongType1, mockDataObj)).to.equal(null); + expect(util.encapsulateMessage(wrongType2, mockDataObj)).to.equal(null); + expect(util.encapsulateMessage(wrongType3, mockDataObj)).to.equal(null); + expect(util.encapsulateMessage(wrongType4, mockDataObj)).to.equal(null); + expect(util.encapsulateMessage(wrongType5, mockDataObj)).to.equal(null); + }); + + it("cannot encapsulate messages with wrong data", () => { + expect(util.encapsulateMessage(mockType, wrongData1)).to.equal(null); + expect(util.encapsulateMessage(mockType, wrongData2)).to.equal(null); + expect(util.encapsulateMessage(mockType, wrongData3)).to.equal(null); + expect(util.encapsulateMessage(mockType, wrongData4)).to.equal(null); + expect(util.encapsulateMessage(mockType, wrongData5)).to.equal(null); + }); + + it("encapsulates the message successfully", () => { + const encapsulatedMessage = util.encapsulateMessage(mockType, mockDataObj); + assert.deepEqual(encapsulatedMessage, { + type: mockType, + data: mockDataObj, + protoVer: CURRENT_PROTOCOL_VERSION, + dataProtoVer: DATA_PROTOCOL_VERSION, + timestamp: encapsulatedMessage.timestamp + }); + }); + }); + + describe("checkTimestamp", () => { + it("fails when getting wrong timestamp values", () => { + const wrongTimestamp1 = 'timestamp'; + const wrongTimestamp2 = undefined; + const wrongTimestamp3 = ['a', 1]; + const wrongTimestamp4 = { foo: 'bar' }; + const wrongTimestamp5 = true; + expect(util.checkTimestamp(wrongTimestamp1)).to.equal(false); + expect(util.checkTimestamp(wrongTimestamp2)).to.equal(false); + expect(util.checkTimestamp(wrongTimestamp3)).to.equal(false); + expect(util.checkTimestamp(wrongTimestamp4)).to.equal(false); + expect(util.checkTimestamp(wrongTimestamp5)).to.equal(false); + }); + + it("passes the timestamp check", () => { + const timestamp = Date.now(); + expect(util.checkTimestamp(timestamp)).to.equal(true); + }); + }); + + describe("signMessage", () => { + const mockPrivateKey = '6204d4e083dd09c7b084e5923c5d664d2e1f3ce8440f90a773638f30c61d9c40'; + it("returns null when no object type but the other types", () => { + const wrongInput1 = 'string'; + const wrongInput2 = undefined; + const wrongInput3 = ['a', 1]; + const wrongInput4 = -1000; + const wrongInput5 = true; + expect(util.signMessage(wrongInput1, mockPrivateKey)).to.equal(null); + expect(util.signMessage(wrongInput2, mockPrivateKey)).to.equal(null); + expect(util.signMessage(wrongInput3, mockPrivateKey)).to.equal(null); + expect(util.signMessage(wrongInput4, mockPrivateKey)).to.equal(null); + expect(util.signMessage(wrongInput5, mockPrivateKey)).to.equal(null); + }); + + it("returns null when no valid private key but other values", () => { + const wrongPrivateKey1 = 'string'; + const wrongPrivateKey2 = null; + const wrongPrivateKey3 = ['a', 1]; + const wrongPrivateKey4 = 1000000000; + const wrongPrivateKey5 = false; + const wrongPrivateKey6 = { object: 123 }; + expect(util.signMessage({ correct: 'body' }, wrongPrivateKey1)).to.equal(null); + expect(util.signMessage({ correct: 'body' }, wrongPrivateKey2)).to.equal(null); + expect(util.signMessage({ correct: 'body' }, wrongPrivateKey3)).to.equal(null); + expect(util.signMessage({ correct: 'body' }, wrongPrivateKey4)).to.equal(null); + expect(util.signMessage({ correct: 'body' }, wrongPrivateKey5)).to.equal(null); + expect(util.signMessage({ correct: 'body' }, wrongPrivateKey6)).to.equal(null); + }); + + it("returns correct the correct digital signature", () => { + const body = { + foo: 'bar', + test: { + 1: 2, + success: [1, 2, 3] + } + }; + expect(util.signMessage(body, mockPrivateKey)).to.equal('0x4455e15b20f5125fff5196081b02ce827a2eaa931a74e6f1ecdcacddb1a91469319c7cdccfa492a96df6cc0d06eace1c4023b2067b6465dc5858d602f72e19dc2f09ea7cd575ff7a54c77dc6f0de33256e309e5e0c9a0aef66082c670e92775c1c'); + }); + }); + + describe("getAddressFromMessage", () => { + const mockPrivateKey = '6204d4e083dd09c7b084e5923c5d664d2e1f3ce8440f90a773638f30c61d9c40'; + const body = { + foo: 'bar', + test: { + 1: 2, + success: [1, 2, 3] + } + }; + const signature = util.signMessage(body, mockPrivateKey); + it("returns null with wrong messages", () => { + const wrongMessage1 = { + data: { + signature: signature + } + }; + const wrongMessage2 = { + data: { + body: 'string', + signature: signature + } + }; + const wrongMessage3 = { + data: { + body: null, + signature: signature + } + }; + const wrongMessage4 = { + data: { + body: ['a', 1], + signature: signature + } + }; + const wrongMessage5 = { + data: { + body: 123123, + signature: signature + } + }; + const wrongMessage6 = { + data: { + body: false, + signature: signature + } + }; + const wrongMessage7 = { + data: { + body: body + } + }; + expect(util.getAddressFromMessage(wrongMessage1)).to.equal(null); + expect(util.getAddressFromMessage(wrongMessage2)).to.equal(null); + expect(util.getAddressFromMessage(wrongMessage3)).to.equal(null); + expect(util.getAddressFromMessage(wrongMessage4)).to.equal(null); + expect(util.getAddressFromMessage(wrongMessage5)).to.equal(null); + expect(util.getAddressFromMessage(wrongMessage6)).to.equal(null); + expect(util.getAddressFromMessage(wrongMessage7)).to.equal(null); + }); + + it("gets correct address", () => { + const mockMessage = { + type: 'test', + data: { + body: body, + signature: signature + } + }; + expect(util.getAddressFromMessage(mockMessage)).to.equal('0xBBB2219cD5eACc54Ce95deF7a67dDe71C8241891'); + }); + }); + + describe("verifySignedMessage", () => { + const mockPrivateKey = '6204d4e083dd09c7b084e5923c5d664d2e1f3ce8440f90a773638f30c61d9c40'; + const body = { + foo: 'bar', + test: { + 1: 2, + success: [1, 2, 3] + } + }; + const signature = util.signMessage(body, mockPrivateKey); + it("returns null with wrong messages", () => { + const wrongMessage1 = { + data: { + signature: signature + } + }; + const wrongMessage2 = { + data: { + body: 'string', + signature: signature + } + }; + const wrongMessage3 = { + data: { + body: null, + signature: signature + } + }; + const wrongMessage4 = { + data: { + body: ['a', 1], + signature: signature + } + }; + const wrongMessage5 = { + data: { + body: 123123, + signature: signature + } + }; + const wrongMessage6 = { + data: { + body: false, + signature: signature + } + }; + const wrongMessage7 = { + data: { + body: body + } + }; + expect(util.verifySignedMessage(wrongMessage1)).to.equal(null); + expect(util.verifySignedMessage(wrongMessage2)).to.equal(null); + expect(util.verifySignedMessage(wrongMessage3)).to.equal(null); + expect(util.verifySignedMessage(wrongMessage4)).to.equal(null); + expect(util.verifySignedMessage(wrongMessage5)).to.equal(null); + expect(util.verifySignedMessage(wrongMessage6)).to.equal(null); + expect(util.verifySignedMessage(wrongMessage7)).to.equal(null); + }); + + it("verifies signature correctly", () => { + const mockMessage = { + type: 'test', + data: { + body: body, + signature: signature + } + }; + const address = util.getAddressFromMessage(mockMessage); + expect(util.verifySignedMessage(mockMessage, address)).to.equal(true); + }); + }); +}); diff --git a/unittest/p2p.test.js b/unittest/p2p.test.js new file mode 100644 index 000000000..97f46d085 --- /dev/null +++ b/unittest/p2p.test.js @@ -0,0 +1,329 @@ +const chai = require('chai'); +const url = require('url'); + +const BlockchainNode = require('../node'); +const VersionUtil = require('../common/version-util'); +const P2pClient = require('../p2p'); +const { + PORT, + P2P_PORT, + DEFAULT_MAX_OUTBOUND, + DEFAULT_MAX_INBOUND, + CONSENSUS_PROTOCOL_VERSION, + CURRENT_PROTOCOL_VERSION, + PROTOCOL_VERSION_MAP, + DATA_PROTOCOL_VERSION +} = require('../common/constants'); + +const expect = chai.expect; +const assert = chai.assert; + +const { min, max } = VersionUtil.matchVersions(PROTOCOL_VERSION_MAP, CURRENT_PROTOCOL_VERSION); +const minProtocolVersion = min === undefined ? CURRENT_PROTOCOL_VERSION : min; +const maxProtocolVersion = max; + +const node = new BlockchainNode(); + +describe("p2p", () => { + let p2pClient; + let p2pServer; + before(() => { + p2pClient = new P2pClient(node, minProtocolVersion, maxProtocolVersion); + p2pServer = p2pClient.server; + p2pServer.listen(); + }); + + after(() => { + p2pClient.stop(); + }); + + describe("server status", () => { + describe("getIpAddress", () => { + it("gets ip address", async () => { + const actual = await p2pServer.getIpAddress(); + const ipAddressRegex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + expect(ipAddressRegex.test(actual)).to.be.true; + }); + }); + + describe("setUpIpAddresses", () => { + it("sets ip address", async () => { + const ipAddressRegex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + expect(ipAddressRegex.test(p2pServer.node.ipAddrInternal)).to.be.true; + expect(ipAddressRegex.test(p2pServer.node.ipAddrExternal)).to.be.true; + }); + }); + + describe("getNodeAddress", () => { + it("gets node address", () => { + expect(p2pServer.getNodeAddress()).to.equal(p2pServer.node.account.address); + }); + }); + + describe("getNodePrivateKey", () => { + it("gets node private key address", () => { + expect(p2pServer.getNodePrivateKey()).to.equal(p2pServer.node.account.private_key); + }); + }); + + describe("getExternalIp", () => { + it("gets external IP address", () => { + expect(p2pServer.getExternalIp()).to.equal(p2pServer.node.ipAddrExternal); + }); + }); + + describe("getProtocolInfo", () => { + it("gets external IP address", () => { + const actual = { + COMPATIBLE_MAX_PROTOCOL_VERSION: maxProtocolVersion, + COMPATIBLE_MIN_PROTOCOL_VERSION: minProtocolVersion, + CONSENSUS_PROTOCOL_VERSION: CONSENSUS_PROTOCOL_VERSION, + CURRENT_PROTOCOL_VERSION: CURRENT_PROTOCOL_VERSION, + DATA_PROTOCOL_VERSION: DATA_PROTOCOL_VERSION + }; + assert.deepEqual(actual, p2pServer.getProtocolInfo()); + }); + }); + + describe("getStateVersionStatus", () => { + it("gets initial state version status", () => { + const actual = { + numVersions: 2, + versionList: ['EMPTY', 'NODE:-1'], + finalVersion: null, + }; + assert.deepEqual(actual, p2pServer.getStateVersionStatus()); + }); + }); + + describe("getConsensusStatus", () => { + it("gets initial consensus status", () => { + const actual = { + health: false, + state: 'STARTING', + stateNumeric: 0, + epoch: 1, + longestNotarizedChainTipsSize: 0 + }; + assert.deepEqual(actual, p2pServer.getConsensusStatus()); + }); + }); + + describe("getBlockStatus", () => { + it("gets initial block status", () => { + const actual = { number: -1, epoch: -1, timestamp: -1 }; + const expected = p2pServer.getBlockStatus(); + delete expected.elapsedTimeMs; + assert.deepEqual(actual, expected); + }); + }); + + describe("getNodeStatus", () => { + it("gets initial node status", () => { + const actual = { + address: p2pServer.getNodeAddress(), + state: 'STARTING', + stateNumeric: 0, + nonce: null, + dbStatus: { + stateInfo: { tree_height: null, tree_size: null }, + stateProof: { '.proof_hash': null } + }, + stateVersionStatus: { + numVersions: 2, + versionList: [ 'EMPTY', 'NODE:-1' ], + finalVersion: null + } + }; + assert.deepEqual(actual, p2pServer.getNodeStatus()); + }); + }); + + describe("getDiskUsage", () => { + it("gets initial disk usage (it depends on the machine)", () => { + const actual = { + available: 113007648768, + free: 235339354112, + total: 250685575168, + used: 15346221056 + }; + assert.deepEqual(Object.keys(actual), Object.keys(p2pServer.getDiskUsage())); + }); + }); + + describe("getCpuUsage", () => { + it("gets initial cpu usage (it depends on the machine)", () => { + const actual = { free: 3174023060, usage: 313233920, total: 3487256980 }; + assert.deepEqual(Object.keys(actual), Object.keys(p2pServer.getCpuUsage())); + }); + }); + + describe("getMemoryUsage", () => { + it("gets initial memory usage (it depends on the machine)", () => { + const actual = { + os: { free: 211546112, usage: 16968323072, total: 17179869184 }, + heap: { + rss: 80953344, + heapTotal: 56041472, + heapUsed: 27042840, + external: 2038194 + }, + heapStats: { + total_heap_size: 56041472, + total_heap_size_executable: 835584, + total_physical_size: 37052632, + total_available_size: 2170221688, + used_heap_size: 27042960, + heap_size_limit: 2197815296, + malloced_memory: 155800, + peak_malloced_memory: 6718576, + does_zap_garbage: 0, + number_of_native_contexts: 2, + number_of_detached_contexts: 0 + } + }; + const expected = p2pServer.getMemoryUsage(); + assert.deepEqual(Object.keys(actual), Object.keys(expected)); + assert.deepEqual(Object.keys(actual.os), Object.keys(expected.os)); + // NOTE(minsulee2): Since the actual.heap part have in difference between the node version + // (> 12.16) and (<= 12.17), which newly includes arrayBuffers info as well. + // See also: the issue #419(https://github.com/ainblockchain/ain-blockchain/issues/419) + expect(Object.keys(expected.heap)).include.members(Object.keys(actual.heap)); + assert.deepEqual(Object.keys(actual.heapStats), Object.keys(expected.heapStats)); + }); + }); + + describe("getRuntimeInfo (it depends on the machine)", () => { + it("gets runtime information", () => { + const actual = { + process: { + version: 'v12.16.0', + platform: 'darwin', + pid: 17424, + uptime: 0, + v8Version: '7.8.279.23-node.31' + }, + os: { + hostname: 'XXXXX-MacBookPro.local', + type: 'Darwin', + release: '20.4.0', + uptime: 892864 + }, + env: { + NETWORK_OPTIMIZATION: undefined, + GENESIS_CONFIGS_DIR: undefined, + MIN_NUM_VALIDATORS: undefined, + ACCOUNT_INDEX: undefined, + P2P_PORT: undefined, + PORT: undefined, + HOSTING_ENV: undefined, + DEBUG: undefined + } + }; + const expected = p2pServer.getRuntimeInfo(); + assert.deepEqual(Object.keys(actual), Object.keys(expected)); + assert.deepEqual(Object.keys(actual.process), Object.keys(expected.process)); + assert.deepEqual(Object.keys(actual.os), Object.keys(expected.os)); + assert.deepEqual(Object.keys(actual.env), Object.keys(expected.env)); + }); + }); + + describe("getTxStatus", () => { + it("gets initial tx status", () => { + const actual = { txPoolSize: 0, txTrackerSize: 0 }; + assert.deepEqual(Object.keys(actual), Object.keys(p2pServer.getTxStatus())); + }); + }); + + describe("getShardingStatus", () => { + it("gets initial sharding status", () => { + const actual = {}; + assert.deepEqual(Object.keys(actual), Object.keys(p2pServer.getShardingStatus())); + }); + }); + }); + + describe("client status", () => { + describe("initConnections", () => { + it("sets maxOutbound", () => { + expect(p2pClient.maxOutbound).to.equal(DEFAULT_MAX_OUTBOUND); + }); + + it("sets maxInbound", () => { + expect(p2pClient.maxInbound).to.equal(DEFAULT_MAX_INBOUND); + }); + }); + + describe("getConnectionStatus", () => { + it("shows initial values of connection status", () => { + const actual = { + maxInbound: DEFAULT_MAX_OUTBOUND, + maxOutbound: DEFAULT_MAX_INBOUND, + numInbound: 0, + numOutbound: 0, + incomingPeers: [], + outgoingPeers: [], + }; + assert.deepEqual(actual, p2pClient.getConnectionStatus()); + }); + }); + + describe("getNetworkStatus", () => { + it("shows initial values of connection status", () => { + const actual = { + ip: p2pClient.server.getExternalIp(), + p2p: { + url: url.format({ + protocol: 'ws', + hostname: p2pClient.server.getExternalIp(), + port: P2P_PORT + }), + port: P2P_PORT, + }, + clientApi: { + url: url.format({ + protocol: 'http', + hostname: p2pClient.server.getExternalIp(), + port: PORT + }), + port: PORT, + }, + jsonRpc: { + url: url.format({ + protocol: 'http', + hostname: p2pClient.server.getExternalIp(), + port: PORT, + pathname: '/json-rpc', + }), + port: PORT, + }, + connectionStatus: p2pClient.getConnectionStatus() + }; + assert.deepEqual(actual, p2pClient.getNetworkStatus()); + }); + }); + + describe("getStatus", () => { + it("shows initial client status", () => { + const blockStatus = p2pServer.getBlockStatus(); + const actual = { + address: p2pServer.getNodeAddress(), + updatedAt: Date.now(), + lastBlockNumber: blockStatus.number, + networkStatus: p2pClient.getNetworkStatus(), + blockStatus: blockStatus, + txStatus: p2pServer.getTxStatus(), + consensusStatus: p2pServer.getConsensusStatus(), + nodeStatus: p2pServer.getNodeStatus(), + shardingStatus: p2pServer.getShardingStatus(), + cpuStatus: p2pServer.getCpuUsage(), + memoryStatus: p2pServer.getMemoryUsage(), + diskStatus: p2pServer.getDiskUsage(), + runtimeInfo: p2pServer.getRuntimeInfo(), + protocolInfo: p2pServer.getProtocolInfo(), + }; + assert.deepEqual(Object.keys(actual), Object.keys(p2pClient.getStatus())); + }); + }); + }); +}); diff --git a/unittest/rule-util.test.js b/unittest/rule-util.test.js index 7d1a2a2cb..1f8313483 100644 --- a/unittest/rule-util.test.js +++ b/unittest/rule-util.test.js @@ -159,7 +159,6 @@ describe("RuleUtil", () => { expect(util.isEmpty('')).to.equal(false); expect(util.isEmpty('abc')).to.equal(false); expect(util.isEmpty('0')).to.equal(false); - expect(util.isEmpty([])).to.equal(false); expect(util.isEmpty([10])).to.equal(false); expect(util.isEmpty({a: 'A'})).to.equal(false); }) @@ -167,6 +166,7 @@ describe("RuleUtil", () => { it("when valid input", () => { expect(util.isEmpty(null)).to.equal(true); expect(util.isEmpty(undefined)).to.equal(true); + expect(util.isEmpty([])).to.equal(true); expect(util.isEmpty({})).to.equal(true); }) }) diff --git a/unittest/state-util.test.js b/unittest/state-util.test.js index 02843be2f..9bccdc91a 100644 --- a/unittest/state-util.test.js +++ b/unittest/state-util.test.js @@ -6,6 +6,13 @@ const { isValidStateLabel, isValidPathForStates, isValidJsObjectForStates, + isValidRuleConfig, + isValidRuleTree, + isValidFunctionConfig, + isValidFunctionTree, + isValidOwnerConfig, + isValidOwnerTree, + applyFunctionChange, setStateTreeVersion, renameStateTreeVersion, deleteStateTree, @@ -187,15 +194,21 @@ describe("state-util", () => { expect(hasReservedChar('/abc')).to.equal(true); expect(hasReservedChar('a/bc')).to.equal(true); expect(hasReservedChar('abc/')).to.equal(true); - expect(hasReservedChar('\u2000/\u2E00')).to.equal(true); expect(hasReservedChar('.')).to.equal(true); - expect(hasReservedChar('*')).to.equal(true); expect(hasReservedChar('$')).to.equal(true); + expect(hasReservedChar('*')).to.equal(true); expect(hasReservedChar('#')).to.equal(true); expect(hasReservedChar('{')).to.equal(true); expect(hasReservedChar('}')).to.equal(true); expect(hasReservedChar('[')).to.equal(true); expect(hasReservedChar(']')).to.equal(true); + expect(hasReservedChar('<')).to.equal(true); + expect(hasReservedChar('>')).to.equal(true); + expect(hasReservedChar("'")).to.equal(true); + expect(hasReservedChar('"')).to.equal(true); + expect(hasReservedChar('`')).to.equal(true); + expect(hasReservedChar(' ')).to.equal(true); + expect(hasReservedChar('\u2000/\u2E00')).to.equal(true); expect(hasReservedChar('\x00')).to.equal(true); expect(hasReservedChar('\x01')).to.equal(true); expect(hasReservedChar('\x02')).to.equal(true); @@ -244,20 +257,35 @@ describe("state-util", () => { }) it("when string input returning false", () => { + expect(hasAllowedPattern('/')).to.equal(false); + expect(hasAllowedPattern('/abc')).to.equal(false); + expect(hasAllowedPattern('a/bc')).to.equal(false); + expect(hasAllowedPattern('abc/')).to.equal(false); expect(hasAllowedPattern('.')).to.equal(false); - expect(hasAllowedPattern('$')).to.equal(false); expect(hasAllowedPattern('./')).to.equal(false); - expect(hasAllowedPattern('$/')).to.equal(false); expect(hasAllowedPattern('a.')).to.equal(false); - expect(hasAllowedPattern('a$')).to.equal(false); expect(hasAllowedPattern('a.b')).to.equal(false); - expect(hasAllowedPattern('a$b')).to.equal(false); expect(hasAllowedPattern('..')).to.equal(false); - expect(hasAllowedPattern('$$')).to.equal(false); expect(hasAllowedPattern('.$')).to.equal(false); expect(hasAllowedPattern('$.')).to.equal(false); + expect(hasAllowedPattern('$')).to.equal(false); + expect(hasAllowedPattern('$/')).to.equal(false); + expect(hasAllowedPattern('a$')).to.equal(false); + expect(hasAllowedPattern('a$b')).to.equal(false); + expect(hasAllowedPattern('$$')).to.equal(false); expect(hasAllowedPattern('*a')).to.equal(false); expect(hasAllowedPattern('a*')).to.equal(false); + expect(hasAllowedPattern('#')).to.equal(false); + expect(hasAllowedPattern('{')).to.equal(false); + expect(hasAllowedPattern('}')).to.equal(false); + expect(hasAllowedPattern('[')).to.equal(false); + expect(hasAllowedPattern(']')).to.equal(false); + expect(hasAllowedPattern('<')).to.equal(false); + expect(hasAllowedPattern('>')).to.equal(false); + expect(hasAllowedPattern("'")).to.equal(false); + expect(hasAllowedPattern('"')).to.equal(false); + expect(hasAllowedPattern('`')).to.equal(false); + expect(hasAllowedPattern(' ')).to.equal(false); }) it("when string input returning true", () => { @@ -283,6 +311,9 @@ describe("state-util", () => { expect(isValidStateLabel('.')).to.equal(false); expect(isValidStateLabel('$')).to.equal(false); expect(isValidStateLabel('/')).to.equal(false); + expect(isValidStateLabel("'")).to.equal(false); + expect(isValidStateLabel('"')).to.equal(false); + expect(isValidStateLabel('`')).to.equal(false); }) it("when string input returning true", () => { @@ -290,6 +321,20 @@ describe("state-util", () => { expect(isValidStateLabel('.a')).to.equal(true); expect(isValidStateLabel('$a')).to.equal(true); expect(isValidStateLabel('*')).to.equal(true); + expect(isValidStateLabel('~')).to.equal(true); + expect(isValidStateLabel('!')).to.equal(true); + expect(isValidStateLabel('@')).to.equal(true); + expect(isValidStateLabel('%')).to.equal(true); + expect(isValidStateLabel('^')).to.equal(true); + expect(isValidStateLabel('&')).to.equal(true); + expect(isValidStateLabel('-')).to.equal(true); + expect(isValidStateLabel('_')).to.equal(true); + expect(isValidStateLabel('=')).to.equal(true); + expect(isValidStateLabel('+')).to.equal(true); + expect(isValidStateLabel('|')).to.equal(true); + expect(isValidStateLabel(';')).to.equal(true); + expect(isValidStateLabel(',')).to.equal(true); + expect(isValidStateLabel('?')).to.equal(true); }) }) @@ -484,6 +529,775 @@ describe("state-util", () => { }) }) + describe("isValidRuleConfig", () => { + it("when invalid input", () => { + assert.deepEqual(isValidRuleConfig(null), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig(undefined), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({}), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig([]), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig([1, 2, 3]), {isValid: false, invalidPath: '/'}); + assert.deepEqual( + isValidRuleConfig(['a', 'b', 'c']), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + undef: undefined + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + empty_obj: {} + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + array: [] + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + array: [1, 2, 3] + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + array: ['a', 'b', 'c'] + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + 'a': { + '.': 'x' + } + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + 'a': { + '$': 'x' + } + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + 'a': { + '*b': 'x' + } + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleConfig({ + 'a': { + 'b*': 'x' + } + }), {isValid: false, invalidPath: '/'}); + }) + + it("when valid input", () => { + assert.deepEqual(isValidRuleConfig(true), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidRuleConfig(false), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidRuleConfig("auth.addr === 'abcd'"), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidRuleConfig( + "(auth.addr === $from || auth.fid === '_stake' || auth.fid === '_unstake' || auth.fid === '_pay' || auth.fid === '_claim' || auth.fid === '_hold' || auth.fid === '_release' || auth.fid === '_collectFee' || auth.fid === '_distributeFee') && !getValue('transfer/' + $from + '/' + $to + '/' + $key) && (util.isServAcntName($from) || util.isCksumAddr($from)) && (util.isServAcntName($to) || util.isCksumAddr($to)) && $from !== $to && util.isNumber(newData) && getValue(util.getBalancePath($from)) >= newData"), + {isValid: true, invalidPath: ''}); + }) + }) + + describe("isValidRuleTree", () => { + it("when invalid input", () => { + assert.deepEqual(isValidRuleTree(undefined), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleTree({}), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleTree([]), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleTree([1, 2, 3]), {isValid: false, invalidPath: '/'}); + assert.deepEqual( + isValidRuleTree(['a', 'b', 'c']), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidRuleTree({ + undef: undefined + }), {isValid: false, invalidPath: '/undef'}); + assert.deepEqual(isValidRuleTree({ + empty_obj: {} + }), {isValid: false, invalidPath: '/empty_obj'}); + assert.deepEqual(isValidRuleTree({ + array: [] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidRuleTree({ + array: [1, 2, 3] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidRuleTree({ + array: ['a', 'b', 'c'] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidRuleTree({ + some_key: {} + }), {isValid: false, invalidPath: '/some_key'}); + assert.deepEqual(isValidRuleTree({ + some_key: null + }), {isValid: false, invalidPath: '/some_key'}); + assert.deepEqual(isValidRuleTree({ + some_key: undefined + }), {isValid: false, invalidPath: '/some_key'}); + }) + + it("when invalid input with invalid owner config", () => { + assert.deepEqual(isValidRuleTree({ + some_path: { + '.write': { + } + } + }), {isValid: false, invalidPath: '/some_path/.write'}); + assert.deepEqual(isValidRuleTree({ + some_path: { + '.write': null + } + }), {isValid: false, invalidPath: '/some_path/.write'}); + assert.deepEqual(isValidRuleTree({ + some_path: { + '.write': undefined + } + }), {isValid: false, invalidPath: '/some_path/.write'}); + }) + + it("when valid input", () => { + assert.deepEqual(isValidRuleTree(null), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidRuleTree({ + '.write': true + }), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidRuleTree({ + some_path1: { + '.write': true + }, + some_path2: { + '.write': "auth.addr === 'abcd'" + } + }), {isValid: true, invalidPath: ''}); + }) + }) + + describe("isValidFunctionConfig", () => { + it("when invalid input", () => { + assert.deepEqual(isValidFunctionConfig(null), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionConfig(undefined), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionConfig({}), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionConfig([]), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionConfig([1, 2, 3]), {isValid: false, invalidPath: '/'}); + assert.deepEqual( + isValidFunctionConfig(['a', 'b', 'c']), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionConfig({ + undef: undefined + }), {isValid: false, invalidPath: '/undef'}); + assert.deepEqual(isValidFunctionConfig({ + empty_obj: {} + }), {isValid: false, invalidPath: '/empty_obj'}); + assert.deepEqual(isValidFunctionConfig({ + array: [] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidFunctionConfig({ + array: [1, 2, 3] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidFunctionConfig({ + array: ['a', 'b', 'c'] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidFunctionConfig({ + 'a': { + '.': 'x' + } + }), {isValid: false, invalidPath: '/a'}); + assert.deepEqual(isValidFunctionConfig({ + 'a': { + '$': 'x' + } + }), {isValid: false, invalidPath: '/a'}); + assert.deepEqual(isValidFunctionConfig({ + 'a': { + '*b': 'x' + } + }), {isValid: false, invalidPath: '/a'}); + assert.deepEqual(isValidFunctionConfig({ + 'a': { + 'b*': 'x' + } + }), {isValid: false, invalidPath: '/a'}); + }) + + it("when invalid input with deeper path", () => { + assert.deepEqual(isValidFunctionConfig({ + a_fid: {} + }), {isValid: false, invalidPath: '/a_fid'}); + assert.deepEqual(isValidFunctionConfig({ + a_fid: 'some string' + }), {isValid: false, invalidPath: '/a_fid'}); + }) + + it("when invalid input with NATIVE type", () => { + assert.deepEqual(isValidFunctionConfig({ + "_transfer": { + // Missing function_type + "function_id": "_transfer" + } + }), {isValid: false, invalidPath: '/_transfer'}); + assert.deepEqual(isValidFunctionConfig({ + "_transfer": { + "function_type": "NATIVE", + // Missing function_id + } + }), {isValid: false, invalidPath: '/_transfer'}); + assert.deepEqual(isValidFunctionConfig({ + "_transfer": { + "function_type": "NATIVE", + "function_id": "_transfer", + "unknown_property": "some value" // Unknown property + } + }), {isValid: false, invalidPath: '/_transfer'}); + assert.deepEqual(isValidFunctionConfig({ + "_transfer": { + "function_type": "unknown type", // Unknown function_type + "function_id": "_transfer" + } + }), {isValid: false, invalidPath: '/_transfer'}); + assert.deepEqual(isValidFunctionConfig({ + "_transfer": { + "function_type": "NATIVE", + "function_id": "some other fid" // Wrong function_id + } + }), {isValid: false, invalidPath: '/_transfer/function_id'}); + }) + + it("when invalid input with REST type", () => { + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + // Missing function_type + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } + }), {isValid: false, invalidPath: '/0x11111'}); + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + "function_type": "REST", + // Missing function_id + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } + }), {isValid: false, invalidPath: '/0x11111'}); + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + // Missing event_listener + "service_name": "https://ainetwork.ai", + } + }), {isValid: false, invalidPath: '/0x11111'}); + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + // Missing service_name + } + }), {isValid: false, invalidPath: '/0x11111'}); + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + "unknown_property": "some value" // Unknown property + } + }), {isValid: false, invalidPath: '/0x11111'}); + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + "function_type": "REST", + "function_id": "some other fid", // Wrong function_id + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } + }), {isValid: false, invalidPath: '/0x11111/function_id'}); + assert.deepEqual(isValidFunctionConfig({ + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "some non-url value", // Invalid url + "service_name": "https://ainetwork.ai", + } + }), {isValid: false, invalidPath: '/0x11111'}); + }) + + it("when valid input", () => { + assert.deepEqual(isValidFunctionConfig({ + "_transfer": { + "function_type": "NATIVE", + "function_id": "_transfer", + }, + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + }, + "fid_to_delete": null // To be deleted + }), {isValid: true, invalidPath: ''}); + }) + }) + + describe("isValidFunctionTree", () => { + it("when invalid input", () => { + assert.deepEqual(isValidFunctionTree(undefined), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionTree({}), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionTree([]), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionTree([1, 2, 3]), {isValid: false, invalidPath: '/'}); + assert.deepEqual( + isValidFunctionTree(['a', 'b', 'c']), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidFunctionTree({ + undef: undefined + }), {isValid: false, invalidPath: '/undef'}); + assert.deepEqual(isValidFunctionTree({ + empty_obj: {} + }), {isValid: false, invalidPath: '/empty_obj'}); + assert.deepEqual(isValidFunctionTree({ + array: [] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidFunctionTree({ + array: [1, 2, 3] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidFunctionTree({ + array: ['a', 'b', 'c'] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidFunctionTree({ + some_key: {} + }), {isValid: false, invalidPath: '/some_key'}); + assert.deepEqual(isValidFunctionTree({ + some_key: null + }), {isValid: false, invalidPath: '/some_key'}); + assert.deepEqual(isValidFunctionTree({ + some_key: undefined + }), {isValid: false, invalidPath: '/some_key'}); + }) + + it("when invalid input with invalid owner config", () => { + assert.deepEqual(isValidFunctionTree({ + some_path: { + '.function': { + } + } + }), {isValid: false, invalidPath: '/some_path/.function'}); + assert.deepEqual(isValidFunctionTree({ + some_path: { + '.function': null + } + }), {isValid: false, invalidPath: '/some_path/.function'}); + assert.deepEqual(isValidFunctionTree({ + some_path: { + '.function': undefined + } + }), {isValid: false, invalidPath: '/some_path/.function'}); + }) + + it("when valid input", () => { + assert.deepEqual(isValidFunctionTree(null), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidFunctionTree({ + '.function': { + "_transfer": { + "function_type": "NATIVE", + "function_id": "_transfer", + }, + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } + } + }), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidFunctionTree({ + some_path1: { + '.function': { + "_transfer": { + "function_type": "NATIVE", + "function_id": "_transfer", + }, + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } + } + }, + some_path2: { + '.function': { + "_transfer": { + "function_type": "NATIVE", + "function_id": "_transfer", + }, + "0x11111": { + "function_type": "REST", + "function_id": "0x11111", + "event_listener": "https://events.ainetwork.ai/trigger", + "service_name": "https://ainetwork.ai", + } + } + } + }), {isValid: true, invalidPath: ''}); + }) + }) + + describe("isValidOwnerConfig", () => { + it("when invalid input", () => { + assert.deepEqual(isValidOwnerConfig(null), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig(undefined), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({}), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig([]), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig([1, 2, 3]), {isValid: false, invalidPath: '/'}); + assert.deepEqual( + isValidOwnerConfig(['a', 'b', 'c']), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + undef: undefined + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + empty_obj: {} + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + array: [] + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + array: [1, 2, 3] + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + array: ['a', 'b', 'c'] + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + 'a': { + '.': 'x' + } + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + 'a': { + '$': 'x' + } + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + 'a': { + '*b': 'x' + } + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + 'a': { + 'b*': 'x' + } + }), {isValid: false, invalidPath: '/'}); + }) + + it("when invalid input with deeper path", () => { + assert.deepEqual(isValidOwnerConfig({ + some_key: {} + }), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerConfig({ + 'owners': null + }), {isValid: false, invalidPath: '/owners'}); + assert.deepEqual(isValidOwnerConfig({ + 'owners': {} + }), {isValid: false, invalidPath: '/owners'}); + }) + + it("when invalid input with invalid owner (address or fid)", () => { + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + '0x0': { // Invalid address + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + }), {isValid: false, invalidPath: '/owners/0x0'}); + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + '0x09a0d53fdf1c36a131938eb379b98910e55eefe1': { // Non-checksum address + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + }), {isValid: false, invalidPath: '/owners/0x09a0d53fdf1c36a131938eb379b98910e55eefe1'}); + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + 'fid:_invalidFid': { // Invalid fid + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + } + } + }), {isValid: false, invalidPath: '/owners/fid:_invalidFid'}); + }) + + it("when invalid input with invalid owner permissions", () => { + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1': { + "branch_owner": true, + "write_function": true, + "write_owner": true, + // Missing write_rule + }, + } + }), {isValid: false, invalidPath: '/owners/0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'}); + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1': { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + "do_something_else": true, // Unknown permission + }, + } + }), {isValid: false, invalidPath: '/owners/0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'}); + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1': { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": 'true', // Non-boolean value + }, + } + }), {isValid: false, invalidPath: '/owners/0x09A0d53FDf1c36A131938eb379b98910e55EEfe1'}); + }) + + it("when valid input", () => { + assert.deepEqual(isValidOwnerConfig({ + 'owners': { + '*': { + "branch_owner": true, + "write_function": false, + "write_owner": false, + "write_rule": false, + }, + '0x09A0d53FDf1c36A131938eb379b98910e55EEfe1': { + "branch_owner": false, + "write_function": true, + "write_owner": true, + "write_rule": true, + }, + 'fid:_createApp': { + "branch_owner": true, + "write_function": true, + "write_owner": true, + "write_rule": true, + }, + } + }), {isValid: true, invalidPath: ''}); + }) + }) + + describe("isValidOwnerTree", () => { + it("when invalid input", () => { + assert.deepEqual(isValidOwnerTree(undefined), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerTree({}), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerTree([]), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerTree([1, 2, 3]), {isValid: false, invalidPath: '/'}); + assert.deepEqual( + isValidOwnerTree(['a', 'b', 'c']), {isValid: false, invalidPath: '/'}); + assert.deepEqual(isValidOwnerTree({ + undef: undefined + }), {isValid: false, invalidPath: '/undef'}); + assert.deepEqual(isValidOwnerTree({ + empty_obj: {} + }), {isValid: false, invalidPath: '/empty_obj'}); + assert.deepEqual(isValidOwnerTree({ + array: [] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidOwnerTree({ + array: [1, 2, 3] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidOwnerTree({ + array: ['a', 'b', 'c'] + }), {isValid: false, invalidPath: '/array'}); + assert.deepEqual(isValidOwnerTree({ + some_key: {} + }), {isValid: false, invalidPath: '/some_key'}); + assert.deepEqual(isValidOwnerTree({ + some_key: null + }), {isValid: false, invalidPath: '/some_key'}); + assert.deepEqual(isValidOwnerTree({ + some_key: undefined + }), {isValid: false, invalidPath: '/some_key'}); + }) + + it("when invalid input with invalid owner config", () => { + assert.deepEqual(isValidOwnerTree({ + some_path: { + '.owner': { + } + } + }), {isValid: false, invalidPath: '/some_path/.owner'}); + assert.deepEqual(isValidOwnerTree({ + some_path: { + '.owner': null + } + }), {isValid: false, invalidPath: '/some_path/.owner'}); + assert.deepEqual(isValidOwnerTree({ + some_path: { + '.owner': undefined + } + }), {isValid: false, invalidPath: '/some_path/.owner'}); + }) + + it("when valid input", () => { + assert.deepEqual(isValidOwnerTree(null), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidOwnerTree({ + '.owner': { + 'owners': { + '*': { + "branch_owner": true, + "write_function": false, + "write_owner": false, + "write_rule": false, + } + } + } + }), {isValid: true, invalidPath: ''}); + assert.deepEqual(isValidOwnerTree({ + some_path1: { + '.owner': { + 'owners': { + '*': { + "branch_owner": true, + "write_function": false, + "write_owner": false, + "write_rule": false, + } + } + } + }, + some_path2: { + '.owner': { + 'owners': { + '*': { + "branch_owner": true, + "write_function": false, + "write_owner": false, + "write_rule": false, + } + } + } + } + }), {isValid: true, invalidPath: ''}); + }) + }) + + describe("applyFunctionChange()", () => { + const curFunction = { + ".function": { + "0x111": { + "function_type": "NATIVE", + "function_id": "0x111" + }, + "0x222": { + "function_type": "NATIVE", + "function_id": "0x222" + }, + "0x333": { + "function_type": "NATIVE", + "function_id": "0x333" + } + } + }; + + it("add / delete / modify non-existing function", () => { + assert.deepEqual(applyFunctionChange(null, { + ".function": { // function + "0x111": null, + "0x222": { + "function_type": "REST", + "function_id": "0x222" + }, + }, + "deeper": { + ".function": { // deeper function + "0x999": { + "function_type": "REST", + "function_id": "0x999" + } + } + } + }), { // the same as the given function change. + ".function": { + "0x111": null, + "0x222": { + "function_type": "REST", + "function_id": "0x222" + }, + }, + "deeper": { + ".function": { + "0x999": { + "function_type": "REST", + "function_id": "0x999" + } + } + } + }); + }); + + it("add / delete / modify existing function", () => { + assert.deepEqual(applyFunctionChange(curFunction, { + ".function": { + "0x111": null, // delete + "0x222": { // modify + "function_type": "REST", + "function_id": "0x222" + }, + "0x444": { // add + "function_type": "REST", + "function_id": "0x444" + } + } + }), { + ".function": { + "0x222": { // modified + "function_type": "REST", + "function_id": "0x222" + }, + "0x333": { // untouched + "function_type": "NATIVE", + "function_id": "0x333" + }, + "0x444": { // added + "function_type": "REST", + "function_id": "0x444" + } + } + }); + }); + + it("add / delete / modify existing function with deeper function", () => { + assert.deepEqual(applyFunctionChange(curFunction, { + ".function": { + "0x111": null, // delete + "0x222": { // modify + "function_type": "REST", + "function_id": "0x222" + }, + "0x444": { // add + "function_type": "REST", + "function_id": "0x444" + } + }, + "deeper": { + ".function": { // deeper function + "0x999": { + "function_type": "REST", + "function_id": "0x999" + } + } + } + }), { + ".function": { // deeper function has no effect + "0x222": { // modified + "function_type": "REST", + "function_id": "0x222" + }, + "0x333": { // untouched + "function_type": "NATIVE", + "function_id": "0x333" + }, + "0x444": { // added + "function_type": "REST", + "function_id": "0x444" + } + } + }); + }); + + it("with null function change", () => { + assert.deepEqual(applyFunctionChange(curFunction, null), null); + }); + }); + describe("setStateTreeVersion", () => { it("leaf node", () => { const ver1 = 'ver1'; diff --git a/yarn.lock b/yarn.lock index 42600535d..99ae718eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4693,6 +4693,11 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== +valid-url@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"