diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51aaf35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/generated_files +config.json +/extensions/core/cryptonotecoin.py +/test/core/cryptonotecoin-test.sh \ No newline at end of file diff --git a/change-checker.sh b/change-checker.sh new file mode 100644 index 0000000..bce3c72 --- /dev/null +++ b/change-checker.sh @@ -0,0 +1,41 @@ +#! /usr/bin/env bash + +# Starts deployment if change is detected +# Cron cycles this script + +# Exit immediately if an error occurs, or if an undeclared variable is used +set -o errexit +set -o nounset + +[ "$OSTYPE" != "win"* ] || die "Install Cygwin to use on Windows" + +# Set directory vars +. "vars.cfg" +GENERATOR_FILE_PATH="${PROJECT_DIR}/generator.sh" + +# Cron cycle period in minutes +CRON_CYCLE='30' +START_DEPLOY="" + +# Pull request +echo "git pull base coin" +cd $BASE_COIN_PATH +git reset --hard origin/master +git pull +cd $PROJECT_DIR + +# Check if file is changed in _posts folder in the cron cycle +CURRENT_TIMESTAMP="$( date +%s )" +if [[ "$OSTYPE" == "darwin"* ]]; then + LAST_EDITED_TIMESTAMP="$( stat -f "%m" ${BASE_COIN_PATH} )" +else + LAST_EDITED_TIMESTAMP="$( stat -c "%Y" ${BASE_COIN_PATH} )" +fi +let TARGET_TIME=${LAST_EDITED_TIMESTAMP}+${CRON_CYCLE}*60 + +if [[ ${TARGET_TIME} -ge ${CURRENT_TIMESTAMP} ]]; then + echo "Freshly edited" + ${START_GENERATOR}="$( exec bash "${GENERATOR_FILE_PATH}" )" +fi + +echo ${START_GENERATOR} diff --git a/compile.sh b/compile.sh new file mode 100644 index 0000000..210354b --- /dev/null +++ b/compile.sh @@ -0,0 +1,99 @@ +#! /usr/bin/env bash + + +# Bash script for change coin files + +# Exit immediately if an error occurs, or if an undeclared variable is used +set -o errexit + + +[ "$OSTYPE" != "win"* ] || die "Windows is not supported" + + +# A POSIX variable +OPTIND=1 # Reset in case getopts has been used previously in the shell. + + +while getopts "c:z" opt; do + case "$opt" in + c) COMPILE_ARGS=${OPTARG} + ;; + z) archive=1 + esac +done +archive=${archive:-0} + +shift $((OPTIND-1)) + +cd ${NEW_COIN_PATH} + +# Compile! +if [[ "$OSTYPE" == "msys" ]]; then + cmake -G "Visual Studio 12 Win64" "..\.." + msbuild.exe Bytecoin.sln /property:Configuration=Release ${COMPILE_ARGS} +else + make release-static +fi + +if [[ $? == "0" ]]; then + echo "Compilation successful" +fi + +# Move and zip binaries +if [[ $archive == "1" ]]; then + BUILD_PATH="${WORK_FOLDERS_PATH}/builds" + MAC_BUILD_NAME="${__CONFIG_core_CRYPTONOTE_NAME}-mac" + LINUX_BUILD_NAME="${__CONFIG_core_CRYPTONOTE_NAME}-linux" + WINDOWS_BUILD_NAME="${__CONFIG_core_CRYPTONOTE_NAME}-windows" + ALL_BUILD_FILES="${__CONFIG_core_CRYPTONOTE_NAME}-all-files" + + case "$OSTYPE" in + msys*) rm -f ${BUILD_PATH}/${WINDOWS_BUILD_NAME}.zip + rm -rf ${BUILD_PATH}/${WINDOWS_BUILD_NAME} + mkdir -p ${BUILD_PATH}/${WINDOWS_BUILD_NAME} + cp ${NEW_COIN_PATH}/build/release/bin/monerod.exe ${BUILD_PATH}/${WINDOWS_BUILD_NAME} + cp ${NEW_COIN_PATH}/build/release/bin/monero-wallet-cli.exe ${BUILD_PATH}/${WINDOWS_BUILD_NAME} + if [[ " ${__CONFIG_extensions_text} " == *"multiply.json"* ]]; then + rm -rf ${BUILD_PATH}/${WINDOWS_BUILD_NAME}/configs/.git + rm -rf ${BUILD_PATH}/${WINDOWS_BUILD_NAME}/configs/.gitignore + fi + cd ${BUILD_PATH} + zip -r ${WINDOWS_BUILD_NAME}.zip ${WINDOWS_BUILD_NAME}/ + ;; + darwin*) rm -f ${BUILD_PATH}/${MAC_BUILD_NAME}.zip + rm -rf ${BUILD_PATH}/${MAC_BUILD_NAME} + mkdir -p ${BUILD_PATH}/${MAC_BUILD_NAME} + cp ${NEW_COIN_PATH}/build/release/bin/monerod ${BUILD_PATH}/${MAC_BUILD_NAME} + cp ${NEW_COIN_PATH}/build/release/bin/monero-wallet-cli ${BUILD_PATH}/${MAC_BUILD_NAME} + if [[ " ${__CONFIG_extensions_text} " == *"multiply.json"* ]]; then + rm -rf ${BUILD_PATH}/${MAC_BUILD_NAME}/configs/.git + rm -rf ${BUILD_PATH}/${MAC_BUILD_NAME}/configs/.gitignore + fi + cd ${BUILD_PATH} + zip -r ${MAC_BUILD_NAME}.zip ${MAC_BUILD_NAME}/ + ;; + *) rm -f ${BUILD_PATH}/${LINUX_BUILD_NAME}.tar.gz + rm -rf ${BUILD_PATH}/${LINUX_BUILD_NAME} + mkdir -p ${BUILD_PATH}/${LINUX_BUILD_NAME} + cp ${NEW_COIN_PATH}/build/release/bin/monerod ${BUILD_PATH}/${LINUX_BUILD_NAME} + cp ${NEW_COIN_PATH}/build/release/bin/monero-wallet-cli ${BUILD_PATH}/${LINUX_BUILD_NAME} + if [[ " ${__CONFIG_extensions_text} " == *"multiply.json"* ]]; then + rm -rf ${BUILD_PATH}/${LINUX_BUILD_NAME}/configs/.git + rm -rf ${BUILD_PATH}/${LINUX_BUILD_NAME}/configs/.gitignore + fi + cd ${BUILD_PATH} + tar -zcvf ${LINUX_BUILD_NAME}.tar.gz ${LINUX_BUILD_NAME} + ;; + esac + + rm -rf ${BUILD_PATH}/${ALL_BUILD_FILES} + mkdir -p ${BUILD_PATH}/${ALL_BUILD_FILES} + cp -R ${NEW_COIN_PATH}/build/release/src/ ${BUILD_PATH}/${ALL_BUILD_FILES}/ + if [[ " ${__CONFIG_extensions_text} " == *"multiply.json"* ]]; then + git clone https://github.com/forknote/configs.git ${BUILD_PATH}/${ALL_BUILD_FILES}/configs + rm -rf ${BUILD_PATH}/${ALL_BUILD_FILES}/configs/.git + rm -rf ${BUILD_PATH}/${ALL_BUILD_FILES}/configs/.gitignore + fi + + rm -rf "${NEW_COIN_PATH}/build" +fi diff --git a/configs/monero-block-explorer.json b/configs/monero-block-explorer.json new file mode 100644 index 0000000..c30d2d1 --- /dev/null +++ b/configs/monero-block-explorer.json @@ -0,0 +1,13 @@ +{ + "extensions": [ + "core/monero.json", + "blockchain-explorer.json" + ], + "base_coin": { + "name": "monero", + "git": "https://github.com/monero-project/monero.git" + }, + "core": { + "CRYPTONOTE_NAME": "monero-block-explorer" + } +} \ No newline at end of file diff --git a/extensions/blockchain-explorer.json b/extensions/blockchain-explorer.json new file mode 100644 index 0000000..0543d8a --- /dev/null +++ b/extensions/blockchain-explorer.json @@ -0,0 +1,740 @@ +{ + "file": "blockchain-explorer.json", + "description": "Adds json rpc methods for block and transaction actions", + "required": [ + "core/monero.json" + ], + "files": [ + { + "path": "/src/blockchain_db/blockchain_db.h", + "changes": [ + { + "action": "add_bellow", + "marker": "virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0;", + "parameters": { + "text": [ + "virtual uint64_t get_block_already_generated_transactions(const uint64_t& height) const = 0;" + ] + } + } + ] + }, + { + "path": "/src/blockchain_db/lmdb/db_lmdb.h", + "changes": [ + { + "action": "add_bellow", + "marker": "virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const;", + "parameters": { + "text": [ + "virtual uint64_t get_block_already_generated_transactions(const uint64_t& height) const;" + ] + } + } + ] + }, + { + "path": "/src/blockchain_db/lmdb/db_lmdb.cpp", + "changes": [ + { + "action": "add_bellow", + "marker": "uint64_t bi_coins;", + "parameters": { + "text": [ + "uint64_t bi_txs;" + ] + } + }, + { + "action": "add_bellow", + "marker": "bi.bi_coins = coins_generated;", + "parameters": { + "text": [ + " bi.bi_txs = m_num_txs;" + ] + } + }, + { + "action": "add_above", + "marker": "crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) const", + "parameters": { + "text": [ + "uint64_t BlockchainLMDB::get_block_already_generated_transactions(const uint64_t& height) const", + "{", + " LOG_PRINT_L3(\"BlockchainLMDB::\" << __func__);", + " check_open();", + "", + " TXN_PREFIX_RDONLY();", + " RCURSOR(block_info);", + "", + " MDB_val_set(result, height);", + " auto get_result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &result, MDB_GET_BOTH);", + " if (get_result == MDB_NOTFOUND)", + " {", + " throw0(BLOCK_DNE(std::string(\"Attempt to get generated coins from height \").append(boost::lexical_cast(height)).append(\" failed -- block size not in db\").c_str()));", + " }", + " else if (get_result)", + " throw0(DB_ERROR(\"Error attempting to retrieve a total generated coins from the db\"));", + "", + " mdb_block_info *bi = (mdb_block_info *)result.mv_data;", + " uint64_t ret = bi->bi_txs;", + " TXN_POSTFIX_RDONLY();", + " return ret;", + "}", + "" + ] + } + }, + { + "action": "add_bellow", + "marker": "bi.bi_coins = *(uint64_t *)v.mv_data;", + "parameters": { + "text": [ + " // Don't really need this", + " bi.bi_txs = 0;" + ] + } + } + ] + }, + { + "path": "/src/cryptonote_core/blockchain.h", + "changes": [ + { + "action": "add_bellow", + "marker": "Blockchain(tx_memory_pool& tx_pool);", + "parameters": { + "text": [ + " void get_last_n_blocks_sizes_from_height(size_t h, std::vector& sz, size_t count) const;", + " uint64_t get_block_granted_full_reward_zone_by_block_version(size_t major_version);" + ] + } + } + ] + }, + { + "path": "/src/cryptonote_core/blockchain.cpp", + "changes": [ + { + "action": "add_bellow", + "marker": "static const uint64_t testnet_hard_fork_version_1_till = 624633;", + "parameters": { + "text": [ + "//------------------------------------------------------------------", + "// get the block sizes of the last blocks, and return by reference with max height .", + "void Blockchain::get_last_n_blocks_sizes_from_height(size_t h, std::vector& sz, size_t count) const", + "{", + " LOG_PRINT_L3(\"Blockchain::\" << __func__);", + " CRITICAL_REGION_LOCAL(m_blockchain_lock);", + "", + " // this function is meaningless for an empty blockchain...granted it should never be empty", + " if(h == 0)", + " return;", + "", + " m_db->block_txn_start(true);", + " // add size of last blocks to vector (or less, if blockchain size < count)", + " size_t start_offset = h - std::min(h, count);", + " for(size_t i = start_offset; i < h; i++)", + " {", + " sz.push_back(m_db->get_block_size(i));", + " }", + " m_db->block_txn_stop();", + "}", + "", + "uint64_t Blockchain::get_block_granted_full_reward_zone_by_block_version(size_t major_version)", + "{", + " uint64_t full_reward_zone = major_version < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;", + "", + " return full_reward_zone;", + "}", + "" + ] + } + } + ] + }, + { + "path": "/src/rpc/core_rpc_server.h", + "changes": [ + { + "action": "add_bellow", + "marker": "MAP_JON_RPC_WE(\"get_version\", on_get_version, COMMAND_RPC_GET_VERSION)", + "parameters": { + "text": [ + " MAP_JON_RPC_WE(\"f_blocks_list_json\", f_on_blocks_list_json, F_COMMAND_RPC_GET_BLOCKS_LIST)", + " MAP_JON_RPC_WE(\"f_block_json\", f_on_block_json, F_COMMAND_RPC_GET_BLOCK_DETAILS)", + " MAP_JON_RPC_WE(\"f_transaction_json\", f_on_transaction_json, F_COMMAND_RPC_GET_TRANSACTION_DETAILS)", + " MAP_JON_RPC_WE(\"f_pool_json\", f_on_transactions_pool_json, F_COMMAND_RPC_GET_POOL)" + ] + } + }, + { + "action": "add_above", + "marker": "private:", + "parameters": { + "text": [ + " bool f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::request& req, F_COMMAND_RPC_GET_BLOCKS_LIST::response& res, epee::json_rpc::error& error_resp);", + " bool f_on_block_json(const F_COMMAND_RPC_GET_BLOCK_DETAILS::request& req, F_COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp);", + " bool f_on_transaction_json(const F_COMMAND_RPC_GET_TRANSACTION_DETAILS::request& req, F_COMMAND_RPC_GET_TRANSACTION_DETAILS::response& res, epee::json_rpc::error& error_resp);", + " bool f_on_transactions_pool_json(const F_COMMAND_RPC_GET_POOL::request& req, F_COMMAND_RPC_GET_POOL::response& res, epee::json_rpc::error& error_resp);", + " bool f_getMixin(const transaction& transaction, uint64_t& mixin);", + "" + ] + } + } + ] + }, + { + "path": "/src/rpc/core_rpc_server.cpp", + "changes": [ + { + "action": "add_above", + "marker": "const command_line::arg_descriptor core_rpc_server::arg_rpc_bind_ip = {", + "parameters": { + "text": [ + " bool core_rpc_server::f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::request& req, F_COMMAND_RPC_GET_BLOCKS_LIST::response& res, epee::json_rpc::error& error_resp) {", + " if (m_core.get_current_blockchain_height() <= req.height) {", + " error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;", + " error_resp.message = std::string(\"To big height: \") + std::to_string(req.height) + \", current blockchain height = \" + std::to_string(m_core.get_current_blockchain_height());", + " return false;", + " }", + "", + " uint32_t print_blocks_count = 30;", + " uint32_t last_height = req.height - print_blocks_count;", + " if (req.height <= print_blocks_count) {", + " last_height = 0;", + " }", + "", + " for (uint32_t i = req.height; i >= last_height; i--) {", + " crypto::hash block_hash = m_core.get_block_id_by_height(static_cast(i));", + " block blk;", + " if (!m_core.get_block_by_hash(block_hash, blk)) {", + " error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;", + " error_resp.message = \"Internal error: can't get block by height. Height = \" + std::to_string(i) + '.';", + " return false;", + " }", + "", + " size_t blokBlobSize = m_core.get_blockchain_storage().get_db().get_block_size(i);", + "", + " f_block_short_response block_short;", + " block_short.cumul_size = blokBlobSize;", + " block_short.timestamp = blk.timestamp;", + " block_short.height = i;", + " block_short.hash = string_tools::pod_to_hex(block_hash);", + " block_short.tx_count = blk.tx_hashes.size() + 1;", + "", + " res.blocks.push_back(block_short);", + "", + " if (i == 0)", + " break;", + " }", + "", + " res.status = CORE_RPC_STATUS_OK;", + " return true;", + " }", + "", + " bool core_rpc_server::f_on_block_json(const F_COMMAND_RPC_GET_BLOCK_DETAILS::request& req, F_COMMAND_RPC_GET_BLOCK_DETAILS::response& res, epee::json_rpc::error& error_resp) {", + " crypto::hash hash;", + "", + " if(!check_core_busy())", + " {", + " error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY;", + " error_resp.message = \"Core is busy.\";", + " return false;", + " }", + "", + " try {", + " uint32_t height = boost::lexical_cast(req.hash);", + " hash = m_core.get_block_id_by_height(height);", + " } catch (boost::bad_lexical_cast &) {", + " if (!parse_hash256(req.hash, hash)) {", + " error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;", + " error_resp.message = \"Failed to parse hex representation of block hash. Hex = \" + req.hash + '.';", + " return false;", + " }", + " }", + "", + " block blk;", + " if (!m_core.get_block_by_hash(hash, blk)) {", + " error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;", + " error_resp.message = \"Internal error: can't get block by hash. Hash = \" + req.hash + '.';", + " return false;", + " }", + "", + " if (blk.miner_tx.vin.front().type() != typeid(txin_gen)) {", + " error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;", + " error_resp.message = \"Internal error: coinbase transaction in the block has the wrong type\";", + " return false;", + " }", + "", + " block_header_responce block_header;", + " res.block.height = boost::get(blk.miner_tx.vin.front()).height;", + " fill_block_header_responce(blk, false, res.block.height, hash, block_header);", + "", + " res.block.major_version = block_header.major_version;", + " res.block.minor_version = block_header.minor_version;", + " res.block.timestamp = block_header.timestamp;", + " res.block.prev_hash = block_header.prev_hash;", + " res.block.nonce = block_header.nonce;", + " res.block.hash = string_tools::pod_to_hex(hash);", + " res.block.depth = m_core.get_current_blockchain_height() - res.block.height - 1;", + " res.block.difficulty = m_core.get_blockchain_storage().block_difficulty(static_cast(res.block.height));", + "", + " res.block.reward = block_header.reward;", + "", + " std::vector blocksSizes;", + " m_core.get_blockchain_storage().get_last_n_blocks_sizes_from_height(res.block.height + 1, blocksSizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);", + " res.block.sizeMedian = epee::misc_utils::median(blocksSizes);", + "", + " uint64_t blockSize = m_core.get_blockchain_storage().get_db().get_block_size(res.block.height);", + " if (!blockSize) {", + " return false;", + " }", + " res.block.blockSize = blockSize;", + "", + " uint64_t alreadyGeneratedCoins = m_core.get_blockchain_storage().get_db().get_block_already_generated_coins(res.block.height);", + " if (!alreadyGeneratedCoins) {", + " return false;", + " }", + " res.block.alreadyGeneratedCoins = alreadyGeneratedCoins;", + "", + " uint64_t alreadyGeneratedTransactions = m_core.get_blockchain_storage().get_db().get_block_already_generated_transactions(res.block.height);", + " if (!alreadyGeneratedTransactions) {", + " return false;", + " }", + " // +1 for coinbase tx", + " res.block.alreadyGeneratedTransactions = alreadyGeneratedTransactions + blk.tx_hashes.size() + 1;", + "", + " uint64_t prevBlockGeneratedCoins = 0;", + " if (res.block.height > 0) {", + " prevBlockGeneratedCoins = m_core.get_blockchain_storage().get_db().get_block_already_generated_coins(res.block.height - 1);", + " if (!prevBlockGeneratedCoins) {", + " return false;", + " }", + " }", + " uint64_t maxReward = 0;", + " uint64_t realReward = 0;", + " int64_t emissionChange = 0;", + " uint64_t blockGrantedFullRewardZone = m_core.get_blockchain_storage().get_block_granted_full_reward_zone_by_block_version(block_header.major_version);", + " res.block.effectiveSizeMedian = std::max(res.block.sizeMedian, blockGrantedFullRewardZone);", + "", + " if (!cryptonote::get_block_reward(0, 0, prevBlockGeneratedCoins, maxReward, res.block.major_version)) {", + " return false;", + " }", + " if (!cryptonote::get_block_reward(res.block.sizeMedian, res.block.blockSize, prevBlockGeneratedCoins, realReward, res.block.major_version)) {", + " return false;", + " }", + "", + " // This is base_reward + tx fees", + " // uint64_t currentReward = get_block_reward(blk);", + "", + " res.block.baseReward = maxReward;", + " if (maxReward == 0 && realReward == 0) {", + " res.block.penalty = static_cast(0);", + " } else {", + " if (maxReward < realReward) {", + " return false;", + " }", + " res.block.penalty = static_cast(maxReward - realReward) / static_cast(maxReward);", + " }", + "", + " res.block.transactionsCumulativeSize = 0;", + "", + " // Base transaction adding", + " f_transaction_short_response transaction_short;", + " transaction_short.hash = string_tools::pod_to_hex(get_transaction_hash(blk.miner_tx));", + " transaction_short.fee = 0;", + " transaction_short.amount_out = get_outs_money_amount(blk.miner_tx);", + " transaction_short.size = get_object_blobsize(blk.miner_tx);", + " res.block.transactions.push_back(transaction_short);", + "", + " res.block.transactionsCumulativeSize += get_object_blobsize(blk.miner_tx);", + "", + " std::list missed_txs;", + " std::list txs;", + " m_core.get_transactions(blk.tx_hashes, txs, missed_txs);", + "", + " res.block.totalFeeAmount = 0;", + "", + " for (const transaction& tx : txs) {", + " f_transaction_short_response transaction_short;", + " uint64_t amount_in = 0;", + " get_inputs_money_amount(tx, amount_in);", + " uint64_t amount_out = get_outs_money_amount(tx);", + "", + " transaction_short.hash = string_tools::pod_to_hex(get_transaction_hash(tx));", + " transaction_short.fee = amount_in - amount_out;", + " transaction_short.amount_out = amount_out;", + " transaction_short.size = get_object_blobsize(tx);", + " res.block.transactions.push_back(transaction_short);", + "", + " res.block.transactionsCumulativeSize += get_object_blobsize(tx);", + " res.block.totalFeeAmount += transaction_short.fee;", + " }", + "", + " res.status = CORE_RPC_STATUS_OK;", + " return true;", + " }", + "", + " bool core_rpc_server::f_on_transaction_json(const F_COMMAND_RPC_GET_TRANSACTION_DETAILS::request& req, F_COMMAND_RPC_GET_TRANSACTION_DETAILS::response& res, epee::json_rpc::error& error_resp) {", + " crypto::hash hash;", + "", + " if (!parse_hash256(req.hash, hash)) {", + " error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;", + " error_resp.message = \"Failed to parse hex representation of transaction hash. Hex = \" + req.hash + '.';", + " return false;", + " }", + "", + " std::vector tx_ids;", + " tx_ids.push_back(hash);", + "", + " std::list missed_txs;", + " std::list txs;", + " m_core.get_transactions(tx_ids, txs, missed_txs);", + "", + " transaction tx;", + "", + " if (1 == txs.size()) {", + " tx = txs.front();", + " } else {", + " error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;", + " error_resp.message = \"transaction wasn't found. Hash = \" + req.hash + '.';", + " return false;", + " }", + "", + " res.tx.version = tx.version;", + " res.tx.unlock_time = tx.unlock_time;", + "", + " for (const txin_v& txin : tx.vin) {", + " if (txin.type() != typeid(txin_to_key)) {", + " continue;", + " }", + " f_tx_vin input;", + " input.amount = boost::get(txin).amount;", + " input.k_image = string_tools::pod_to_hex(boost::get(txin).k_image);", + " res.tx.vin.push_back(input);", + " }", + "", + " for (const tx_out& txout : tx.vout) {", + " f_tx_vout output;", + " output.amount = txout.amount;", + " txout_to_key tx_out_to_key = boost::get(txout.target);", + " output.key = string_tools::pod_to_hex(tx_out_to_key.key);", + " res.tx.vout.push_back(output);", + " }", + "", + " uint32_t blockHeight = m_core.get_blockchain_storage().get_db().get_tx_block_height(hash);", + " crypto::hash blockHash = m_core.get_block_id_by_height(blockHeight);", + " if (blockHeight) {", + " block blk;", + " if (m_core.get_block_by_hash(blockHash, blk)) {", + " uint64_t tx_cumulative_block_size = m_core.get_blockchain_storage().get_db().get_block_size(blockHeight);", + " uint64_t blokBlobSize = get_object_blobsize(blk);", + " uint64_t minerTxBlobSize = get_object_blobsize(blk.miner_tx);", + " f_block_short_response block_short;", + "", + " block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize;", + " block_short.timestamp = blk.timestamp;", + " block_short.height = blockHeight;", + " block_short.hash = string_tools::pod_to_hex(blockHash);", + " block_short.tx_count = blk.tx_hashes.size() + 1;", + " res.block = block_short;", + " }", + " }", + "", + " uint64_t amount_in = 0;", + " get_inputs_money_amount(tx, amount_in);", + " uint64_t amount_out = get_outs_money_amount(tx);", + "", + " crypto::hash tx_hash = get_transaction_hash(tx);", + " res.txDetails.hash = string_tools::pod_to_hex(tx_hash);", + " res.txDetails.fee = amount_in - amount_out;", + " if (amount_in == 0)", + " res.txDetails.fee = 0;", + " res.txDetails.amount_out = amount_out;", + " res.txDetails.size = get_object_blobsize(tx);", + "", + " uint64_t mixin;", + " if (!f_getMixin(tx, mixin)) {", + " return false;", + " }", + " res.txDetails.mixin = mixin;", + "", + " std::vector tx_extra_fields;", + " parse_tx_extra(tx.extra, tx_extra_fields);", + "", + " tx_extra_nonce extra_nonce;", + " crypto::hash paymentId;", + "", + " // EDIT THIS PART FOR ENCRYPTED PAYMENT_ID", + " if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) {", + " if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentId)) {", + " res.txDetails.paymentId = string_tools::pod_to_hex(paymentId);", + " } else {", + " res.txDetails.paymentId = \"\";", + " }", + " }", + "", + " res.status = CORE_RPC_STATUS_OK;", + " return true;", + " }", + "", + " bool core_rpc_server::f_on_transactions_pool_json(const F_COMMAND_RPC_GET_POOL::request& req, F_COMMAND_RPC_GET_POOL::response& res, epee::json_rpc::error& error_resp) {", + " // res.transactions = m_core.print_pool(true);", + " res.status = CORE_RPC_STATUS_OK;", + " return true;", + " }", + "", + " bool core_rpc_server::f_getMixin(const transaction& transaction, uint64_t& mixin) {", + " mixin = 0;", + " for (const txin_v& txin : transaction.vin) {", + " if (txin.type() != typeid(txin_to_key)) {", + " continue;", + " }", + " uint64_t currentMixin = boost::get(txin).key_offsets.size();", + " if (currentMixin > mixin) {", + " mixin = currentMixin;", + " }", + " }", + " return true;", + " }", + "" + ] + } + } + ] + }, + { + "path": "/src/rpc/core_rpc_server_commands_defs.h", + "changes": [ + { + "action": "add_above", + "marker": "struct COMMAND_RPC_GET_HEIGHT", + "parameters": { + "text": [ + "", + " struct f_transaction_short_response {", + " std::string hash;", + " uint64_t fee;", + " uint64_t amount_out;", + " uint64_t size;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(hash)", + " KV_SERIALIZE(fee)", + " KV_SERIALIZE(amount_out)", + " KV_SERIALIZE(size)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct f_tx_vin", + " {", + " uint64_t amount;", + " std::string k_image;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(amount)", + " KV_SERIALIZE(k_image)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct f_tx_vout", + " {", + " uint64_t amount;", + " std::string key;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(amount)", + " KV_SERIALIZE(key)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct f_transaction {", + " uint64_t version;", + " uint64_t unlock_time;", + " std::vector vin;", + " std::vector vout;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(version)", + " KV_SERIALIZE(unlock_time)", + " KV_SERIALIZE(vin)", + " KV_SERIALIZE(vout)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct f_transaction_details_response {", + " std::string hash;", + " uint64_t size;", + " std::string paymentId;", + " uint64_t mixin;", + " uint64_t fee;", + " uint64_t amount_out;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(hash)", + " KV_SERIALIZE(size)", + " KV_SERIALIZE(paymentId)", + " KV_SERIALIZE(mixin)", + " KV_SERIALIZE(fee)", + " KV_SERIALIZE(amount_out)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct f_block_short_response {", + " uint64_t timestamp;", + " uint32_t height;", + " std::string hash;", + " uint64_t tx_count;", + " uint64_t cumul_size;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(timestamp)", + " KV_SERIALIZE(height)", + " KV_SERIALIZE(hash)", + " KV_SERIALIZE(cumul_size)", + " KV_SERIALIZE(tx_count)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct f_block_details_response {", + " uint8_t major_version;", + " uint8_t minor_version; ", + " uint64_t timestamp;", + " std::string prev_hash;", + " uint32_t nonce;", + " bool orphan_status;", + " uint32_t height;", + " uint64_t depth;", + " std::string hash;", + " uint64_t difficulty;", + " uint64_t reward;", + " uint64_t blockSize;", + " uint64_t sizeMedian;", + " uint64_t effectiveSizeMedian;", + " uint64_t transactionsCumulativeSize;", + " uint64_t alreadyGeneratedCoins;", + " uint64_t alreadyGeneratedTransactions;", + " uint64_t baseReward;", + " double penalty;", + " uint64_t totalFeeAmount;", + " std::vector transactions;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(major_version)", + " KV_SERIALIZE(minor_version)", + " KV_SERIALIZE(timestamp)", + " KV_SERIALIZE(prev_hash)", + " KV_SERIALIZE(nonce)", + " KV_SERIALIZE(orphan_status)", + " KV_SERIALIZE(height)", + " KV_SERIALIZE(depth)", + " KV_SERIALIZE(hash)", + " KV_SERIALIZE(difficulty)", + " KV_SERIALIZE(reward)", + " KV_SERIALIZE(blockSize)", + " KV_SERIALIZE(sizeMedian)", + " KV_SERIALIZE(effectiveSizeMedian)", + " KV_SERIALIZE(transactionsCumulativeSize)", + " KV_SERIALIZE(alreadyGeneratedCoins)", + " KV_SERIALIZE(alreadyGeneratedTransactions)", + " KV_SERIALIZE(baseReward)", + " KV_SERIALIZE(penalty)", + " KV_SERIALIZE(transactions)", + " KV_SERIALIZE(totalFeeAmount)", + " END_KV_SERIALIZE_MAP()", + " };", + "" + ] + } + }, + { + "action": "add_above", + "marker": "struct COMMAND_RPC_GET_VERSION", + "parameters": { + "text": [ + "struct F_COMMAND_RPC_GET_BLOCKS_LIST {", + " struct request {", + " uint64_t height;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(height)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct response {", + " std::vector blocks; //transactions blobs as hex", + " std::string status;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(blocks)", + " KV_SERIALIZE(status)", + " END_KV_SERIALIZE_MAP()", + " };", + "};", + "", + "struct F_COMMAND_RPC_GET_BLOCK_DETAILS {", + " struct request {", + " std::string hash;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(hash)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct response {", + " f_block_details_response block;", + " std::string status;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(block)", + " KV_SERIALIZE(status)", + " END_KV_SERIALIZE_MAP()", + " };", + "};", + "", + "struct F_COMMAND_RPC_GET_TRANSACTION_DETAILS {", + " struct request {", + " std::string hash;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(hash)", + " END_KV_SERIALIZE_MAP()", + " };", + "", + " struct response {", + " f_transaction tx;", + " f_transaction_details_response txDetails;", + " f_block_short_response block;", + " std::string status;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(tx)", + " KV_SERIALIZE(txDetails)", + " KV_SERIALIZE(block)", + " KV_SERIALIZE(status)", + " END_KV_SERIALIZE_MAP()", + " };", + "};", + "", + "struct F_COMMAND_RPC_GET_POOL {", + " typedef std::vector request;", + "", + " struct response {", + " std::vector transactions; //transactions blobs as hex", + " std::string status;", + "", + " BEGIN_KV_SERIALIZE_MAP()", + " KV_SERIALIZE(transactions)", + " KV_SERIALIZE(status)", + " END_KV_SERIALIZE_MAP()", + " };", + "};", + "" + ] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/extensions/core/monero.json b/extensions/core/monero.json new file mode 100644 index 0000000..3e2f614 --- /dev/null +++ b/extensions/core/monero.json @@ -0,0 +1,7 @@ +{ + "file": "core/monero.json", + "description": "Makes essensial parameters editable.", + "required": [], + "files": [ + ] +} diff --git a/generator.sh b/generator.sh new file mode 100644 index 0000000..085137c --- /dev/null +++ b/generator.sh @@ -0,0 +1,141 @@ +#! /usr/bin/env bash + + +# Bash script for automatic generation and deployment + +# Exit immediately if an error occurs, or if an undeclared variable is used +set -o errexit + +[ "$OSTYPE" != "win"* ] || die "Install MinGW to use on Windows" + +# For bold text +if [ "$OSTYPE" != "msys" ]; then + bold=$(tput bold) + normal=$(tput sgr0) +fi + +# Set directory vars +. "vars.cfg" + +# Perform cleanup on exit +function finish { + # Remove temporary files if exist + echo "Remove temporary files..." + rm -f "${UPDATES_PATH}" + rm -f "${BASH_CONFIG}" + rm -rf "${TEMP_PATH}" +} +trap finish EXIT + +# Generate source code and compile +function generate_coin { + # Define coin paths + export BASE_COIN_PATH="${WORK_FOLDERS_PATH}/${__CONFIG_base_coin_name}" + export NEW_COIN_PATH="${WORK_FOLDERS_PATH}/${__CONFIG_core_CRYPTONOTE_NAME}" + if [ -d "${BASE_COIN_PATH}" ]; then + cd "${BASE_COIN_PATH}" + echo "Updating ${__CONFIG_base_coin_name}..." + git pull + cd "${PROJECT_DIR}" + else + echo "Cloning ${__CONFIG_base_coin_name}..." + git clone "${__CONFIG_base_coin_git}" "${BASE_COIN_PATH}" + fi + + # Exit if base coin does not exists + if [ ! -d "${BASE_COIN_PATH}" ]; then + echo "Base coin does not exists" + echo "Abort clone generation" + exit 4 + fi + + echo "Make temporary ${__CONFIG_base_coin_name} copy..." + [ -d "${TEMP_PATH}" ] || mkdir -p "${TEMP_PATH}" + cp -af "${BASE_COIN_PATH}/." "${TEMP_PATH}" + + # Extensions + echo "Personalize base coin source..." + export __CONFIG_extensions_text="${__CONFIG_extensions[@]}" + for extension in "${__CONFIG_extensions[@]}" + do + echo "${bold}Execute ${EXTENSIONS_PATH}/${extension}${normal}" + python "lib/file-modification.py" --extension "${EXTENSIONS_PATH}/${extension}" --config=$CONFIG_FILE --source=${TEMP_PATH} + done + + [ -d "${NEW_COIN_PATH}" ] || mkdir -p "${NEW_COIN_PATH}" + + echo "Create patch" + cd "${WORK_FOLDERS_PATH}"; + EXCLUDE_FROM_DIFF="-x '.git'" + if [ -d "${BASE_COIN_PATH}/build" ]; then + EXCLUDE_FROM_DIFF="${EXCLUDE_FROM_DIFF} -x 'build'" + fi + diff -Naur -x .git ${NEW_COIN_PATH##${WORK_FOLDERS_PATH}/} ${TEMP_PATH##${WORK_FOLDERS_PATH}/} > "${UPDATES_PATH}" || [ $? -eq 1 ] + + echo "Apply patch" + [ -d "${NEW_COIN_PATH}" ] || mkdir -p "${NEW_COIN_PATH}" + if [ ! -z "${UPDATES_PATH}" ]; then + # Generate new coin + cd "${NEW_COIN_PATH}" && patch -s -p1 < "${UPDATES_PATH}" && cd "${SCRIPTS_PATH}" + chmod -R 755 ${NEW_COIN_PATH} + + bash "${SCRIPTS_PATH}/compile.sh" -c "${COMPILE_ARGS}" -z + fi +} + +# Usage info +show_help() { +cat << EOF +Usage: ${0##*/} [-h] [-f FILE] [-c ] +Reads a config file and creates and compiles Cryptonote coin. "config.json" as default + + -h display this help and exit + -f config file + -c compile arguments +EOF +} + +# A POSIX variable +OPTIND=1 # Reset in case getopts has been used previously in the shell. + +# Initialize our own variables: +CONFIG_FILE='config.json' +COMPILE_ARGS='' + +while getopts "h?f:c:" opt; do + case "$opt" in + h|\?) + show_help + exit 0 + ;; + f) CONFIG_FILE=${OPTARG} + ;; + c) COMPILE_ARGS=${OPTARG} + ;; + esac +done + +shift $((OPTIND-1)) + +# Setting config file +if [[ "${CONFIG_FILE}" != /* ]]; then + CONFIG_FILE="${CONFIG_PATH}/${CONFIG_FILE}" +fi + +if [ ! -f ${CONFIG_FILE} ]; then + echo "ERROR: config file does not exits" + exit +fi + +[ -d "${WORK_FOLDERS_PATH}" ] || mkdir -p "${WORK_FOLDERS_PATH}" + +# Get environment environment_variables +python "lib/environment_variables.py" --config=$CONFIG_FILE --output=$BASH_CONFIG +if [ ! -f ${BASH_CONFIG} ]; then + echo "Config file was not translated to bash." + echo "Abort coin generation" + exit 3 +fi +source ${BASH_CONFIG} + +generate_coin diff --git a/install_dependencies.sh b/install_dependencies.sh new file mode 100644 index 0000000..1134b44 --- /dev/null +++ b/install_dependencies.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +# Template: OpenBazaar +# +# install_dependencies.sh - Setup your Cryptonote development environment in one step. +# +# This script will only get better as its tested on more development environments +# if you can't modify it to make it better, please open an issue with a full +# error report at https://github.com/forknote/monero-generator.git/issues/new +# +# Credits: Forknote +# +# Code borrowed from: +# https://github.com/OpenBazaar/OpenBazaar/blob/develop/configure.sh +# https://github.com/Quanttek/install_monero/blob/master/install_monero.sh + +#exit on error +set -e + +function command_exists { + # this should be a very portable way of checking if something is on the path + # usage: "if command_exists foo; then echo it exists; fi" + type "$1" &> /dev/null +} + +function brewDoctor { + if ! brew doctor; then + echo "" + echo "'brew doctor' did not exit cleanly! This may be okay. Read above." + echo "" + read -p "Press [Enter] to continue anyway or [ctrl + c] to exit and do what the doctor says..." + fi +} + +function brewUpgrade { + echo "If your homebrew packages are outdated, we recommend upgrading them now. This may take some time." + read -r -p "Do you want to do this? [y/N] " response + if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]] + then + if ! brew upgrade; then + echo "" + echo "There were errors when attempting to 'brew upgrade' and there could be issues with the installation of Cryptonote generator." + echo "" + read -p "Press [Enter] to continue anyway or [ctrl + c] to exit and fix those errors." + fi + fi +} + +function installMac { + # print commands (useful for debugging) + # set -x #disabled because the echos and stdout are verbose enough to see progress + + # install brew if it is not installed, otherwise upgrade it + if ! command_exists brew ; then + echo "installing brew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + else + echo "updating, upgrading, checking brew..." + brew update + brewDoctor + brewUpgrade + brew prune + fi + + # install gpg/sqlite3/python/wget/openssl/zmq if they aren't installed + for dep in cmake boost python + do + if ! command_exists $dep ; then + brew install $dep + fi + done + + doneMessage +} + +function unsupportedOS { + echo "Unsupported OS. Only MacOSX and Ubuntu are supported." +} + +function installUbuntu { + . /etc/lsb-release + + # print commands + set -x + + if [[ $DISTRIB_RELEASE == 16* ]] ; then + sudo apt-get update + sudo apt-get -y install build-essential python-dev gcc-4.9 g++-4.9 git cmake libboost1.58-all-dev librocksdb-dev + export CXXFLAGS="-std=gnu++11" + + doneMessage + else if [[ $DISTRIB_RELEASE == 14* ]] ; then + sudo apt-get update + sudo apt-get -y install build-essential python-dev gcc-4.8 g++-4.8 git cmake libboost1.55-all-dev + + doneMessage + else + echo "Only Ubuntu 16.04 is supported" + fi +} + +function doneMessage { + echo "Cryptonote generator configuration finished." + echo "type 'bash generator.sh [-h] [-f FILE] [-c ]' to generate Cryptonote coin." +} + +if [[ $OSTYPE == darwin* ]] ; then + installMac +elif [[ $OSTYPE == linux-gnu || $OSTYPE == linux-gnueabihf ]]; then + installUbuntu +else + unsupportedOS +fi \ No newline at end of file diff --git a/lib/environment_variables.py b/lib/environment_variables.py new file mode 100644 index 0000000..41ca314 --- /dev/null +++ b/lib/environment_variables.py @@ -0,0 +1,38 @@ +import json +import argparse +import sys + + +def convert_to_bash ( entity, prefix ): + for key, value in entity.iteritems(): + if isinstance(value, dict): + convert_to_bash(entity[key], prefix + key + '_') + elif isinstance(value, list): + print "export " + str(prefix) + str(key) + "=(" + " ".join(json.dumps(str(item)) for item in value) + ")" + elif isinstance(value, int): + print "export " + str(prefix) + str(key) + "=" + str(value) + else: + print "export " + str(prefix) + str(key) + "=" + str(json.dumps(value)) + "" + +parser = argparse.ArgumentParser() + +parser.add_argument('--config', action='store', dest='config_file', + default='config.json', + help='Configuration filename. Format: json' + ) +parser.add_argument('--output', action='store', dest='output', + default='config.cfg', + help='Output path. Format: string' + ) + +args = parser.parse_args() + +json_data=open(args.config_file) +config = json.load(json_data) +json_data.close() + +f = open(args.output, 'w+') +sys.stdout = f +convert_to_bash(config, '__CONFIG_') +sys.stdout = sys.__stdout__ +f.close() diff --git a/lib/file-modification.py b/lib/file-modification.py new file mode 100644 index 0000000..102a9bd --- /dev/null +++ b/lib/file-modification.py @@ -0,0 +1,215 @@ +import argparse +import fileinput +import itertools +import json +import os +import re +import shutil +import sys + + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + +def reverse_enumerate(iterable): + """ + Enumerate over an iterable in reverse order while retaining proper indexes + """ + return itertools.izip(reversed(xrange(len(iterable))), reversed(iterable)) + + +def text_creator(change): + replace_text = '' + if 'var' in change['parameters'].keys(): + if change['parameters']['var'] in config['core'].keys(): + aggregated_var = "" + if 'separator' in change['parameters'].keys(): + for element in config['core'][change['parameters']['var']]: + if 'quote_it' in change['parameters'].keys() and change['parameters']['quote_it'] == True: + aggregated_var += "\"" + element + "\"" + else: + aggregated_var += element + aggregated_var += change['parameters']['separator'] + aggregated_var = aggregated_var[:-len(change['parameters']['separator'])] + else: + aggregated_var = config['core'][change['parameters']['var']] + replace_text = "\n".join(change['parameters']['text']).replace("%s", str(aggregated_var)) + "\n" + elif 'replace_text_alt' in change['parameters'].keys(): + replace_text = '\n'.join(change['parameters']['replace_text_alt']) + "\n" + elif 'mandatory' in change['parameters'].keys() and change['parameters']['mandatory'] == False: + sys.__stdout__.write("NONE\n") + return None + else: + sys.__stdout__.write(bcolors.FAIL + "ERROR: variable is not in the config file - " + change['parameters']['var']+ bcolors.ENDC + "\n") + sys.exit(2) + else: + replace_text = '\n'.join(change['parameters']['text']) + "\n" + + # sys.__stdout__.write("Replace text: " + replace_text + "\n") + return replace_text + +parser = argparse.ArgumentParser() +parser.add_argument('--extension', action='store', dest='extension_file', + help='Extension filename. Format: json' + ) +parser.add_argument('--config', action='store', dest='config_file', + default='config.json', + help='Configuration filename. Format: json' + ) +parser.add_argument('--source', action='store', dest='source', + default='tmp', + help='Working folder containing the base coin source' + ) +args = parser.parse_args() + +try: + extension_json_data = open(args.extension_file) +except IOError as e: + print "I/O error({0}): {1} - {2}".format(e.errno, e.strerror, args.extension_file) + sys.exit(2) +else: + try: + extension = json.load(extension_json_data) + except ValueError as e: + print "Not a valid JSON file: " + args.extension_file + sys.exit(3) + else: + extension_json_data.close() + +try: + config_json_data = open(args.config_file) +except IOError as e: + print "I/O error({0}): {1} - {2}".format(e.errno, e.strerror, args.config_file) + sys.exit(2) +else: + try: + config = json.load(config_json_data) + except ValueError as e: + print "Not a valid JSON file: " + args.config_file + sys.exit(3) + else: + config_json_data.close() + +required_extensions = [i for i in extension['required']] +loaded_extensions = config['extensions'][:(config['extensions'].index(extension['file']))] + +if not (set(required_extensions) <= set(loaded_extensions)): + print bcolors.FAIL + "ERROR: Required extension not loaded: required" + str(required_extensions) + " loaded" + str(loaded_extensions) + bcolors.ENDC + "\n" + exit(3) + + +for file in extension['files']: + # Add new file to output source + if 'action' in file.keys() and 'source' in file.keys() and file['action'] == 'add': + source_path = os.path.dirname(os.path.realpath(args.extension_file)) + file['source'] + if (os.path.isfile(source_path)): + sys.__stdout__.write("- Adding file " + source_path + "\n") + shutil.copyfile(source_path,args.source + file['path']) + else: + sys.__stdout__.write(bcolors.FAIL + "ERROR: file does not exists - " + source_path + bcolors.ENDC + "\n") + exit(4) + continue + + # If multiline, get the whole file + if 'multiline' in file.keys() and file['multiline'] == True: + work_file = open(args.source + file['path'], 'r') + work_file_content = work_file.read() + work_file.close() + + # Start pre-tests + sys.__stdout__.write("- Making pre-tests in file " + args.source + file['path'] + "\n") + changes = list(file['changes']) + for index, change in reverse_enumerate(changes): + TEMPLATE_re = re.compile(r"%s" % change['marker'], re.MULTILINE) + match = TEMPLATE_re.search(work_file_content) + if match: + sys.__stdout__.write(" + Pretest: marker exists - " + change['marker'] + "\n") + del changes[index] + + for index, change in reverse_enumerate(changes): + if 'may_not_exist' in change and change['may_not_exist']: + sys.__stdout__.write(bcolors.WARNING + "WARNING: marker not found - " + change['marker'] + bcolors.ENDC + "\n") + del changes[index] + + for change in changes: + sys.__stdout__.write(bcolors.FAIL + "ERROR: marker not found - " + change['marker'] + bcolors.ENDC + "\n") + sys.exit(2) + # End pre-tests + + sys.__stdout__.write("- Making changes in file " + args.source + file['path'] + "\n") + changes = list(file['changes']) + for change in changes: + sys.__stdout__.write(" + Replaced text at marker: " + change['marker'] + "\n") + replace_text = text_creator(change) + if replace_text is not None: + TEMPLATE_re = re.compile(r"%s" % change['marker'], re.DOTALL) + work_file_content = TEMPLATE_re.sub(replace_text, work_file_content) + "\n" + + work_file = open(args.source + file['path'], "w") + work_file.write(work_file_content) + work_file.close() + # If not multiline - check line by line + else: + # Start pre-tests + sys.__stdout__.write("- Making pre-tests in file " + args.source + file['path'] + "\n") + changes = list(file['changes']) + for line in fileinput.input([args.source + file['path']], inplace=True): + for index, change in reverse_enumerate(changes): + if change['marker'] in line: + sys.__stdout__.write(" + Pretest: marker exists - " + change['marker'] + "\n") + del changes[index] + sys.stdout.write(line) + + for index, change in reverse_enumerate(changes): + if 'may_not_exist' in change and change['may_not_exist']: + sys.__stdout__.write(bcolors.WARNING + "WARNING: marker not found - " + change['marker'] + bcolors.ENDC + "\n") + del changes[index] + + for change in changes: + sys.__stdout__.write(bcolors.FAIL + "ERROR: marker not found - " + change['marker'] + bcolors.ENDC + "\n") + sys.exit(2) + # End pre-tests + + + sys.__stdout__.write("- Making changes in file " + args.source + file['path'] + "\n") + changes = list(file['changes']) + for line in fileinput.input([args.source + file['path']], inplace=True): + add_after_line = "" + delete_line = False + for change in changes: + if change['action'] == "replace": + if change['marker'] in line: + sys.__stdout__.write(" + Replacing line at marker: " + change['marker'] + "\n") + replace_text = text_creator(change) + if replace_text is not None: + line = replace_text + if change['action'] == "add_above": + if change['marker'] in line: + sys.__stdout__.write(" + Added text before marker: " + change['marker'] + "\n") + replace_text = text_creator(change) + if replace_text is not None: + add_before_line = replace_text + sys.stdout.write(replace_text) + if change['action'] == "delete": + if change['marker'] in line: + sys.__stdout__.write(" + Delete line at marker: " + change['marker'] + "\n") + delete_line = True + if change['action'] == "add_bellow": + if change['marker'] in line: + sys.__stdout__.write(" + Added text after marker: " + change['marker'] + "\n") + replace_text = text_creator(change) + if replace_text is not None: + add_after_line = replace_text + + if delete_line != True: + sys.stdout.write(line) + if add_after_line != "": + sys.stdout.write(add_after_line) diff --git a/vars.cfg b/vars.cfg new file mode 100644 index 0000000..17618a2 --- /dev/null +++ b/vars.cfg @@ -0,0 +1,13 @@ +export PROJECT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export WORK_FOLDERS_PATH="${PROJECT_DIR}/generated_files" +export SCRIPTS_PATH="${PROJECT_DIR}" +export EXTENSIONS_PATH="${SCRIPTS_PATH}/extensions" +export TESTS_PATH="${SCRIPTS_PATH}/tests" +export CONFIG_PATH="${SCRIPTS_PATH}" +export TEMP_PATH="${WORK_FOLDERS_PATH}/tmp" +export UPDATES_PATH="${WORK_FOLDERS_PATH}/updates.dashcoin.patch" +export BASH_CONFIG="${WORK_FOLDERS_PATH}/config.cfg" + +export CUSTOM_CUSTOMIZE_SCRIPT_PATH="${SCRIPTS_PATH}/custom/customize.sh" +export CUSTOM_CUSTOMIZE_TESTS_PATH="${SCRIPTS_PATH}/custom/customize-test.sh" +export CUSTOM_GENERATE_SCRIPT_PATH="${SCRIPTS_PATH}/custom/generate.sh"