diff --git a/CMakeLists.txt b/CMakeLists.txt index 0471054df..f8e60f19f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,11 +45,11 @@ ENDIF() IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${COMMON_ARGS}") #libbitcoin has too many ignored-qualifiers, and TODOs - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 ${COMMON_ARGS} -pthread -fno-enforce-eh-specs -fnothrow-opt -Wno-reorder -Wno-ignored-qualifiers -Wno-unused-function -Wno-unused-but-set-variable -Wno-sign-compare -Wno-unused-but-set-parameter") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 ${COMMON_ARGS} -pthread -fno-enforce-eh-specs -fnothrow-opt -Wno-reorder -Wno-ignored-qualifiers -Wno-unused-function -Wno-unused-but-set-variable -Wno-sign-compare -Wno-unused-but-set-parameter -Wno-implicit-fallthrough") ELSEIF("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") SET(CMAKE_C_FLAGS "-std=c11 ${COMMON_ARGS}") - SET(CMAKE_CXX_FLAGS "-std=c++14 ${COMMON_ARGS} -Wno-reorder -Wno-ignored-qualifiers -Wno-inconsistent-missing-override -Wno-missing-braces -Wno-mismatched-tags -Wno-overloaded-virtual -Wno-sometimes-uninitialized -Wno-macro-redefined -Wno-uninitialized -Wno-unused-private-field -Wno-unused-function") + SET(CMAKE_CXX_FLAGS "-std=c++14 ${COMMON_ARGS} -Wno-reorder -Wno-ignored-qualifiers -Wno-inconsistent-missing-override -Wno-missing-braces -Wno-mismatched-tags -Wno-overloaded-virtual -Wno-sometimes-uninitialized -Wno-macro-redefined -Wno-uninitialized -Wno-unused-private-field -Wno-unused-function -Wno-implicit-fallthrough") ENDIF() SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC") diff --git a/builds/msvc-140/bitcoin/bitcoin.vcxproj b/builds/msvc-140/bitcoin/bitcoin.vcxproj index 8b861e7cf..ade9b8d18 100644 --- a/builds/msvc-140/bitcoin/bitcoin.vcxproj +++ b/builds/msvc-140/bitcoin/bitcoin.vcxproj @@ -83,8 +83,6 @@ - - @@ -233,8 +231,6 @@ - - diff --git a/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters b/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters index f86c370d5..d790a3178 100644 --- a/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters +++ b/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters @@ -395,12 +395,6 @@ Source Files\message - - Source Files\message - - - Source Files\message - Source Files\message @@ -823,9 +817,6 @@ Header Files\math - - Header Files\message - Header Files\message @@ -919,9 +910,6 @@ Header Files\message - - Header Files\message - Header Files\unicode diff --git a/builds/msvc/vs2015/metaverse.vcxproj b/builds/msvc/vs2015/metaverse.vcxproj index 9dab7c353..e68b60c70 100644 --- a/builds/msvc/vs2015/metaverse.vcxproj +++ b/builds/msvc/vs2015/metaverse.vcxproj @@ -509,8 +509,6 @@ - - @@ -1080,13 +1078,11 @@ - - diff --git a/builds/msvc/vs2015/metaverse.vcxproj.filters b/builds/msvc/vs2015/metaverse.vcxproj.filters index 0ffa99291..661489065 100644 --- a/builds/msvc/vs2015/metaverse.vcxproj.filters +++ b/builds/msvc/vs2015/metaverse.vcxproj.filters @@ -1176,12 +1176,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -2900,9 +2894,6 @@ Header Files - - Header Files - Header Files @@ -2918,9 +2909,6 @@ Header Files - - Header Files - Header Files diff --git a/contrib/jsoncpp/jsoncpp.cpp b/contrib/jsoncpp/jsoncpp.cpp index a85e280ed..16caaf933 100644 --- a/contrib/jsoncpp/jsoncpp.cpp +++ b/contrib/jsoncpp/jsoncpp.cpp @@ -6,28 +6,28 @@ // ////////////////////////////////////////////////////////////////////// /* -The JsonCpp library's source code, including accompanying documentation, +The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... -Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and The JsonCpp Authors, and is released under the terms of the MIT License (see below). -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License - + The full text of the MIT License follows: ======================================================================== @@ -238,7 +238,7 @@ static inline void fixNumericLocaleInput(char* begin, char* end) { #include #if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above #define snprintf sprintf_s #elif _MSC_VER >= 1900 // VC++ 14.0 and above #define snprintf std::snprintf @@ -382,7 +382,7 @@ bool Reader::parse(const char* beginDoc, bool Reader::readValue() { // readValue() may call itself only if it calls readObject() or ReadArray(). - // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). + // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). // parse() executes one nodes_.push(), so > instead of >=. if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); @@ -3549,9 +3549,13 @@ Value& Value::resolveReference(const char* key) { // @param key is not null-terminated. Value& Value::resolveReference(char const* key, char const* cend) { + std::string info("in Json::Value::resolveReference(key, end): requires objectValue. "); + info += "key: "; info += key; + info += ", cend: "; info += cend; + JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, - "in Json::Value::resolveReference(key, end): requires objectValue"); + info); if (type_ == nullValue) *this = Value(objectValue); CZString actualKey( @@ -4214,7 +4218,7 @@ Value& Path::make(Value& root) const { #endif #endif -#if defined(__BORLANDC__) +#if defined(__BORLANDC__) #include #define isfinite _finite #define snprintf _snprintf @@ -5288,7 +5292,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); bool eyc = settings_["enableYAMLCompatibility"].asBool(); bool dnp = settings_["dropNullPlaceholders"].asBool(); - bool usf = settings_["useSpecialFloats"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); unsigned int pre = settings_["precision"].asUInt(); CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { diff --git a/include/metaverse/bitcoin.hpp b/include/metaverse/bitcoin.hpp index 581bbf589..be431c6da 100644 --- a/include/metaverse/bitcoin.hpp +++ b/include/metaverse/bitcoin.hpp @@ -63,8 +63,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/include/metaverse/bitcoin/chain/attachment/account/account.hpp b/include/metaverse/bitcoin/chain/attachment/account/account.hpp index 599e464f0..2f238499f 100644 --- a/include/metaverse/bitcoin/chain/attachment/account/account.hpp +++ b/include/metaverse/bitcoin/chain/attachment/account/account.hpp @@ -181,12 +181,6 @@ class BC_API account uint8_t get_priority() const; void set_priority(const uint8_t priority); - void set_user_status(const uint8_t status); - uint8_t get_user_status() const; - - void set_system_status(const uint8_t status); - uint8_t get_system_status() const; - void set_type(uint8_t type); uint8_t get_type() const; diff --git a/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp b/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp index 2d98fd563..a71e29d2f 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp @@ -120,7 +120,7 @@ class BC_API asset_cert static std::string get_domain(const std::string& symbol); static bool is_valid_domain(const std::string& domain); - static std::string get_key(const std::string&symbol, asset_cert_type bit); + static std::string get_key(const std::string&symbol, const asset_cert_type& bit); private: // NOTICE: ref CAssetCert in transaction.h diff --git a/include/metaverse/bitcoin/chain/output.hpp b/include/metaverse/bitcoin/chain/output.hpp index 0145bfb3a..410997977 100644 --- a/include/metaverse/bitcoin/chain/output.hpp +++ b/include/metaverse/bitcoin/chain/output.hpp @@ -64,7 +64,6 @@ class BC_API output std::string to_string(uint32_t flags) const; bool is_valid() const; code check_attachment_address(bc::blockchain::block_chain_impl& chain) const; - code check_attachment_did_match_address(bc::blockchain::block_chain_impl& chain) const; std::string get_script_address() const; void reset(); uint64_t serialized_size() const; diff --git a/include/metaverse/bitcoin/constants.hpp b/include/metaverse/bitcoin/constants.hpp index 578dbd134..19ca77991 100644 --- a/include/metaverse/bitcoin/constants.hpp +++ b/include/metaverse/bitcoin/constants.hpp @@ -56,6 +56,10 @@ BC_CONSTEXPR uint32_t max_input_sequence = max_uint32; BC_CONSTEXPR uint32_t total_reward = 100000000; +BC_CONSTEXPR uint64_t min_fee_to_issue_asset = 10 * 100000000; +BC_CONSTEXPR uint64_t min_fee_to_register_did = 1 * 100000000; +BC_CONSTEXPR uint32_t min_fee_percentage_to_miner = 20; + // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. [Tue Nov 5 00:53:20 1985 UTC] BC_CONSTEXPR uint32_t locktime_threshold = 500000000; @@ -117,6 +121,8 @@ BC_CONSTEXPR message::network_address unspecified_network_address // TODO: make static. BC_API hash_number max_target(); +BC_API std::string get_developer_community_address(bool is_testnet); + } // namespace libbitcoin #endif diff --git a/include/metaverse/bitcoin/messages.hpp b/include/metaverse/bitcoin/messages.hpp index 7a7e08591..5e4d6a7b1 100644 --- a/include/metaverse/bitcoin/messages.hpp +++ b/include/metaverse/bitcoin/messages.hpp @@ -23,8 +23,6 @@ #include #include -#include -#include #include #include #include diff --git a/include/metaverse/bitcoin/version.hpp b/include/metaverse/bitcoin/version.hpp index d1e948a9a..1acfb06e2 100644 --- a/include/metaverse/bitcoin/version.hpp +++ b/include/metaverse/bitcoin/version.hpp @@ -12,9 +12,9 @@ * For interpretation of the versioning scheme see: http://semver.org */ -#define MVS_VERSION "0.8.1" +#define MVS_VERSION "0.8.2" #define MVS_MAJOR_VERSION 0 #define MVS_MINOR_VERSION 8 -#define MVS_PATCH_VERSION 1 +#define MVS_PATCH_VERSION 2 #endif diff --git a/include/metaverse/blockchain/block_chain_impl.hpp b/include/metaverse/blockchain/block_chain_impl.hpp index 8dec047a5..3f9548294 100644 --- a/include/metaverse/blockchain/block_chain_impl.hpp +++ b/include/metaverse/blockchain/block_chain_impl.hpp @@ -229,10 +229,6 @@ class BCB_API block_chain_impl operation_result store_account(std::shared_ptr acc); std::shared_ptr get_account(const std::string& name); std::shared_ptr> get_accounts(); - account_status get_account_user_status(const std::string& name); - account_status get_account_system_status(const std::string& name); - bool set_account_user_status(const std::string& name, uint8_t status); - bool set_account_system_status(const std::string& name, uint8_t status); operation_result delete_account(const std::string& name); operation_result delete_account_address(const std::string& name); @@ -257,8 +253,9 @@ class BCB_API block_chain_impl uint64_t get_account_asset_volume(const std::string& account, const std::string& asset); uint64_t get_asset_volume(const std::string& asset); + // asset api bool is_asset_exist(const std::string& asset_name, bool check_local_db=true); - bool get_asset_height(const std::string& asset_name, uint64_t& height); + uint64_t get_asset_height(const std::string& asset_name) const ; std::shared_ptr get_local_assets(); std::shared_ptr get_issued_assets(); std::shared_ptr get_issued_asset(const std::string& symbol); @@ -269,6 +266,7 @@ class BCB_API block_chain_impl // cert api bool is_asset_cert_exist(const std::string& symbol, asset_cert_type cert_type); + uint64_t get_asset_cert_height(const std::string& cert_symbol,const asset_cert_type& cert_type); std::shared_ptr get_issued_asset_certs(); std::shared_ptr get_account_asset_cert( const std::string& account, const std::string& symbol, asset_cert_type cert_type); @@ -278,6 +276,8 @@ class BCB_API block_chain_impl const std::string& address, const std::string& symbol, asset_cert_type cert_type); // identifiable asset + bool is_asset_mit_exist(const std::string& symbol); + uint64_t get_asset_mit_height(const std::string& mit_symbol)const; std::shared_ptr get_registered_mit(const std::string& symbol); std::shared_ptr get_registered_mits(); std::shared_ptr get_mit_history(const std::string& symbol, @@ -287,10 +287,10 @@ class BCB_API block_chain_impl // account did api bool is_did_exist(const std::string& symbol); - bool get_did_height(const std::string& symbol, uint64_t& height); - bool is_address_registered_did(const std::string& address); + uint64_t get_did_height(const std::string& symbol) const; + bool is_address_registered_did(const std::string& address, uint64_t fork_index = max_uint64); bool is_account_owned_did(const std::string& account, const std::string& symbol); - std::string get_did_from_address(const std::string& address); + std::string get_did_from_address(const std::string& address, uint64_t fork_index = max_uint64); std::shared_ptr get_registered_did(const std::string& symbol); std::shared_ptr get_registered_dids(); std::shared_ptr get_account_dids(const std::string& account); @@ -320,11 +320,11 @@ class BCB_API block_chain_impl std::shared_ptr get_account_addresses(const std::string& name); void uppercase_symbol(std::string& symbol); - bool is_valid_address(const std::string& address); - bool is_payment_address(const std::string& address); - bool is_stealth_address(const std::string& address); - bool is_script_address(const std::string& address); - bool is_blackhole_address(const std::string& address); + static bool is_valid_address(const std::string& address); + static bool is_payment_address(const std::string& address); + static bool is_stealth_address(const std::string& address); + static bool is_script_address(const std::string& address); + static bool is_blackhole_address(const std::string& address); void fired(); organizer& get_organizer(); diff --git a/include/metaverse/blockchain/validate_block.hpp b/include/metaverse/blockchain/validate_block.hpp index 417253bfc..4ad066a20 100644 --- a/include/metaverse/blockchain/validate_block.hpp +++ b/include/metaverse/blockchain/validate_block.hpp @@ -46,7 +46,7 @@ class BCB_API validate_block public: code check_block(blockchain::block_chain_impl& chain) const; code accept_block() const; - code connect_block(hash_digest& err_tx) const; + code connect_block(hash_digest& err_tx, blockchain::block_chain_impl& chain) const; /// Required to call before calling accept_block or connect_block. void initialize_context(); @@ -55,6 +55,15 @@ class BCB_API validate_block bool get_transaction(const hash_digest& tx_hash, chain::transaction& prev_tx, size_t& prev_height) const; + virtual std::string get_did_from_address_consider_orphan_chain(const std::string& address, const std::string& did_symbol) const = 0; + virtual bool is_did_match_address_in_orphan_chain(const std::string& symbol, const std::string& address) const = 0; + virtual bool is_did_in_orphan_chain(const std::string& symbol) const = 0; + virtual bool is_asset_in_orphan_chain(const std::string& symbol) const = 0; + virtual bool is_asset_cert_in_orphan_chain(const std::string& symbol, asset_cert_type cert_type) const = 0; + virtual bool is_asset_mit_in_orphan_chain(const std::string& symbol) const = 0; + + virtual size_t get_fork_index() const { return max_size_t; } + protected: typedef std::vector versions; typedef std::function stopped_callback; diff --git a/include/metaverse/blockchain/validate_block_impl.hpp b/include/metaverse/blockchain/validate_block_impl.hpp index e9e874d42..e174d0d9a 100644 --- a/include/metaverse/blockchain/validate_block_impl.hpp +++ b/include/metaverse/blockchain/validate_block_impl.hpp @@ -44,6 +44,15 @@ class BCB_API validate_block_impl virtual bool is_valid_proof_of_work(const chain::header& header) const; virtual bool check_get_coinage_reward_transaction(const chain::transaction& coinage_reward_coinbase, const chain::output& output) const; + virtual std::string get_did_from_address_consider_orphan_chain(const std::string& address, const std::string& did_symbol) const override; + virtual bool is_did_match_address_in_orphan_chain(const std::string& symbol, const std::string& address) const override; + virtual bool is_did_in_orphan_chain(const std::string& symbol) const override; + virtual bool is_asset_in_orphan_chain(const std::string& symbol) const override; + virtual bool is_asset_cert_in_orphan_chain(const std::string& symbol, asset_cert_type cert_type) const override; + virtual bool is_asset_mit_in_orphan_chain(const std::string& symbol) const override; + + virtual size_t get_fork_index() const override { return fork_index_; } + protected: uint64_t median_time_past() const; u256 previous_block_bits() const; diff --git a/include/metaverse/blockchain/validate_transaction.hpp b/include/metaverse/blockchain/validate_transaction.hpp index 5f976165e..0d6645e17 100644 --- a/include/metaverse/blockchain/validate_transaction.hpp +++ b/include/metaverse/blockchain/validate_transaction.hpp @@ -67,17 +67,22 @@ class BCB_API validate_transaction code check_asset_mit_transaction() const; code check_did_transaction() const; bool connect_did_input(const did& info) const; - code connect_input_address_match_did(const output& output) const; + bool is_did_match_address_in_orphan_chain(const std::string& symbol, const std::string& address) const; + bool is_did_in_orphan_chain(const std::string& did) const; + code check_attachment_to_did(const output& output) const; + code connect_attachment_from_did(const output& output) const; bool connect_input(const chain::transaction& previous_tx, size_t parent_height); - static bool tally_fees(const chain::transaction& tx, uint64_t value_in, - uint64_t& fees); + static bool tally_fees(blockchain::block_chain_impl& chain, + const chain::transaction& tx, uint64_t value_in, uint64_t& fees); + static bool check_special_fees(bool is_testnet, const chain::transaction& tx, uint64_t fees); bool check_asset_amount(const transaction& tx) const; bool check_asset_symbol(const transaction& tx) const; bool check_asset_certs(const transaction& tx) const; bool check_asset_mit(const transaction& tx) const; + bool check_address_registered_did(const std::string& address) const; //check input did match output did bool check_did_symbol_match(const transaction& tx) const; @@ -115,6 +120,10 @@ class BCB_API validate_transaction void check_double_spend(const code& ec, const chain::input_point& point); void check_fees() const; code check_tx_connect_input() const; + bool check_did_exist(const std::string& did) const; + bool check_asset_exist(const std::string& symbol) const; + bool check_asset_cert_exist(const std::string& cert, asset_cert_type cert_type) const; + bool check_asset_mit_exist(const std::string& mit) const; block_chain_impl& blockchain_; const transaction_ptr tx_; diff --git a/include/metaverse/consensus/libethash/internal.c b/include/metaverse/consensus/libethash/internal.c index c76a1521b..0610fa7d5 100644 --- a/include/metaverse/consensus/libethash/internal.c +++ b/include/metaverse/consensus/libethash/internal.c @@ -8,11 +8,11 @@ ethash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + along with cpp-ethereum. If not, see . */ /** @file internal.c * @author Tim Hughes @@ -218,10 +218,10 @@ static bool ethash_hash( for (unsigned n = 0; n != MIX_NODES; ++n) { node const* dag_node; + node tmp_node; if (full_nodes) { dag_node = &full_nodes[MIX_NODES * index + n]; } else { - node tmp_node; ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light); dag_node = &tmp_node; } @@ -241,7 +241,7 @@ static bool ethash_hash( #elif defined(__MIC__) { // __m512i implementation via union - // Each vector register (zmm) can store sixteen 32-bit integer numbers + // Each vector register (zmm) can store sixteen 32-bit integer numbers __m512i fnv_prime = _mm512_set1_epi32(FNV_PRIME); __m512i zmm0 = _mm512_mullo_epi32(fnv_prime, mix[n].zmm[0]); mix[n].zmm[0] = _mm512_xor_si512(zmm0, dag_node->zmm[0]); @@ -389,7 +389,7 @@ ethash_return_value_t ethash_light_compute_internal( uint64_t nonce ) { - ethash_return_value_t ret; + ethash_return_value_t ret; ret.success = true; if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) { ret.success = false; @@ -446,42 +446,44 @@ ethash_full_t ethash_full_new_internal( return NULL; } ret->file_size = (size_t)full_size; - switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) { - case ETHASH_IO_FAIL: - // ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case + + enum ethash_io_rc err = ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false); + if (err == ETHASH_IO_FAIL) goto fail_free_full; - case ETHASH_IO_MEMO_MATCH: - if (!ethash_mmap(ret, f)) { - ETHASH_CRITICAL("mmap failure()"); - goto fail_close_file; - } -#if defined(__MIC__) - node* tmp_nodes = _mm_malloc((size_t)full_size, 64); - //copy all nodes from ret->data - //mmapped_nodes are not aligned properly - uint32_t const countnodes = (uint32_t) ((size_t)ret->file_size / sizeof(node)); - //fprintf(stderr,"ethash_full_new_internal:countnodes:%d",countnodes); - for (uint32_t i = 1; i != countnodes; ++i) { - tmp_nodes[i] = ret->data[i]; - } - ret->data = tmp_nodes; -#endif - return ret; - case ETHASH_IO_MEMO_SIZE_MISMATCH: + + if (err == ETHASH_IO_MEMO_SIZE_MISMATCH) { // if a DAG of same filename but unexpected size is found, silently force new file creation if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) { ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size."); goto fail_free_full; } - // fallthrough to the mismatch case here, DO NOT go through match - case ETHASH_IO_MEMO_MISMATCH: + // we now need to go through the mismatch case, NOT the match case + err = ETHASH_IO_MEMO_MISMATCH; + } + + if (err == ETHASH_IO_MEMO_MISMATCH || err == ETHASH_IO_MEMO_MATCH) { if (!ethash_mmap(ret, f)) { ETHASH_CRITICAL("mmap failure()"); goto fail_close_file; } - break; + + if (err == ETHASH_IO_MEMO_MATCH) { +#if defined(__MIC__) + node* tmp_nodes = _mm_malloc((size_t)full_size, 64); + //copy all nodes from ret->data + //mmapped_nodes are not aligned properly + uint32_t const countnodes = (uint32_t) ((size_t)ret->file_size / sizeof(node)); + //fprintf(stderr,"ethash_full_new_internal:countnodes:%d",countnodes); + for (uint32_t i = 1; i != countnodes; ++i) { + tmp_nodes[i] = ret->data[i]; + } + ret->data = tmp_nodes; +#endif + return ret; + } } + #if defined(__MIC__) ret->data = _mm_malloc((size_t)full_size, 64); #endif @@ -568,4 +570,4 @@ void const* ethash_full_dag(ethash_full_t full) uint64_t ethash_full_dag_size(ethash_full_t full) { return full->file_size; -} +} \ No newline at end of file diff --git a/include/metaverse/database/databases/address_asset_database.hpp b/include/metaverse/database/databases/address_asset_database.hpp index 0ac6eb4d1..b52a7e83c 100644 --- a/include/metaverse/database/databases/address_asset_database.hpp +++ b/include/metaverse/database/databases/address_asset_database.hpp @@ -121,6 +121,10 @@ class BCD_API address_asset_database const std::string& symbol, asset_cert_type cert_type, size_t from_height) const; + business_history::list get_asset_certs_history(const std::string& address, + const std::string& symbol, asset_cert_type cert_type, + size_t from_height) const; + /// Delete the last row that was added to key. void delete_last_row(const short_hash& key); diff --git a/include/metaverse/database/databases/address_did_database.hpp b/include/metaverse/database/databases/address_did_database.hpp index 158d2e3c3..e9005a34d 100644 --- a/include/metaverse/database/databases/address_did_database.hpp +++ b/include/metaverse/database/databases/address_did_database.hpp @@ -112,7 +112,7 @@ class BCD_API address_did_database business_address_did::list get_dids(const std::string& address, size_t from_height, business_kind kind) const; business_address_did::list get_dids(const std::string& address, - size_t from_height) const; + size_t from_height, size_t to_height = max_uint64) const; business_address_message::list get_messages(const std::string& address, size_t from_height) const; diff --git a/include/metaverse/database/databases/blockchain_asset_database.hpp b/include/metaverse/database/databases/blockchain_asset_database.hpp index 610f3bf38..e61061d2b 100644 --- a/include/metaverse/database/databases/blockchain_asset_database.hpp +++ b/include/metaverse/database/databases/blockchain_asset_database.hpp @@ -67,6 +67,12 @@ class BCD_API blockchain_asset_database uint64_t get_asset_volume(const std::string& name) const; + /// + std::shared_ptr get_register_history(const std::string & asset_symbol) const; + /// + uint64_t get_register_height(const std::string & asset_symbol) const; + + void store(const hash_digest& hash, const blockchain_asset& sp_detail); /// Delete a transaction from database. diff --git a/include/metaverse/database/databases/blockchain_did_database.hpp b/include/metaverse/database/databases/blockchain_did_database.hpp index c56d624b7..76e2b6d13 100644 --- a/include/metaverse/database/databases/blockchain_did_database.hpp +++ b/include/metaverse/database/databases/blockchain_did_database.hpp @@ -67,6 +67,15 @@ class BCD_API blockchain_did_database /// std::shared_ptr > get_blockchain_dids() const; + /// + std::shared_ptr get_register_history(const std::string & did_symbol) const; + /// + uint64_t get_register_height(const std::string & did_symbol) const; + + std::shared_ptr > getdids_from_address_history( + const std::string &address, const uint64_t& fromheight = 0 + ,const uint64_t & toheight = max_uint64 ) const; + void store(const hash_digest& hash, const blockchain_did& sp_detail); /// Delete a transaction from database. diff --git a/include/metaverse/database/databases/blockchain_mit_database.hpp b/include/metaverse/database/databases/blockchain_mit_database.hpp index 682b56112..a6bd5d73c 100644 --- a/include/metaverse/database/databases/blockchain_mit_database.hpp +++ b/include/metaverse/database/databases/blockchain_mit_database.hpp @@ -57,6 +57,11 @@ class BCD_API blockchain_mit_database /// Get all asset certs std::shared_ptr get_blockchain_mits() const; + /// + std::shared_ptr get_register_history(const std::string & mit_symbol) const; + /// + uint64_t get_register_height(const std::string & mit_symbol) const; + void store(const asset_mit_info& mit_info); /// Delete a transaction from database. diff --git a/include/metaverse/database/impl/slab_hash_table.ipp b/include/metaverse/database/impl/slab_hash_table.ipp index 37ce68e4d..85d0a49b1 100644 --- a/include/metaverse/database/impl/slab_hash_table.ipp +++ b/include/metaverse/database/impl/slab_hash_table.ipp @@ -182,6 +182,7 @@ std::vector slab_hash_table::finds(const KeyType& key) cons return ret; } + // This is limited to returning all the item in the special index. template std::shared_ptr> slab_hash_table::find(uint64_t index) const @@ -215,7 +216,6 @@ std::shared_ptr> slab_hash_table::find(uint64_t return vec_memo; } - // This is limited to unlinking the first of multiple matching key values. template bool slab_hash_table::unlink(const KeyType& key) diff --git a/include/metaverse/explorer/extensions/base_helper.hpp b/include/metaverse/explorer/extensions/base_helper.hpp index b9bddf03d..d0078d976 100644 --- a/include/metaverse/explorer/extensions/base_helper.hpp +++ b/include/metaverse/explorer/extensions/base_helper.hpp @@ -147,10 +147,39 @@ struct balances { uint64_t frozen_balance; }; +struct deposited_balance { + deposited_balance(const std::string& address_, const string& tx_hash_, const string& row_hash_, + uint64_t balance_, uint64_t deposited_, uint64_t expiration_) + : address(address_) + , tx_hash(tx_hash_) + , row_hash(row_hash_) + , balance(balance_) + , deposited_height(deposited_) + , expiration_height(expiration_) + {} + + std::string address; + std::string tx_hash; + std::string row_hash; + uint64_t balance; + uint64_t deposited_height; + uint64_t expiration_height; + + // for sort + bool operator< (const deposited_balance& other) const { + return expiration_height < other.expiration_height; + } + + typedef std::vector list; +}; + // helper function void sync_fetchbalance(wallet::payment_address& address, bc::blockchain::block_chain_impl& blockchain, balances& addr_balance); +void sync_fetch_deposited_balance(wallet::payment_address& address, + bc::blockchain::block_chain_impl& blockchain, std::shared_ptr sh_vec); + void sync_fetch_asset_balance(const std::string& address, bool sum_all, bc::blockchain::block_chain_impl& blockchain, std::shared_ptr sh_asset_vec); @@ -165,6 +194,8 @@ std::string get_random_payment_address(std::shared_ptr unissued_asset_; std::string domain_cert_address_; std::string attenuation_model_param_; + uint32_t fee_percentage_to_miner_; }; class BCX_API secondary_issuing_asset : public base_transfer_helper @@ -548,11 +581,13 @@ class BCX_API registering_did : public base_multisig_transfer_helper public: registering_did(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, - std::string&& from, std::string&& symbol, receiver_record::list&& receiver_list, uint64_t fee, + std::string&& from, std::string&& symbol, receiver_record::list&& receiver_list, + uint64_t fee, uint32_t fee_percentage_to_miner, account_multisig&& multisig) : base_multisig_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), std::move(from), std::move(receiver_list), fee, std::move(symbol), std::move(multisig)) + , fee_percentage_to_miner_(fee_percentage_to_miner) {} ~registering_did() @@ -564,6 +599,9 @@ class BCX_API registering_did : public base_multisig_transfer_helper tx_.version = transaction_version::check_nova_feature; tx_.locktime = 0; }; + +private: + uint32_t fee_percentage_to_miner_; }; class BCX_API sending_multisig_did : public base_transfer_helper diff --git a/include/metaverse/explorer/extensions/commands/didsend.hpp b/include/metaverse/explorer/extensions/commands/didsend.hpp index 9987561db..a75088b92 100644 --- a/include/metaverse/explorer/extensions/commands/didsend.hpp +++ b/include/metaverse/explorer/extensions/commands/didsend.hpp @@ -43,7 +43,7 @@ class didsend: public send_command return get_argument_metadata() .add("ACCOUNTNAME", 1) .add("ACCOUNTAUTH", 1) - .add("TODID/TOADDRESS", 1) + .add("TO_", 1) .add("AMOUNT", 1); } @@ -53,7 +53,7 @@ class didsend: public send_command const auto raw = requires_raw_input(); load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); - load_input(argument_.did, "TODID/TOADDRESS", variables, input, raw); + load_input(argument_.did, "TO_", variables, input, raw); load_input(argument_.amount, "AMOUNT", variables, input, raw); } @@ -78,7 +78,7 @@ class didsend: public send_command BX_ACCOUNT_AUTH ) ( - "TODID/TOADDRESS", + "TO_", value(&argument_.did)->required(), "Send to this did/address" ) diff --git a/include/metaverse/explorer/extensions/commands/didsendasset.hpp b/include/metaverse/explorer/extensions/commands/didsendasset.hpp index 43f526dc6..24d7146e0 100644 --- a/include/metaverse/explorer/extensions/commands/didsendasset.hpp +++ b/include/metaverse/explorer/extensions/commands/didsendasset.hpp @@ -45,7 +45,7 @@ class didsendasset: public command_extension return get_argument_metadata() .add("ACCOUNTNAME", 1) .add("ACCOUNTAUTH", 1) - .add("TODID/TOADDRESS", 1) + .add("TO_", 1) .add("ASSET", 1) .add("AMOUNT", 1); } @@ -56,7 +56,7 @@ class didsendasset: public command_extension const auto raw = requires_raw_input(); load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); - load_input(argument_.did, "TODID/TOADDRESS", variables, input, raw); + load_input(argument_.did, "TO_", variables, input, raw); load_input(argument_.symbol, "ASSET", variables, input, raw); load_input(argument_.amount, "AMOUNT", variables, input, raw); } @@ -82,7 +82,7 @@ class didsendasset: public command_extension BX_ACCOUNT_AUTH ) ( - "TODID/TOADDRESS", + "TO_", value(&argument_.did)->required(), "Asset receiver did/address." ) diff --git a/include/metaverse/explorer/extensions/commands/didsendassetfrom.hpp b/include/metaverse/explorer/extensions/commands/didsendassetfrom.hpp index 51206e6b1..2d7236ac7 100644 --- a/include/metaverse/explorer/extensions/commands/didsendassetfrom.hpp +++ b/include/metaverse/explorer/extensions/commands/didsendassetfrom.hpp @@ -45,8 +45,8 @@ class didsendassetfrom: public command_extension return get_argument_metadata() .add("ACCOUNTNAME", 1) .add("ACCOUNTAUTH", 1) - .add("FROMDID/FROMADDRESS", 1) - .add("TODID/TOADDRESS", 1) + .add("FROM_", 1) + .add("TO_", 1) .add("SYMBOL", 1) .add("AMOUNT", 1); } @@ -57,8 +57,8 @@ class didsendassetfrom: public command_extension const auto raw = requires_raw_input(); load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); - load_input(argument_.fromdid, "FROMDID/FROMADDRESS", variables, input, raw); - load_input(argument_.todid, "TODID/TOADDRESS", variables, input, raw); + load_input(argument_.fromdid, "FROM_", variables, input, raw); + load_input(argument_.todid, "TO_", variables, input, raw); load_input(argument_.symbol, "SYMBOL", variables, input, raw); load_input(argument_.amount, "AMOUNT", variables, input, raw); } @@ -84,12 +84,12 @@ class didsendassetfrom: public command_extension BX_ACCOUNT_AUTH ) ( - "FROMDID/FROMADDRESS", + "FROM_", value(&argument_.fromdid)->required(), "From did/address" ) ( - "TODID/TOADDRESS", + "TO_", value(&argument_.todid)->required(), "Target did/address" ) diff --git a/include/metaverse/explorer/extensions/commands/didsendfrom.hpp b/include/metaverse/explorer/extensions/commands/didsendfrom.hpp index 9216475b3..189b3d193 100644 --- a/include/metaverse/explorer/extensions/commands/didsendfrom.hpp +++ b/include/metaverse/explorer/extensions/commands/didsendfrom.hpp @@ -45,8 +45,8 @@ class didsendfrom: public send_command return get_argument_metadata() .add("ACCOUNTNAME", 1) .add("ACCOUNTAUTH", 1) - .add("FROMDID/FROMADDRESS", 1) - .add("TODID/TOADDRESS", 1) + .add("FROM_", 1) + .add("TO_", 1) .add("AMOUNT", 1); } @@ -56,8 +56,8 @@ class didsendfrom: public send_command const auto raw = requires_raw_input(); load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); - load_input(argument_.fromdid, "FROMDID/FROMADDRESS", variables, input, raw); - load_input(argument_.todid, "TODID/TOADDRESS", variables, input, raw); + load_input(argument_.fromdid, "FROM_", variables, input, raw); + load_input(argument_.todid, "TO_", variables, input, raw); load_input(argument_.amount, "AMOUNT", variables, input, raw); } @@ -82,12 +82,12 @@ class didsendfrom: public send_command BX_ACCOUNT_AUTH ) ( - "FROMDID/FROMADDRESS", + "FROM_", value(&argument_.fromdid)->required(), "Send from this did/address" ) ( - "TODID/TOADDRESS", + "TO_", value(&argument_.todid)->required(), "Send to this did/address" ) diff --git a/include/metaverse/explorer/extensions/commands/getaddressasset.hpp b/include/metaverse/explorer/extensions/commands/getaddressasset.hpp index 4ee135441..0a345b80e 100644 --- a/include/metaverse/explorer/extensions/commands/getaddressasset.hpp +++ b/include/metaverse/explorer/extensions/commands/getaddressasset.hpp @@ -43,8 +43,6 @@ class getaddressasset: public command_extension arguments_metadata& load_arguments() override { return get_argument_metadata() - //.add("ACCOUNTNAME", 1) - //.add("ACCOUNTAUTH", 1) .add("ADDRESS", 1); } @@ -52,8 +50,6 @@ class getaddressasset: public command_extension po::variables_map& variables) override { const auto raw = requires_raw_input(); - //load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); - //load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); load_input(argument_.address, "ADDRESS", variables, input, raw); } diff --git a/include/metaverse/explorer/extensions/commands/getdid.hpp b/include/metaverse/explorer/extensions/commands/getdid.hpp index b463ce9d7..3bbb73701 100644 --- a/include/metaverse/explorer/extensions/commands/getdid.hpp +++ b/include/metaverse/explorer/extensions/commands/getdid.hpp @@ -43,14 +43,14 @@ class getdid: public command_extension arguments_metadata& load_arguments() override { return get_argument_metadata() - .add("DID/ADDRESS", 1); + .add("DidOrAddress", 1); } void load_fallbacks (std::istream& input, po::variables_map& variables) override { const auto raw = requires_raw_input(); - load_input(option_.symbol, "DID/ADDRESS", variables, input, raw); + load_input(option_.symbol, "DidOrAddress", variables, input, raw); } options_metadata& load_options() override @@ -64,7 +64,7 @@ class getdid: public command_extension "Get a description and instructions for this command." ) ( - "DID/ADDRESS", + "DidOrAddress", value(&option_.symbol), "Did symbol or standard address; If no input parameters, then display whole network DIDs." ); diff --git a/include/metaverse/explorer/extensions/commands/issue.hpp b/include/metaverse/explorer/extensions/commands/issue.hpp index b0e7e449f..f8bba85d4 100644 --- a/include/metaverse/explorer/extensions/commands/issue.hpp +++ b/include/metaverse/explorer/extensions/commands/issue.hpp @@ -88,8 +88,13 @@ class issue: public command_extension ) ( "fee,f", - value(&argument_.fee)->default_value(1000000000), - "The fee of tx. default_value 10 etp" + value(&argument_.fee)->default_value(10 * 100000000), + "The fee of tx. minimum is 10 etp." + ) + ( + "percentage,p", + value(&argument_.percentage)->default_value(20), + "Percentage of fee send to miner. minimum is 20." ); return options; @@ -106,6 +111,7 @@ class issue: public command_extension { std::string symbol; uint64_t fee; + uint32_t percentage; } argument_; struct option diff --git a/include/metaverse/explorer/extensions/commands/listbalances.hpp b/include/metaverse/explorer/extensions/commands/listbalances.hpp index 193cf1d1e..8b3e95e70 100644 --- a/include/metaverse/explorer/extensions/commands/listbalances.hpp +++ b/include/metaverse/explorer/extensions/commands/listbalances.hpp @@ -65,10 +65,15 @@ class listbalances: public command_extension value()->zero_tokens(), "Get a description and instructions for this command." ) + ( + "deposited,d", + value(&option_.deposited)->zero_tokens()->default_value(false), + "list deposited ETPs, default is false." + ) ( "nozero,n", value(&option_.non_zero)->zero_tokens()->default_value(false), - "Defaults to false." + "Default is false." ) ( "greater_equal,g", @@ -108,6 +113,7 @@ class listbalances: public command_extension struct option { bool non_zero; + bool deposited; uint64_t greater; uint64_t lesser; } option_; diff --git a/include/metaverse/explorer/extensions/commands/registerdid.hpp b/include/metaverse/explorer/extensions/commands/registerdid.hpp index 1678856f0..c7d94285c 100644 --- a/include/metaverse/explorer/extensions/commands/registerdid.hpp +++ b/include/metaverse/explorer/extensions/commands/registerdid.hpp @@ -93,6 +93,11 @@ class registerdid: public command_extension "fee,f", value(&argument_.fee)->default_value(100000000), "The fee of tx. defaults to 1 etp." + ) + ( + "percentage,p", + value(&argument_.percentage)->default_value(20), + "Percentage of fee send to miner. minimum is 20." ); return options; @@ -110,6 +115,7 @@ class registerdid: public command_extension std::string address; std::string symbol; uint64_t fee; + uint32_t percentage; } argument_; struct option diff --git a/include/metaverse/explorer/impl/json_helper.ipp b/include/metaverse/explorer/impl/json_helper.ipp index 0d3d9a973..b3600d497 100644 --- a/include/metaverse/explorer/impl/json_helper.ipp +++ b/include/metaverse/explorer/impl/json_helper.ipp @@ -40,6 +40,8 @@ Json::Value json_helper::prop_tree_list(const std::string& name, const Values& v for (const auto& value: values) list.append(Json::Value()[denormalized_name] = prop_list(value)); + if(list.isNull()) + list.resize(0); return list; } @@ -53,6 +55,10 @@ Json::Value json_helper::prop_tree_list_of_lists(const std::string& name, for (const auto& value: values) list.append(Json::Value()[denormalized_name] = prop_list(value, json)); + + if(list.isNull()) + list.resize(0); + return list; } @@ -68,6 +74,9 @@ Json::Value json_helper::prop_value_list(const std::string& name, const Values& list.append(Json::Value()[denormalized_name] += value); } + if(list.isNull()) + list.resize(0); + return list; } diff --git a/include/metaverse/explorer/json_helper.hpp b/include/metaverse/explorer/json_helper.hpp index 4543c169e..c147ec769 100644 --- a/include/metaverse/explorer/json_helper.hpp +++ b/include/metaverse/explorer/json_helper.hpp @@ -275,6 +275,9 @@ BCX_API Json::Value prop_tree(const chain::points_info& points_info, bool json); */ BCX_API Json::Value prop_list(const transaction& transaction, bool json); BCX_API Json::Value prop_list(const transaction& transaction, uint64_t tx_height, bool json); + +BCX_API Json::Value prop_list_of_rawtx(const transaction& transaction, bool with_hash, bool ignore_compatibility=false); + /** * Generate a property tree for a transaction. * @param[in] transaction The transaction. @@ -464,6 +467,14 @@ BCX_API Json::Value prop_list(const bc::chain::account_multisig& multisign); */ BCX_API Json::Value prop_attenuation_model_param(const data_chunk& param); +/** + * Generate a property list for an account. + * @param[in] acc The account. + * @return A property list. + */ +typedef std::tuple account_info; +BCX_API Json::Value prop_list(const account_info& acc); + private: uint8_t version_{ 1 }; //1 - api v1; 2 - api v2; }; diff --git a/include/metaverse/explorer/version.hpp b/include/metaverse/explorer/version.hpp index d5566d76a..4247cf7b2 100644 --- a/include/metaverse/explorer/version.hpp +++ b/include/metaverse/explorer/version.hpp @@ -12,9 +12,9 @@ * For interpretation of the versioning scheme see: http://semver.org */ -#define MVS_EXPLORER_VERSION "0.8.1" +#define MVS_EXPLORER_VERSION "0.8.2" #define MVS_EXPLORER_MAJOR_VERSION 0 #define MVS_EXPLORER_MINOR_VERSION 8 -#define MVS_EXPLORER_PATCH_VERSION 1 +#define MVS_EXPLORER_PATCH_VERSION 2 #endif diff --git a/include/metaverse/mgbubble/utility/Stream_buf.hpp b/include/metaverse/mgbubble/utility/Stream_buf.hpp index 53f01af1d..b04da3e23 100644 --- a/include/metaverse/mgbubble/utility/Stream_buf.hpp +++ b/include/metaverse/mgbubble/utility/Stream_buf.hpp @@ -30,7 +30,7 @@ namespace mgbubble { class StreamBuf : public std::streambuf { public: - explicit StreamBuf(mbuf& buf) throw(std::bad_alloc); + explicit StreamBuf(mbuf& buf); ~StreamBuf() noexcept override; // Copy. diff --git a/include/metaverse/network/message_subscriber.hpp b/include/metaverse/network/message_subscriber.hpp index f46c68a72..31a700269 100644 --- a/include/metaverse/network/message_subscriber.hpp +++ b/include/metaverse/network/message_subscriber.hpp @@ -57,7 +57,6 @@ class BCT_API message_subscriber { public: DEFINE_SUBSCRIBER_TYPE(address); - DEFINE_SUBSCRIBER_TYPE(alert); DEFINE_SUBSCRIBER_TYPE(block_message); DEFINE_SUBSCRIBER_TYPE(block_transactions); DEFINE_SUBSCRIBER_TYPE(compact_block); @@ -172,7 +171,6 @@ class BCT_API message_subscriber private: DEFINE_SUBSCRIBER_OVERLOAD(address); - DEFINE_SUBSCRIBER_OVERLOAD(alert); DEFINE_SUBSCRIBER_OVERLOAD(block_message); DEFINE_SUBSCRIBER_OVERLOAD(block_transactions); DEFINE_SUBSCRIBER_OVERLOAD(compact_block); @@ -200,7 +198,6 @@ class BCT_API message_subscriber DEFINE_SUBSCRIBER_OVERLOAD(version); DECLARE_SUBSCRIBER(address); - DECLARE_SUBSCRIBER(alert); DECLARE_SUBSCRIBER(block_message); DECLARE_SUBSCRIBER(block_transactions); DECLARE_SUBSCRIBER(compact_block); diff --git a/src/lib/bitcoin/chain/attachment/account/account.cpp b/src/lib/bitcoin/chain/attachment/account/account.cpp index 479a1eaba..b4eca84e6 100644 --- a/src/lib/bitcoin/chain/attachment/account/account.cpp +++ b/src/lib/bitcoin/chain/attachment/account/account.cpp @@ -491,26 +491,6 @@ void account::set_priority(uint8_t priority) this->priority = priority; } -void account::set_user_status(uint8_t status) -{ - this->status |= status; -} - -void account::set_system_status(uint8_t status) -{ - this->status |= (status << sizeof(uint8_t)); -} - -uint8_t account::get_user_status() const -{ - return static_cast(status & 0xff); -} - -uint8_t account::get_system_status() const -{ - return static_cast(status >> sizeof(uint8_t)); -} - const account_multisig::list& account::get_multisig_vec() const { return multisig_vec; diff --git a/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp b/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp index 2df3c9df5..a8f16e25e 100644 --- a/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp +++ b/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp @@ -84,7 +84,7 @@ bool asset_cert::is_valid_domain(const std::string& domain) return !domain.empty(); } -std::string asset_cert::get_key(const std::string&symbol, asset_cert_type bit) +std::string asset_cert::get_key(const std::string&symbol, const asset_cert_type& bit) { return std::string(symbol + ":^#`@:" + std::to_string(bit)); } diff --git a/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp b/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp index 2bcbb2ae4..453c7da46 100644 --- a/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp +++ b/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp @@ -650,12 +650,16 @@ bool attenuation_model::check_model_param_uc_uq(attenuation_model& parser) log::debug(LOG_HEADER) << "attenuation param error: UQ.size() != UN"; return false; } - if (sum_and_check_numbers(UCs, is_positive_number) != LP) { - log::debug(LOG_HEADER) << "attenuation param error: LP != sum(UC) or exist UC <= 0"; + auto value = sum_and_check_numbers(UCs, is_positive_number); + if (value != LP) { + log::debug(LOG_HEADER) << "attenuation param error: LP(" + << std::to_string(LP) <<") != sum(UC)(" << std::to_string(value) << ") or exist UC <= 0"; return false; } - if (sum_and_check_numbers(UQs, is_positive_number) != LQ) { - log::debug(LOG_HEADER) << "attenuation param error: LQ != sum(UQ) or exist UQ <= 0"; + value = sum_and_check_numbers(UQs, is_positive_number); + if (value != LQ) { + log::debug(LOG_HEADER) << "attenuation param error: LQ(" + << std::to_string(LQ) <<") != sum(UQ)(" << std::to_string(value) << ") or exist UQ <= 0"; return false; } return true; diff --git a/src/lib/bitcoin/chain/block.cpp b/src/lib/bitcoin/chain/block.cpp index 94fd5e44f..27c460c8e 100644 --- a/src/lib/bitcoin/chain/block.cpp +++ b/src/lib/bitcoin/chain/block.cpp @@ -146,7 +146,7 @@ data_chunk block::to_data(bool with_transaction_count) const data_sink ostream(data); to_data(ostream, with_transaction_count); ostream.flush(); - BITCOIN_ASSERT(data.size() == serialized_size(with_transaction_count)); + BITCOIN_ASSERT(header.number <= 1270000 || data.size() == serialized_size(with_transaction_count)); return data; } diff --git a/src/lib/bitcoin/chain/output.cpp b/src/lib/bitcoin/chain/output.cpp index 12906cc1e..3e66aa60c 100644 --- a/src/lib/bitcoin/chain/output.cpp +++ b/src/lib/bitcoin/chain/output.cpp @@ -157,31 +157,21 @@ code output::check_attachment_address(bc::blockchain::block_chain_impl& chain) c if (is_asset || is_did) { auto script_address = get_script_address(); if (attachment_address != script_address) { - if (is_asset) + log::debug("output::check_attachment_address") + << (is_asset ? "asset" : "did") + << " attachment address " << attachment_address + << " is not equal to script address " << script_address; + if (is_asset) { return error::asset_address_not_match; - if (is_did) + } + if (is_did) { return error::did_address_not_match; + } } } return error::success; } -code output::check_attachment_did_match_address(bc::blockchain::block_chain_impl& chain) const -{ - - auto todid = attach_data.get_to_did(); - if (!todid.empty()) - { - auto address = get_script_address(); - if (todid != chain.get_did_from_address(address)) - { - return error::did_address_not_match; - } - } - - return error::success; -} - void output::reset() { value = 0; diff --git a/src/lib/bitcoin/constants.cpp b/src/lib/bitcoin/constants.cpp index 270a1fab7..6d8b26207 100644 --- a/src/lib/bitcoin/constants.cpp +++ b/src/lib/bitcoin/constants.cpp @@ -30,4 +30,13 @@ hash_number max_target() return max_target; } +std::string get_developer_community_address(bool is_testnet) +{ + std::string address("MAwLwVGwJyFsTBfNj2j5nCUrQXGVRvHzPh"); // developer-community address for mainnet + if (is_testnet) { + address = "tJNo92g6DavpaCZbYjrH45iQ8eAKnLqmms"; // developer-community address for testnet + } + return address; +} + } // namespace libbitcoin diff --git a/src/lib/bitcoin/message/heading.cpp b/src/lib/bitcoin/message/heading.cpp index 942fcd2f2..5156bb405 100644 --- a/src/lib/bitcoin/message/heading.cpp +++ b/src/lib/bitcoin/message/heading.cpp @@ -150,8 +150,6 @@ message_type heading::type() const // TODO: convert to static map. if (command == address::command) return message_type::address; - if (command == alert::command) - return message_type::alert; if (command == block_transactions::command) return message_type::block_transactions; if (command == block_message::command) diff --git a/src/lib/blockchain/block_chain_impl.cpp b/src/lib/blockchain/block_chain_impl.cpp index da2a05c9d..7f24fc184 100644 --- a/src/lib/blockchain/block_chain_impl.cpp +++ b/src/lib/blockchain/block_chain_impl.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -1450,6 +1452,11 @@ bool block_chain_impl::is_asset_cert_exist(const std::string& symbol, asset_cert return database_.certs.get(key) != nullptr; } +bool block_chain_impl::is_asset_mit_exist(const std::string& symbol) +{ + return get_registered_mit(symbol) != nullptr; +} + std::shared_ptr block_chain_impl::get_registered_mit(const std::string& symbol) { BITCOIN_ASSERT(!symbol.empty()); @@ -1845,11 +1852,9 @@ bool block_chain_impl::is_did_exist(const std::string& did_name) /* check did address exist or not */ -bool block_chain_impl::is_address_registered_did(const std::string& did_address) +bool block_chain_impl::is_address_registered_did(const std::string& did_address, uint64_t fork_index) { - // find from blockchain database - auto&& did_vec = database_.address_dids.get_dids(did_address, 0); - return !did_vec.empty(); + return !get_did_from_address(did_address, fork_index).empty(); } bool block_chain_impl::is_account_owned_did(const std::string& account, const std::string& symbol) @@ -1877,28 +1882,49 @@ std::shared_ptr block_chain_impl::get_account_dids(const std:: auto pvaddr = get_account_addresses(account); if (pvaddr) { for (const auto& account_address : *pvaddr) { - auto&& did_vec = database_.address_dids.get_dids(account_address.get_address(), 0); - if (!did_vec.empty()) { - sh_vec->emplace_back(std::move(did_vec[0].detail)); + auto did_address = account_address.get_address(); + auto did_symbol = get_did_from_address(did_address); + if (!did_symbol.empty()) { + sh_vec->emplace_back(did_detail(did_symbol, did_address)); } } } return sh_vec; } - /* find did by address */ -std::string block_chain_impl::get_did_from_address(const std::string& did_address) +std::string block_chain_impl::get_did_from_address(const std::string& did_address, uint64_t fork_index) { - // find from blockchain database - business_address_did::list did_vec = database_.address_dids.get_dids(did_address, 0); + //search from address_did_database first + business_address_did::list did_vec = database_.address_dids.get_dids(did_address, 0, fork_index); - if (!did_vec.empty()) { - return did_vec[0].detail.get_symbol(); + std::string did_symbol= ""; + if(!did_vec.empty()) + did_symbol = did_vec[0].detail.get_symbol(); + + if(did_symbol != "") + { + //double check + std::shared_ptr blockchain_didlist = get_did_history_addresses(did_symbol); + auto iter = std::find_if(blockchain_didlist->begin(), blockchain_didlist->end(), + [fork_index](const blockchain_did & item){ + return is_blackhole_address(item.get_did().get_address()) || item.get_height() <= fork_index; + }); + + if(iter == blockchain_didlist->end() || iter->get_did().get_address() != did_address) + return ""; + } + else if(did_symbol == "" && fork_index != max_uint64) + { + // search from dids database + auto sp_did_vec = database_.dids.getdids_from_address_history(did_address, 0, fork_index); + if (sp_did_vec && !sp_did_vec->empty()) { + did_symbol = sp_did_vec->back().get_did().get_symbol(); + } } - return ""; + return did_symbol; } /* find history addresses by the did symbol @@ -1987,48 +2013,6 @@ std::shared_ptr block_chain_impl::get_account_me return sp_asset_vec; } -account_status block_chain_impl::get_account_user_status(const std::string& name) -{ - account_status ret_val = account_status::error; - auto account = get_account(name); - if (account) // account exist - ret_val = static_cast(account->get_user_status()); - return ret_val; -} - -account_status block_chain_impl::get_account_system_status(const std::string& name) -{ - account_status ret_val = account_status::error; - auto account = get_account(name); - if (account) // account exist - ret_val = static_cast(account->get_system_status()); - return ret_val; -} - -bool block_chain_impl::set_account_user_status(const std::string& name, uint8_t status) -{ - bool ret_val = false; - auto account = get_account(name); - if (account) // account exist - { - account->set_user_status(status); - ret_val = true; - } - return ret_val; -} - -bool block_chain_impl::set_account_system_status(const std::string& name, uint8_t status) -{ - bool ret_val = false; - auto account = get_account(name); - if (account) // account exist - { - account->set_system_status(status); - ret_val = true; - } - return ret_val; -} - void block_chain_impl::fired() { organizer_.fired(); @@ -2058,30 +2042,34 @@ bool block_chain_impl::is_asset_exist(const std::string& asset_name, bool check_ return false; } -bool block_chain_impl::get_asset_height(const std::string& asset_name, uint64_t& height) +uint64_t block_chain_impl::get_asset_height(const std::string& asset_name)const { - const auto hash = get_hash(asset_name); - - // std::shared_ptr - auto sp_asset = database_.assets.get(hash); - if(sp_asset) { - height = sp_asset->get_height(); - } - - return (sp_asset != nullptr); + return database_.assets.get_register_height(asset_name); } -bool block_chain_impl::get_did_height(const std::string& did_name, uint64_t& height) +uint64_t block_chain_impl::get_did_height(const std::string& did_name)const { - const auto hash = get_hash(did_name); + return database_.dids.get_register_height(did_name); +} - // std::shared_ptr - auto sp_did = database_.dids.get(hash); - if(sp_did) { - height = sp_did->get_height(); +uint64_t block_chain_impl::get_asset_cert_height(const std::string& cert_symbol,const asset_cert_type& cert_type) +{ + auto&& key_str = asset_cert::get_key(cert_symbol, cert_type); + const auto key = get_hash(key_str); + auto cert = database_.certs.get(key); + if(cert) + { + business_history::list history_cert = database_.address_assets.get_asset_certs_history(cert->get_address(), cert_symbol, cert_type, 0); + if(history_cert.size()>0){ + return history_cert.back().output_height; + } } + return max_uint64; +} - return (sp_did != nullptr); +uint64_t block_chain_impl::get_asset_mit_height(const std::string& mit_symbol) const +{ + return database_.mits.get_register_height(mit_symbol); } /// get asset from local database including all account's assets diff --git a/src/lib/blockchain/organizer.cpp b/src/lib/blockchain/organizer.cpp index cc338d100..bf89f34ca 100644 --- a/src/lib/blockchain/organizer.cpp +++ b/src/lib/blockchain/organizer.cpp @@ -109,27 +109,31 @@ code organizer::verify(uint64_t fork_point, }; // Validates current_block - validate_block_impl validate(chain_, fork_point, orphan_chain, - orphan_index, height, *current_block, use_testnet_rules_, checkpoints_, - callback); + validate_block_impl validate(chain_, fork_point, orphan_chain, orphan_index, height, + *current_block, use_testnet_rules_, checkpoints_, callback); // Checks that are independent of the chain. auto ec = validate.check_block(static_cast(this->chain_)); - - if (ec) + if (error::success != ec) { + log::debug(LOG_BLOCKCHAIN) << "organizer: check_block failed! fork_point: " + << std::to_string(fork_point) << ", orphan_index: " << std::to_string(orphan_index); return ec; + } validate.initialize_context(); // Checks that are dependent on height and preceding blocks. ec = validate.accept_block(); - - if (ec) + if (error::success != ec) { + log::debug(LOG_BLOCKCHAIN) << "organizer: accept_block failed! fork_point: " + << std::to_string(fork_point) << ", orphan_index: " << std::to_string(orphan_index); return ec; + } // Start strict validation if past last checkpoint. - if (!strict(fork_point)) + if (!strict(fork_point)) { return ec; + } const auto total_inputs = count_inputs(*current_block); const auto total_transactions = current_block->transactions.size(); @@ -143,7 +147,7 @@ code organizer::verify(uint64_t fork_point, { hash_digest err_tx; // Checks that include input->output traversal. - ec = validate.connect_block(err_tx); + ec = validate.connect_block(err_tx, static_cast(this->chain_)); if(ec && err_tx != null_hash) { dynamic_cast(chain_).pool().delete_tx(err_tx); } diff --git a/src/lib/blockchain/orphan_pool.cpp b/src/lib/blockchain/orphan_pool.cpp index 70f1c85c5..6fcc90958 100644 --- a/src/lib/blockchain/orphan_pool.cpp +++ b/src/lib/blockchain/orphan_pool.cpp @@ -88,7 +88,7 @@ void orphan_pool::remove(block_detail::ptr block) log::debug(LOG_BLOCKCHAIN) << "Orphan pool removed block [" << encode_hash(block->hash()) - << "] old size (" << old_size << ")."; + << "] old size (" << old_size << "). with status: " << block->error().message(); } // TODO: use hash table pool to eliminate this O(n^2) search. diff --git a/src/lib/blockchain/transaction_pool.cpp b/src/lib/blockchain/transaction_pool.cpp index a20658044..ccf3ec2ff 100644 --- a/src/lib/blockchain/transaction_pool.cpp +++ b/src/lib/blockchain/transaction_pool.cpp @@ -162,100 +162,123 @@ code transaction_pool::check_symbol_repeat(transaction_ptr tx) std::set asset_mits; std::set dids; std::set didaddreses; + std::set didattaches; - for (auto& item : buffer_) + auto check_outputs = [&](transaction_ptr txs)->code { - if (!item.tx) - continue; - - for (auto& output : item.tx->outputs) + for (auto &output : txs->outputs) { - if (output.is_asset_issue()) { + //add attachment check;avoid send with did while transfer + if (output.attach_data.get_version() == DID_ATTACH_VERIFY_VERSION) + { + auto check_did = [&dids, &didattaches](string attach_did) { + if (!attach_did.empty() && dids.find(attach_did) != dids.end()) + { + log::debug(LOG_BLOCKCHAIN) + << "check_symbol_repeat attachment did: " + attach_did + << " already exists in memorypool!"; + return false; + } + + didattaches.insert(attach_did); + return true; + }; + + if (!check_did(output.attach_data.get_from_did()) + || !check_did(output.attach_data.get_to_did())) { + log::debug(LOG_BLOCKCHAIN) + << "check_symbol_repeat from_did " + output.attach_data.get_from_did() + << " to_did " + output.attach_data.get_to_did() + << " check failed!" + << " " << tx->to_string(1); + return error::did_exist; + } + } + + if (output.is_asset_issue()) + { auto r = assets.insert(output.get_asset_symbol()); - if (r.second == false) { + if (r.second == false) + { + log::debug(LOG_BLOCKCHAIN) + << "check_symbol_repeat asset " + output.get_asset_symbol() + << " already exists in memorypool!" + << " " << tx->to_string(1); return error::asset_exist; } } - else if (output.is_asset_cert()) { - auto&& key = output.get_asset_cert().get_key(); + else if (output.is_asset_cert()) + { + auto &&key = output.get_asset_cert().get_key(); auto r = asset_certs.insert(key); - if (r.second == false) { + if (r.second == false) + { log::debug(LOG_BLOCKCHAIN) - << " cert " + output.get_asset_cert_symbol() + << "check_symbol_repeat cert " + output.get_asset_cert_symbol() << " with type " << output.get_asset_cert_type() - << " already exists in pool!" + << " already exists in memorypool!" << " " << tx->to_string(1); return error::asset_cert_exist; } } - else if (output.is_asset_mit()) { + else if (output.is_asset_mit()) + { auto r = asset_mits.insert(output.get_asset_symbol()); - if (r.second == false) { + if (r.second == false) + { log::debug(LOG_BLOCKCHAIN) - << " mit " + output.get_asset_symbol() - << " already exists in block!" + << "check_symbol_repeat mit " + output.get_asset_symbol() + << " already exists in memorypool!" << " " << tx->to_string(1); return error::mit_exist; } } - else if (output.is_did()) { - auto didexist = dids.insert(output.get_did_symbol()); + else if (output.is_did()) + { + auto didsymbol = output.get_did_symbol(); + auto didexist = dids.insert(didsymbol); if (didexist.second == false) { + log::debug(LOG_BLOCKCHAIN) + << "check_symbol_repeat did " + didsymbol + << " already exists in memorypool!" + << " " << tx->to_string(1); return error::did_exist; } auto didaddress = didaddreses.insert(output.get_did_address()); - if (didaddress.second == false ) { + if (didaddress.second == false) + { + log::debug(LOG_BLOCKCHAIN) + << "check_symbol_repeat did address " + output.get_did_address() + << " already has did on it in memorypool!" + << " " << tx->to_string(1); return error::address_registered_did; } + + if (didattaches.find(didsymbol) != didattaches.end()) + { + log::debug(LOG_BLOCKCHAIN) + << "check_symbol_repeat attachment did: " + didsymbol + << " already transfer in memorypool!" + << " " << tx->to_string(1); + return error::did_exist; + } } } - } + return error::success; + }; - for (auto& output : tx->outputs) + code ec; + for (auto &item : buffer_) { - if (output.is_asset_issue()) { - auto r = assets.insert(output.get_asset_symbol()); - if (r.second == false) { - return error::asset_exist; - } - } - else if (output.is_asset_cert()) { - auto&& key = output.get_asset_cert().get_key(); - auto r = asset_certs.insert(key); - if (r.second == false) { - log::debug(LOG_BLOCKCHAIN) - << " cert " + output.get_asset_cert_symbol() - << " with type " << output.get_asset_cert_type() - << " already exists!" - << " " << tx->to_string(1); - return error::asset_cert_exist; - } - } - else if (output.is_asset_mit()) { - auto r = asset_mits.insert(output.get_asset_symbol()); - if (r.second == false) { - log::debug(LOG_BLOCKCHAIN) - << " mit " + output.get_asset_symbol() - << " already exists!" - << " " << tx->to_string(1); - return error::mit_exist; - } - } - else if (output.is_did()) { - auto didexist = dids.insert(output.get_did_symbol()); - if (didexist.second == false) { - return error::did_exist; - } + if (!item.tx) + continue; - auto didaddress = didaddreses.insert(output.get_did_address()); - if (didaddress.second == false ) { - return error::address_registered_did; - } - } + if((ec = check_outputs(item.tx)) != error::success) + break; } - return error::success; + return ec != error::success ? ec : check_outputs(tx); } // handle_confirm will never fire if handle_validate returns a failure code. diff --git a/src/lib/blockchain/validate_block.cpp b/src/lib/blockchain/validate_block.cpp index 8ee89da6a..798b63871 100644 --- a/src/lib/blockchain/validate_block.cpp +++ b/src/lib/blockchain/validate_block.cpp @@ -272,24 +272,28 @@ code validate_block::check_block(blockchain::block_chain_impl& chain) const std::set asset_mits; std::set dids; std::set didaddreses; + code first_tx_ec = error::success; for (const auto& tx : transactions) { RETURN_IF_STOPPED(); const auto validate_tx = std::make_shared(chain, tx, *this); auto ec = validate_tx->check_transaction(); - if (ec) - return ec; - - ec = validate_tx->check_transaction_connect_input(header.number); - if (ec) - return ec; + if (!ec) { + ec = validate_tx->check_transaction_connect_input(header.number); + } - for (auto& output : const_cast(tx).outputs) { + for (size_t i = 0; (!ec) && (i < tx.outputs.size()); ++i) { + const auto& output = tx.outputs[i]; if (output.is_asset_issue()) { auto r = assets.insert(output.get_asset_symbol()); if (r.second == false) { - return error::asset_exist; + log::debug(LOG_BLOCKCHAIN) + << "check_block asset " + output.get_asset_symbol() + << " already exists in block!" + << " " << tx.to_string(1); + ec = error::asset_exist; + break; } } else if (output.is_asset_cert()) { @@ -297,35 +301,58 @@ code validate_block::check_block(blockchain::block_chain_impl& chain) const auto r = asset_certs.insert(key); if (r.second == false) { log::debug(LOG_BLOCKCHAIN) - << " cert " + output.get_asset_cert_symbol() + << "check_block cert " + output.get_asset_cert_symbol() << " with type " << output.get_asset_cert_type() << " already exists in block!" << " " << tx.to_string(1); - return error::asset_cert_exist; + ec = error::asset_cert_exist; + break; } } else if (output.is_asset_mit()) { auto r = asset_mits.insert(output.get_asset_symbol()); if (r.second == false) { log::debug(LOG_BLOCKCHAIN) - << " mit " + output.get_asset_symbol() + << "check_block mit " + output.get_asset_symbol() << " already exists in block!" << " " << tx.to_string(1); - return error::mit_exist; + ec = error::mit_exist; + break; } } else if (output.is_did()) { auto didexist = dids.insert(output.get_did_symbol()); if (didexist.second == false) { - return error::did_exist; + log::debug(LOG_BLOCKCHAIN) + << "check_block did " + output.get_did_symbol() + << " already exists in block!" + << " " << tx.to_string(1); + ec = error::did_exist; + break; } auto didaddress = didaddreses.insert(output.get_did_address()); if (didaddress.second == false ) { - return error::address_registered_did; + log::debug(LOG_BLOCKCHAIN) + << "check_block did " + output.get_did_address() + << " address_registered_did!" + << " " << tx.to_string(1); + ec = error::address_registered_did; + break; } } } + + if (ec) { + if (!first_tx_ec) { + first_tx_ec = ec; + } + chain.pool().delete_tx(tx.hash()); + } + } + + if (first_tx_ec) { + return first_tx_ec; } RETURN_IF_STOPPED(); @@ -547,7 +574,7 @@ bool validate_block::is_valid_coinbase_height(size_t height, const block& block) return std::equal(expected.begin(), expected.end(), actual.begin()); } -code validate_block::connect_block(hash_digest& err_tx) const +code validate_block::connect_block(hash_digest& err_tx, blockchain::block_chain_impl& chain) const { err_tx = null_hash; const auto& transactions = current_block_.transactions; @@ -612,7 +639,7 @@ code validate_block::connect_block(hash_digest& err_tx) const RETURN_IF_STOPPED(); - if (!validate_transaction::tally_fees(tx, value_in, fees)) + if (!validate_transaction::tally_fees(chain, tx, value_in, fees)) { err_tx = tx.hash(); return error::fees_out_of_range; diff --git a/src/lib/blockchain/validate_block_impl.cpp b/src/lib/blockchain/validate_block_impl.cpp index 07d3d7527..e710742dc 100644 --- a/src/lib/blockchain/validate_block_impl.cpp +++ b/src/lib/blockchain/validate_block_impl.cpp @@ -35,26 +35,27 @@ namespace blockchain { static constexpr size_t median_time_past_blocks = 11; validate_block_impl::validate_block_impl(simple_chain& chain, - size_t fork_index, const block_detail::list& orphan_chain, - size_t orphan_index, size_t height, const chain::block& block, - bool testnet, const config::checkpoint::list& checks, - stopped_callback stopped) - : validate_block(height, block, testnet, checks, stopped), - chain_(chain), - height_(height), - fork_index_(fork_index), - orphan_index_(orphan_index), - orphan_chain_(orphan_chain) + size_t fork_index, const block_detail::list& orphan_chain, + size_t orphan_index, size_t height, const chain::block& block, + bool testnet, const config::checkpoint::list& checks, + stopped_callback stopped) + : validate_block(height, block, testnet, checks, stopped), + chain_(chain), + height_(height), + fork_index_(fork_index), + orphan_index_(orphan_index), + orphan_chain_(orphan_chain) { } bool validate_block_impl::is_valid_proof_of_work(const chain::header& header) const { chain::header parent_header; - if(orphan_index_ != 0) { + if (orphan_index_ != 0) { parent_header = orphan_chain_[orphan_index_ - 1]->actual()->header; - }else{ - static_cast(chain_).get_header(parent_header, header.number - 1); + } + else { + static_cast(chain_).get_header(parent_header, header.number - 1); } return MinerAux::verifySeal(const_cast(header), parent_header); } @@ -93,8 +94,7 @@ uint64_t validate_block_impl::actual_time_span(size_t interval) const BITCOIN_ASSERT(height_ > 0 && height_ >= interval); // height - interval and height - 1, return time difference - return fetch_block(height_ - 1).timestamp - - fetch_block(height_ - interval).timestamp; + return fetch_block(height_ - 1).timestamp - fetch_block(height_ - interval).timestamp; } uint64_t validate_block_impl::median_time_past() const @@ -113,8 +113,7 @@ uint64_t validate_block_impl::median_time_past() const chain::header validate_block_impl::fetch_block(size_t fetch_height) const { - if (fetch_height > fork_index_) - { + if (fetch_height > fork_index_) { const auto fetch_index = fetch_height - fork_index_ - 1; BITCOIN_ASSERT(fetch_index <= orphan_index_); BITCOIN_ASSERT(orphan_index_ < orphan_chain_.size()); @@ -122,7 +121,7 @@ chain::header validate_block_impl::fetch_block(size_t fetch_height) const } chain::header out; - DEBUG_ONLY(const auto result =) chain_.get_header(out, fetch_height); + DEBUG_ONLY(const auto result = ) chain_.get_header(out, fetch_height); BITCOIN_ASSERT(result); return out; } @@ -158,7 +157,7 @@ bool validate_block_impl::is_output_spent( } bool validate_block_impl::fetch_transaction(chain::transaction& tx, - size_t& tx_height, const hash_digest& tx_hash) const + size_t& tx_height, const hash_digest& tx_hash) const { uint64_t out_height; const auto result = chain_.get_transaction(tx, out_height, tx_hash); @@ -166,23 +165,20 @@ bool validate_block_impl::fetch_transaction(chain::transaction& tx, BITCOIN_ASSERT(out_height <= max_size_t); tx_height = static_cast(out_height); - if (!result || tx_after_fork(tx_height, fork_index_)) + if (!result || tx_after_fork(tx_height, fork_index_)) { return fetch_orphan_transaction(tx, tx_height, tx_hash); + } return true; } bool validate_block_impl::fetch_orphan_transaction(chain::transaction& tx, - size_t& tx_height, const hash_digest& tx_hash) const + size_t& tx_height, const hash_digest& tx_hash) const { - for (size_t orphan = 0; orphan <= orphan_index_; ++orphan) - { + for (size_t orphan = 0; orphan <= orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); - - for (const auto& orphan_tx: orphan_block->transactions) - { - if (orphan_tx.hash() == tx_hash) - { + for (const auto& orphan_tx : orphan_block->transactions) { + if (orphan_tx.hash() == tx_hash) { // TRANSACTION COPY tx = orphan_tx; tx_height = fork_index_ + orphan + 1; @@ -194,6 +190,165 @@ bool validate_block_impl::fetch_orphan_transaction(chain::transaction& tx, return false; } +std::string validate_block_impl::get_did_from_address_consider_orphan_chain( + const std::string& address, const std::string& did_symbol) const +{ + BITCOIN_ASSERT(!address.empty()); + + if (address.empty()) { + log::debug("blockchain") << "get_did_from_address_consider_orphan_chain: address is empty"; + return ""; + } + + auto orphan = orphan_index_; + while (orphan > 0) { + const auto& orphan_block = orphan_chain_[--orphan]->actual(); + for (const auto& orphan_tx : orphan_block->transactions) { + // iter outputs + for (const auto& output : orphan_tx.outputs) { + if (output.is_did_register() || output.is_did_transfer()) { + if (address == output.get_did_address()) { + return output.get_did_symbol(); + } + } + } + + // iter inputs + for (const auto& input : orphan_tx.inputs) { + size_t previous_height; + transaction previous_tx; + const auto& previous_output = input.previous_output; + + // This searches the blockchain and then the orphan pool up to and + // including the current (orphan) block and excluding blocks above fork. + if (!fetch_transaction(previous_tx, previous_height, previous_output.hash)) + { + log::warning(LOG_BLOCKCHAIN) + << "Failure fetching input transaction [" + << encode_hash(previous_output.hash) << "]"; + return ""; + } + + const auto& previous_tx_out = previous_tx.outputs[previous_output.index]; + + if (previous_tx_out.is_did_register() || previous_tx_out.is_did_transfer()) { + if (address == previous_tx_out.get_did_address()) { + return ""; + } + } + } + } + } + + return did_symbol; +} + +bool validate_block_impl::is_did_match_address_in_orphan_chain(const std::string& did, const std::string& address) const +{ + BITCOIN_ASSERT(!did.empty()); + BITCOIN_ASSERT(!address.empty()); + + if (address.empty()) { + log::debug("blockchain") << "check did match address in orphan chain: address is null for did: " << did; + return false; + } + + auto orphan = orphan_index_; + while (orphan > 0) { + const auto& orphan_block = orphan_chain_[--orphan]->actual(); + for (const auto& orphan_tx : orphan_block->transactions) { + for (auto& output : orphan_tx.outputs) { + if (output.is_did_register() || output.is_did_transfer()) { + if (did == output.get_did_symbol()) { + return address == output.get_did_address(); + } + } + } + } + } + + return false; +} + +bool validate_block_impl::is_did_in_orphan_chain(const std::string& did) const +{ + BITCOIN_ASSERT(!did.empty()); + + for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + const auto& orphan_block = orphan_chain_[orphan]->actual(); + for (const auto& orphan_tx : orphan_block->transactions) { + for (auto& output : orphan_tx.outputs) { + if (output.is_did_register()) { + if (did == output.get_did_symbol()) { + return true; + } + } + } + } + } + + return false; +} + +bool validate_block_impl::is_asset_in_orphan_chain(const std::string& symbol) const +{ + BITCOIN_ASSERT(!symbol.empty()); + + for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + const auto& orphan_block = orphan_chain_[orphan]->actual(); + for (const auto& orphan_tx : orphan_block->transactions) { + for (const auto& output : orphan_tx.outputs) { + if (output.is_asset_issue()) { + if (symbol == output.get_asset_symbol()) { + return true; + } + } + } + } + } + + return false; +} + +bool validate_block_impl::is_asset_cert_in_orphan_chain(const std::string& symbol, asset_cert_type cert_type) const +{ + BITCOIN_ASSERT(!symbol.empty()); + + for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + const auto& orphan_block = orphan_chain_[orphan]->actual(); + for (const auto& orphan_tx : orphan_block->transactions) { + for (const auto& output : orphan_tx.outputs) { + if (symbol == output.get_asset_cert_symbol() && + cert_type == output.get_asset_cert_type()) { + return true; + } + } + } + } + + return false; +} + +bool validate_block_impl::is_asset_mit_in_orphan_chain(const std::string& symbol) const +{ + BITCOIN_ASSERT(!symbol.empty()); + + for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + const auto& orphan_block = orphan_chain_[orphan]->actual(); + for (const auto& orphan_tx : orphan_block->transactions) { + for (const auto& output : orphan_tx.outputs) { + if (output.is_asset_mit_register()) { + if (symbol == output.get_asset_mit_symbol()) { + return true; + } + } + } + } + } + + return false; +} + bool validate_block_impl::is_output_spent( const chain::output_point& previous_output, size_t index_in_parent, size_t input_index) const @@ -214,27 +369,21 @@ bool validate_block_impl::orphan_is_spent( const chain::output_point& previous_output, size_t skip_tx, size_t skip_input) const { - for (size_t orphan = 0; orphan <= orphan_index_; ++orphan) - { + for (size_t orphan = 0; orphan <= orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); const auto& transactions = orphan_block->transactions; BITCOIN_ASSERT(!transactions.empty()); BITCOIN_ASSERT(transactions.front().is_coinbase()); - for (size_t tx_index = 0; tx_index < transactions.size(); - ++tx_index) - { + for (size_t tx_index = 0; tx_index < transactions.size(); ++tx_index) { // TODO: too deep, move this section to subfunction. const auto& orphan_tx = transactions[tx_index]; - for (size_t input_index = 0; input_index < orphan_tx.inputs.size(); - ++input_index) - { + for (size_t input_index = 0; input_index < orphan_tx.inputs.size(); ++input_index) { const auto& orphan_input = orphan_tx.inputs[input_index]; - if (orphan == orphan_index_ && tx_index == skip_tx && - input_index == skip_input) + if (orphan == orphan_index_ && tx_index == skip_tx && input_index == skip_input) continue; if (orphan_input.previous_output == previous_output) @@ -254,11 +403,12 @@ bool validate_block_impl::check_get_coinage_reward_transaction(const chain::tran wallet::payment_address addr2 = wallet::payment_address::extract(output.script); uint64_t coinage_reward_value = libbitcoin::consensus::miner::calculate_lockblock_reward(lock_height, output.value); - if(addr1 == addr2 - && lock_height == coinbase_lock_height - && coinage_reward_value == coinage_reward_coinbase.outputs[0].value) { + if (addr1 == addr2 + && lock_height == coinbase_lock_height + && coinage_reward_value == coinage_reward_coinbase.outputs[0].value) { return true; - } else { + } + else { return false; } } diff --git a/src/lib/blockchain/validate_transaction.cpp b/src/lib/blockchain/validate_transaction.cpp index ea6c22778..690167f0b 100644 --- a/src/lib/blockchain/validate_transaction.cpp +++ b/src/lib/blockchain/validate_transaction.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -316,7 +317,7 @@ code validate_transaction::check_tx_connect_input() const { uint64_t fee = 0; - if (!tally_fees(*tx_, value_in_, fee)) { + if (!tally_fees(blockchain_, *tx_, value_in_, fee)) { return error::fees_out_of_range; } @@ -559,7 +560,7 @@ code validate_transaction::check_asset_issue_transaction() const if (!check_same(asset_address, detail.get_address())) { return error::asset_issue_error; } - if (chain.is_asset_exist(asset_symbol, false)) { + if (check_asset_exist(asset_symbol)) { return error::asset_exist; } if (operation::is_pay_key_hash_with_attenuation_model_pattern(output.script.operations)) { @@ -710,7 +711,7 @@ code validate_transaction::check_asset_cert_transaction() const } // check cert not exists - if (chain.is_asset_cert_exist(cert_symbol, cur_cert_type)) { + if (check_asset_cert_exist(cert_symbol, cur_cert_type)) { log::debug(LOG_BLOCKCHAIN) << "issue cert: " << cert_info.get_symbol() << " already exists."; return error::asset_cert_exist; @@ -731,14 +732,6 @@ code validate_transaction::check_asset_cert_transaction() const << cert_info.get_symbol() << " does not match."; return error::asset_cert_error; } - - // check cert exists - auto cur_cert_type = cert_info.get_type(); - if (!chain.is_asset_cert_exist(cert_symbol, cur_cert_type)) { - log::debug(LOG_BLOCKCHAIN) << "transfer cert: " - << cert_info.get_symbol() << " not exists."; - return error::asset_cert_not_exist; - } } else if (output.is_asset_cert()) { asset_cert&& cert_info = output.get_asset_cert(); @@ -818,7 +811,7 @@ code validate_transaction::check_asset_cert_transaction() const } // check asset not exist. - if (chain.is_asset_exist(cert_symbol, false)) { + if (check_asset_exist(cert_symbol)) { log::debug(LOG_BLOCKCHAIN) << "issue cert: " << "asset symbol '" + cert_symbol + "' already exists in blockchain!"; return error::asset_exist; @@ -866,7 +859,7 @@ code validate_transaction::check_asset_mit_transaction() const } // check asset not exists - if (nullptr != chain.get_registered_mit(asset_symbol)) { + if (check_asset_mit_exist(asset_symbol)) { log::debug(LOG_BLOCKCHAIN) << "register MIT: " << asset_symbol << " already exists."; return error::mit_exist; @@ -880,13 +873,6 @@ code validate_transaction::check_asset_mit_transaction() const auto&& asset_info = output.get_asset_mit(); asset_symbol = asset_info.get_symbol(); - - // check asset exists - if (nullptr == chain.get_registered_mit(asset_symbol)) { - log::debug(LOG_BLOCKCHAIN) << "transfer MIT: " - << asset_symbol << " not exists."; - return error::mit_not_exist; - } } else if (output.is_etp()) { if (!check_same(asset_address, output.get_script_address())) { @@ -949,10 +935,96 @@ code validate_transaction::check_asset_mit_transaction() const return error::success; } +bool validate_transaction::check_did_exist(const std::string& did) const +{ + uint64_t height = blockchain_.get_did_height(did); + + if (validate_block_ ) { + //register before fork or find in orphan chain + if (height <= validate_block_->get_fork_index() || validate_block_->is_did_in_orphan_chain(did)) { + return true; + } + + return false; + } + + return height != max_uint64; +} + +bool validate_transaction::check_asset_exist(const std::string& symbol) const +{ + uint64_t height = blockchain_.get_asset_height(symbol); + + if (validate_block_) { + //register before fork or find in orphan chain + if (height <= validate_block_->get_fork_index() || validate_block_->is_asset_in_orphan_chain(symbol)) { + return true; + } + + return false; + } + + return height != max_uint64; +} + +bool validate_transaction::check_asset_cert_exist(const std::string& cert, asset_cert_type cert_type) const +{ + uint64_t height = blockchain_.get_asset_cert_height(cert, cert_type); + + if (validate_block_) { + //register before fork or find in orphan chain + if (height <= validate_block_->get_fork_index() || validate_block_->is_asset_cert_in_orphan_chain(cert, cert_type)) { + return true; + } + + return false; + } + + return height != max_uint64; +} + +bool validate_transaction::check_asset_mit_exist(const std::string& mit) const +{ + uint64_t height = blockchain_.get_asset_mit_height(mit); + + if (validate_block_) { + //register before fork or find in orphan chain + if (height <= validate_block_->get_fork_index() || validate_block_->is_asset_mit_in_orphan_chain(mit)) { + return true; + } + + return false; + } + + return height != max_uint64; +} + +bool validate_transaction::check_address_registered_did(const std::string& address) const +{ + uint64_t fork_index = validate_block_ ? validate_block_->get_fork_index() : max_uint64; + auto did_symbol = blockchain_.get_did_from_address(address, fork_index); + + if (!validate_block_) { + if (did_symbol.empty()) { + return false; + } + } + else { + did_symbol = validate_block_->get_did_from_address_consider_orphan_chain(address, did_symbol); + if (did_symbol.empty()) { + return false; + } + } + + log::debug(LOG_BLOCKCHAIN) << "address " << address << " already exists did " << did_symbol; + return true; +} + code validate_transaction::check_did_transaction() const { const chain::transaction& tx = *tx_; blockchain::block_chain_impl& chain = blockchain_; + uint64_t fork_index = validate_block_ ? validate_block_->get_fork_index() : max_uint64; code ret = error::success; @@ -964,11 +1036,11 @@ code validate_transaction::check_did_transaction() const return ret; //to_did check(strong check) - if ((ret = output.check_attachment_did_match_address(chain)) != error::success) + if ((ret = check_attachment_to_did(output)) != error::success) return ret; //from_did (weak check) - if ((ret = connect_input_address_match_did(output)) != error::success) { + if ((ret = connect_attachment_from_did(output)) != error::success) { return ret; } @@ -977,11 +1049,15 @@ code validate_transaction::check_did_transaction() const return error::did_symbol_invalid; } - if (chain.is_did_exist(output.get_did_symbol())) { + if (check_did_exist(output.get_did_symbol())) { + log::debug(LOG_BLOCKCHAIN) << "did_register: " + << output.get_did_symbol() << " already exists"; return error::did_exist; } - if (chain.is_address_registered_did(output.get_did_address())) { + if (check_address_registered_did(output.get_did_address())) { + log::debug(LOG_BLOCKCHAIN) << "address " + << output.get_did_address() << " already exists did, cannot register did."; return error::address_registered_did; } @@ -995,12 +1071,9 @@ code validate_transaction::check_did_transaction() const } } else if (output.is_did_transfer()) { - //did transfer only for did is exist - if (!chain.is_did_exist(output.get_did_symbol())) { - return error::did_not_exist; - } - - if (chain.is_address_registered_did(output.get_did_address())) { + if (check_address_registered_did(output.get_did_address())) { + log::debug(LOG_BLOCKCHAIN) << "address " + << output.get_did_address() << " already exists did, cannot transfer did."; return error::address_registered_did; } @@ -1082,35 +1155,83 @@ bool validate_transaction::connect_did_input(const did& info) const || (found_address_info && info.get_status() == DID_DETAIL_TYPE); } -code validate_transaction::connect_input_address_match_did(const output& output) const +bool validate_transaction::is_did_match_address_in_orphan_chain(const std::string& did, const std::string& address) const { - const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + if (validate_block_ && validate_block_->is_did_match_address_in_orphan_chain(did, address)) { + log::debug(LOG_BLOCKCHAIN) << "did_in_orphan_chain: " + << did << ", match address: " << address; + return true; + } - const attachment& attach = output.attach_data; + return false; +} + +bool validate_transaction::is_did_in_orphan_chain(const std::string& did) const +{ + if (validate_block_ && validate_block_->is_did_in_orphan_chain(did)) { + log::debug(LOG_BLOCKCHAIN) << "did_in_orphan_chain: " << did << " exist"; + return true; + } + + return false; +} - if (attach.get_from_did().empty()) { +code validate_transaction::check_attachment_to_did(const output& output) const +{ + auto todid = output.attach_data.get_to_did(); + if (todid.empty()) { return error::success; } - for ( auto & input : tx.inputs) - { + auto address = output.get_script_address(); + + if (is_did_match_address_in_orphan_chain(todid, address)) { + return error::success; + } + + uint64_t fork_index = validate_block_ ? validate_block_->get_fork_index() : max_uint64; + auto did = blockchain_.get_did_from_address(address, fork_index); + if (todid == did) { + return error::success; + } + + log::debug(LOG_BLOCKCHAIN) << "check_attachment_to_did: " + << todid << ", address: " << address + << "; get did from address is " << did; + return error::did_address_not_match; +} + +code validate_transaction::connect_attachment_from_did(const output& output) const +{ + auto from_did = output.attach_data.get_from_did(); + if (from_did.empty()) { + return error::success; + } + + for (auto& input : tx_->inputs) { chain::transaction prev_tx; uint64_t prev_height{0}; if (!get_previous_tx(prev_tx, prev_height, input)) { - log::debug(LOG_BLOCKCHAIN) << "connect_input_address_match_did: input not found: " + log::debug(LOG_BLOCKCHAIN) << "connect_attachment_from_did: input not found: " << encode_hash(input.previous_output.hash); return error::input_not_found; } auto prev_output = prev_tx.outputs.at(input.previous_output.index); auto address = prev_output.get_script_address(); - if (attach.get_from_did() == chain.get_did_from_address(address)) { + + if (is_did_match_address_in_orphan_chain(from_did, address)) { return error::success; } - } + uint64_t fork_index = validate_block_ ? validate_block_->get_fork_index() : max_uint64; + if (from_did == blockchain_.get_did_from_address(address, fork_index)) { + return error::success; + } + } + log::debug(LOG_BLOCKCHAIN) << "connect_attachment_from_did: input not found for from_did: " + << from_did; return error::did_address_not_match; } @@ -1231,7 +1352,7 @@ code validate_transaction::check_transaction_basic() const } else if (output.is_asset_cert()) { auto&& asset_cert = output.get_asset_cert(); - if (!chain.is_did_exist(asset_cert.get_owner())) { + if (!check_did_exist(asset_cert.get_owner())) { return error::did_address_needed; } } @@ -1426,8 +1547,81 @@ bool validate_transaction::connect_input( const transaction& previous_tx, size_t return value_in_ <= max_money(); } -bool validate_transaction::tally_fees(const transaction& tx, uint64_t value_in, - uint64_t& total_fees) +bool validate_transaction::check_special_fees(bool is_testnet, const chain::transaction& tx, uint64_t fee) +{ + // check fee of issue asset or register did + auto developer_community_address = bc::get_developer_community_address(is_testnet); + std::vector etp_vec; + uint32_t special_fee_type = 0; + std::string to_address; + for (auto& output: tx.outputs) { + if (output.is_etp()) { + if (output.get_script_address() == developer_community_address) { + etp_vec.push_back(output.value); + } + else { + etp_vec.push_back(0); + } + } + else if (output.is_asset_issue()) { + special_fee_type = 1; + to_address = output.get_script_address(); + } + else if (output.is_did_register()) { + special_fee_type = 2; + to_address = output.get_script_address(); + } + } + + // skip transaction to developer community address + if (special_fee_type > 0 && to_address != developer_community_address) { + uint64_t fee_to_developer = std::accumulate(etp_vec.begin() , etp_vec.end(), (uint64_t)0); + if (fee_to_developer > 0) { + uint64_t total_fee = fee + fee_to_developer; + uint32_t percentage_to_miner = (uint32_t)std::ceil((fee * 100.0) / total_fee); + + bool result = false; + if (special_fee_type == 1) { + result = (total_fee >= bc::min_fee_to_issue_asset + && percentage_to_miner >= min_fee_percentage_to_miner); + + } + else if (special_fee_type == 2) { + result = (total_fee >= bc::min_fee_to_register_did + && percentage_to_miner >= min_fee_percentage_to_miner); + } + + if (!result) { + log::debug(LOG_BLOCKCHAIN) << "check fees failed: " + << (special_fee_type == 1 ? "issue asset" : "register did") + << ", total_fee: " << std::to_string(total_fee) + << ", fee_percentage_to_miner: " << std::to_string(percentage_to_miner); + return false; + } + } + else { + if (special_fee_type == 1) { + if (fee < bc::min_fee_to_issue_asset) { + log::debug(LOG_BLOCKCHAIN) << "check fees failed: fee for issuing asset less than " + << std::to_string(bc::min_fee_to_issue_asset); + return false; + } + } + else if (special_fee_type == 2) { + if (fee < bc::min_fee_to_register_did) { + log::debug(LOG_BLOCKCHAIN) << "check fees failed: fee for registerring did less than " + << std::to_string(bc::min_fee_to_register_did); + return false; + } + } + } + } + + return true; +} + +bool validate_transaction::tally_fees(blockchain::block_chain_impl& chain, + const transaction& tx, uint64_t value_in, uint64_t& total_fees) { const auto value_out = tx.total_output_value(); @@ -1435,8 +1629,10 @@ bool validate_transaction::tally_fees(const transaction& tx, uint64_t value_in, return false; const auto fee = value_in - value_out; - if (fee < min_tx_fee) + if (fee < min_tx_fee) { return false; + } + total_fees += fee; return total_fees <= max_money(); } diff --git a/src/lib/consensus/miner.cpp b/src/lib/consensus/miner.cpp index 2cb83272e..e4eeac206 100644 --- a/src/lib/consensus/miner.cpp +++ b/src/lib/consensus/miner.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #define LOG_HEADER "consensus" using namespace std; @@ -82,7 +83,7 @@ miner::~miner() } bool miner::get_input_etp(const transaction& tx, const std::vector& transactions, - uint64_t& total_inputs, previous_out_map_t& previous_out_map) const + uint64_t& total_inputs, previous_out_map_t& previous_out_map) const { total_inputs = 0; block_chain_impl& block_chain = node_.chain_impl(); @@ -122,7 +123,7 @@ bool miner::get_input_etp(const transaction& tx, const std::vector& transactions, - previous_out_map_t& previous_out_map, tx_fee_map_t& tx_fee_map) const + previous_out_map_t& previous_out_map, tx_fee_map_t& tx_fee_map) const { boost::mutex mutex; mutex.lock(); @@ -140,13 +141,30 @@ bool miner::get_transaction(std::vector& transactions, for (auto i = transactions.begin(); i != transactions.end(); ) { auto& tx = **i; auto hash = tx.hash(); - auto transaction_is_ok = true; - uint64_t fee = 0; + uint64_t total_input_value = 0; + bool ready = get_input_etp(tx, transactions, total_input_value, previous_out_map); + if (!ready) { + // erase tx but not delete it from pool if parent tx is not ready + i = transactions.erase(i); + break; + } + uint64_t total_output_value = tx.total_output_value(); + uint64_t fee = total_input_value - total_output_value; + + // check fees + if (fee < min_tx_fee || !blockchain::validate_transaction::check_special_fees(setting_.use_testnet_rules, tx, fee)) { + i = transactions.erase(i); + // delete it from pool if not enough fee + node_.pool().delete_tx(hash); + break; + } + + auto transaction_is_ok = true; for (auto& output : tx.outputs) { if (tx.version >= transaction_version::check_output_script - && output.script.pattern() == script_pattern::non_standard) { + && output.script.pattern() == script_pattern::non_standard) { #ifdef MVS_DEBUG log::error(LOG_HEADER) << "transaction output script error! tx:" << tx.to_string(1); #endif @@ -154,43 +172,6 @@ bool miner::get_transaction(std::vector& transactions, transaction_is_ok = false; break; } - - uint64_t total_input_value = 0; - bool ready = get_input_etp(tx, transactions, total_input_value, previous_out_map); - if (!ready) { - // erase tx but not delete it from pool if parent tx is not ready - transaction_is_ok = false; - break; - } - - uint64_t total_output_value = tx.total_output_value(); - - // check normal fee - if (total_input_value < total_output_value + min_tx_fee) { - transaction_is_ok = false; - } - else { - fee = total_input_value - total_output_value; - // check fee for issue asset - if (output.is_asset_issue() && fee < coin_price(10)) { - transaction_is_ok = false; - } - // check fee for issue did - else if (output.is_did_register() && fee < coin_price(1)) { - transaction_is_ok = false; - } - } - - if (!transaction_is_ok) { -#ifdef MVS_DEBUG - log::debug(LOG_HEADER) << "not enough fee! input: " - << total_input_value << ", output: " << total_output_value - << ", tx: " << tx.to_string(1); -#endif - // delete ifrom pool if not enough fee - node_.pool().delete_tx(hash); - break; - } } if (transaction_is_ok && (sets.find(hash) == sets.end())) { diff --git a/src/lib/database/data_base.cpp b/src/lib/database/data_base.cpp index 2abd25fb9..b508cf1f8 100644 --- a/src/lib/database/data_base.cpp +++ b/src/lib/database/data_base.cpp @@ -813,12 +813,12 @@ void data_base::push(const block& block, uint64_t height) if (!tx.is_coinbase()) push_inputs(tx_hash, height, tx.inputs); - std::string didaddress = tx.get_did_transfer_old_address(); - if (!didaddress.empty()) { - data_chunk data(didaddress.begin(), didaddress.end()); - short_hash key = ripemd160_hash(data); - address_dids.delete_old_did(key); - } + // std::string didaddress = tx.get_did_transfer_old_address(); + // if (!didaddress.empty()) { + // data_chunk data(didaddress.begin(), didaddress.end()); + // short_hash key = ripemd160_hash(data); + // address_dids.delete_old_did(key); + // } // Add outputs push_outputs(tx_hash, height, tx.outputs); diff --git a/src/lib/database/databases/address_asset_database.cpp b/src/lib/database/databases/address_asset_database.cpp index bf761c1a2..00482d95c 100644 --- a/src/lib/database/databases/address_asset_database.cpp +++ b/src/lib/database/databases/address_asset_database.cpp @@ -778,6 +778,41 @@ business_address_asset_cert::list address_asset_database::get_asset_certs(const return unspent; } + +business_history::list address_asset_database::get_asset_certs_history(const std::string& address, + const std::string& symbol, asset_cert_type cert_type, + size_t from_height) const +{ + data_chunk data(address.begin(), address.end()); + auto key = ripemd160_hash(data); + business_history::list result = get_business_history(key, from_height); + business_history::list unspent(result.size()); + + auto it = std::copy_if(result.begin(), result.end(), unspent.begin(), [&symbol,&cert_type](business_history & row) + { + if ((row.data.get_kind_value() != business_kind::asset_cert)) // asset_cert + return false; + + auto cert_info = boost::get(row.data.get_data()); + if (!symbol.empty()) { + if (symbol != cert_info.get_symbol()) { + return false; + } + } + + if (cert_type != asset_cert_ns::none) { + if (cert_type != cert_info.get_type()) { + return false; + } + } + return true; + }); + + unspent.resize(std::distance(unspent.begin(),it)); + return unspent; +} + + void address_asset_database::sync() { lookup_manager_.sync(); diff --git a/src/lib/database/databases/address_did_database.cpp b/src/lib/database/databases/address_did_database.cpp index f2f1698c5..8fa588a6f 100644 --- a/src/lib/database/databases/address_did_database.cpp +++ b/src/lib/database/databases/address_did_database.cpp @@ -673,7 +673,7 @@ business_address_did::list address_did_database::get_dids(const std::string& add // get all kinds of did in the database(blockchain) business_address_did::list address_did_database::get_dids(const std::string& address, - size_t from_height) const + size_t from_height, size_t to_height) const { data_chunk data(address.begin(), address.end()); auto key = ripemd160_hash(data); @@ -685,6 +685,13 @@ business_address_did::list address_did_database::get_dids(const std::string& add && (row.data.get_kind_value() != business_kind::did_transfer)) // did_transfer continue; + if(address != wallet::payment_address::blackhole_address) + { + if (row.output_height > to_height) { + continue; + } + } + uint8_t status = 0xff; if (row.spend.hash == null_hash) status = 0; // 0 -- unspent 1 -- confirmed diff --git a/src/lib/database/databases/blockchain_asset_cert_database.cpp b/src/lib/database/databases/blockchain_asset_cert_database.cpp index 7902624c0..0c8b53f43 100644 --- a/src/lib/database/databases/blockchain_asset_cert_database.cpp +++ b/src/lib/database/databases/blockchain_asset_cert_database.cpp @@ -145,6 +145,7 @@ std::shared_ptr> blockchain_asset_cert_database::get_blo return vec_acc; } + void blockchain_asset_cert_database::store(const asset_cert& sp_cert) { auto&& key_str = sp_cert.get_key(); diff --git a/src/lib/database/databases/blockchain_asset_database.cpp b/src/lib/database/databases/blockchain_asset_database.cpp index 3c6e36b3a..69173bb07 100644 --- a/src/lib/database/databases/blockchain_asset_database.cpp +++ b/src/lib/database/databases/blockchain_asset_database.cpp @@ -165,6 +165,34 @@ std::shared_ptr> blockchain_asset_database::get_bl return vec_acc; } +/// +std::shared_ptr blockchain_asset_database::get_register_history(const std::string & asset_symbol) const +{ + std::shared_ptr blockchain_asset_ = nullptr; + data_chunk data(asset_symbol.begin(), asset_symbol.end()); + auto key = sha256_hash(data); + + auto memo = lookup_map_.rfind(key); + if(memo) + { + blockchain_asset_ = std::make_shared(); + const auto memory = REMAP_ADDRESS(memo); + auto deserial = make_deserializer_unsafe(memory); + blockchain_asset_->from_data(deserial); + } + + return blockchain_asset_; +} + +/// +uint64_t blockchain_asset_database::get_register_height(const std::string & asset_symbol) const +{ + std::shared_ptr blockchain_asset_ = get_register_history(asset_symbol); + if(blockchain_asset_) + return blockchain_asset_->get_height(); + return max_uint64; +} + void blockchain_asset_database::store(const hash_digest& hash, const blockchain_asset& sp_detail) { // Write block data. diff --git a/src/lib/database/databases/blockchain_did_database.cpp b/src/lib/database/databases/blockchain_did_database.cpp index 92790b6f3..877e7b4cb 100644 --- a/src/lib/database/databases/blockchain_did_database.cpp +++ b/src/lib/database/databases/blockchain_did_database.cpp @@ -131,8 +131,6 @@ std::shared_ptr blockchain_did_database::get(const hash_digest& std::shared_ptr> blockchain_did_database::get_history_dids(const hash_digest &hash) const { auto did_details = std::make_shared>(); - if (!did_details) - return nullptr; const auto raw_memory_vec = lookup_map_.finds(hash); for(const auto& raw_memory : raw_memory_vec) { @@ -169,6 +167,35 @@ std::shared_ptr> blockchain_did_database::get_blockc return vec_acc; } +/// +std::shared_ptr blockchain_did_database::get_register_history(const std::string & did_symbol) const +{ + std::shared_ptr blockchain_did_ = nullptr; + data_chunk data(did_symbol.begin(), did_symbol.end()); + auto key = sha256_hash(data); + + auto memo = lookup_map_.rfind(key); + if(memo) + { + blockchain_did_ = std::make_shared(); + const auto memory = REMAP_ADDRESS(memo); + auto deserial = make_deserializer_unsafe(memory); + blockchain_did_->from_data(deserial); + } + + return blockchain_did_; +} + + uint64_t blockchain_did_database::get_register_height(const std::string & did_symbol) const + { + std::shared_ptr blockchain_did_ = get_register_history(did_symbol); + if(blockchain_did_) + return blockchain_did_->get_height(); + + return max_uint64; + } + + void blockchain_did_database::store(const hash_digest& hash, const blockchain_did& sp_detail) { // Write block data. @@ -219,6 +246,39 @@ std::shared_ptr blockchain_did_database::update_address_status(c return detail; } +std::shared_ptr > blockchain_did_database::getdids_from_address_history(const std::string &address, + const uint64_t &fromheight, const uint64_t &toheight) const +{ + auto vec_acc = std::make_shared>(); + uint64_t i = 0; + for( i = 0; i < number_buckets; i++ ) { + auto sp_memo = lookup_map_.find(i); + for(auto& elem : *sp_memo) + { + const auto memory = REMAP_ADDRESS(elem); + auto deserial = make_deserializer_unsafe(memory); + blockchain_did blockchain_did_ = blockchain_did::factory_from_data(deserial); + + const auto height = blockchain_did_.get_height(); + const auto did_address = blockchain_did_.get_did().get_address(); + + if (did_address == address) { + if((height >= fromheight && height <= toheight) + || (height == max_uint32 && address == wallet::payment_address::blackhole_address)) { + vec_acc->emplace_back(blockchain_did_); + } + } + } + } + + std::sort(vec_acc->begin(), vec_acc->end(), []( blockchain_did & first, blockchain_did & second) + { + return first.get_height() < second.get_height(); + }); + return vec_acc; + +} + std::shared_ptr blockchain_did_database::pop_did_transfer(const hash_digest &hash) { lookup_map_.unlink(hash); diff --git a/src/lib/database/databases/blockchain_mit_database.cpp b/src/lib/database/databases/blockchain_mit_database.cpp index f6eca3961..588d3459e 100644 --- a/src/lib/database/databases/blockchain_mit_database.cpp +++ b/src/lib/database/databases/blockchain_mit_database.cpp @@ -143,6 +143,35 @@ std::shared_ptr blockchain_mit_database::get_blockchain_mi return vec_acc; } +/// +std::shared_ptr blockchain_mit_database::get_register_history(const std::string & mit_symbol) const +{ + std::shared_ptr asset_mit_ = nullptr; + data_chunk data(mit_symbol.begin(), mit_symbol.end()); + auto key = sha256_hash(data); + + auto memo = lookup_map_.rfind(key); + if(memo) + { + asset_mit_ = std::make_shared(); + const auto memory = REMAP_ADDRESS(memo); + auto deserial = make_deserializer_unsafe(memory); + *asset_mit_ = asset_mit_info::factory_from_data(deserial); + } + + return asset_mit_; +} + +/// +uint64_t blockchain_mit_database::get_register_height(const std::string & mit_symbol) const +{ + std::shared_ptr asset_mit_ = get_register_history(mit_symbol); + if(asset_mit_) + return asset_mit_->output_height; + + return max_uint64; +} + void blockchain_mit_database::store(const asset_mit_info& mit_info) { const auto& key_str = mit_info.mit.get_symbol(); diff --git a/src/lib/explorer/extensions/base_helper.cpp b/src/lib/explorer/extensions/base_helper.cpp index fd222b528..c0303b460 100644 --- a/src/lib/explorer/extensions/base_helper.cpp +++ b/src/lib/explorer/extensions/base_helper.cpp @@ -256,10 +256,75 @@ void sync_fetch_asset_balance(const std::string& address, bool sum_all, } } +static uint32_t get_domain_cert_count(bc::blockchain::block_chain_impl& blockchain, + const std::string& account_name) +{ + auto pvaddr = blockchain.get_account_addresses(account_name); + if (!pvaddr) { + return 0; + } + + auto sh_vec = std::make_shared(); + for (auto& each : *pvaddr){ + sync_fetch_asset_cert_balance(each.get_address(), "", blockchain, sh_vec, asset_cert_ns::domain); + } + + return sh_vec->size(); +} + +void sync_fetch_deposited_balance(wallet::payment_address& address, + bc::blockchain::block_chain_impl& blockchain, std::shared_ptr sh_vec) +{ + chain::transaction tx_temp; + uint64_t tx_height; + uint64_t height = 0; + blockchain.get_last_height(height); + + auto&& address_str = address.encoded(); + auto&& rows = blockchain.get_address_history(address, false); + for (auto& row: rows) { + if (row.output_height == 0) { + continue; + } + + // spend unconfirmed (or no spend attempted) + if ((row.spend.hash == null_hash) + && blockchain.get_transaction(row.output.hash, tx_temp, tx_height)) { + BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); + auto output = tx_temp.outputs.at(row.output.index); + + if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) { + // deposit utxo in block + uint64_t deposit_height = chain::operation:: + get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); + uint64_t expiration_height = row.output_height + deposit_height; + + if (expiration_height > height) { + auto&& tx_hash = encode_hash(tx_temp.hash()); + const auto match = [&tx_hash](const deposited_balance& balance) { + return balance.tx_hash == tx_hash; + }; + auto iter = std::find_if(sh_vec->begin(), sh_vec->end(), match); + if (iter != sh_vec->end()) { + // interest of deposit + iter->balance += row.value; + } + else { + auto&& row_hash = encode_hash(row.output.hash); + deposited_balance balance(address_str, tx_hash, row_hash, + row.value, deposit_height, expiration_height); + sh_vec->push_back(std::move(balance)); + } + } + } + } + } +} + void sync_fetchbalance(wallet::payment_address& address, bc::blockchain::block_chain_impl& blockchain, balances& addr_balance) { - auto&& rows = blockchain.get_address_history(address); + auto&& rows = blockchain.get_address_history(address, false); uint64_t total_received = 0; uint64_t confirmed_balance = 0; @@ -271,9 +336,10 @@ void sync_fetchbalance(wallet::payment_address& address, uint64_t height = 0; blockchain.get_last_height(height); - for (auto& row: rows) - { - total_received += row.value; + for (auto& row: rows) { + if (row.output_height == 0) { + continue; + } // spend unconfirmed (or no spend attempted) if ((row.spend.hash == null_hash) @@ -282,23 +348,17 @@ void sync_fetchbalance(wallet::payment_address& address, auto output = tx_temp.outputs.at(row.output.index); if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) { - if (row.output_height == 0) { - // deposit utxo in transaction pool + // deposit utxo in block + uint64_t lock_height = chain::operation:: + get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); + if ((row.output_height + lock_height) > height) { + // utxo already in block but deposit not expire frozen_balance += row.value; } - else { - // deposit utxo in block - uint64_t lock_height = chain::operation:: - get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); - if((row.output_height + lock_height) > height) { - // utxo already in block but deposit not expire - frozen_balance += row.value; - } - } } else if (tx_temp.is_coinbase()) { // coin base etp maturity etp check // add not coinbase_maturity etp into frozen - if ((row.output_height == 0) || ((row.output_height + coinbase_maturity) > height)) { + if ((row.output_height + coinbase_maturity) > height) { frozen_balance += row.value; } } @@ -306,8 +366,9 @@ void sync_fetchbalance(wallet::payment_address& address, unspent_balance += row.value; } - if (row.output_height != 0 && - (row.spend.hash == null_hash || row.spend_height == 0)) + total_received += row.value; + + if ((row.spend.hash == null_hash || row.spend_height == 0)) confirmed_balance += row.value; } @@ -1385,13 +1446,23 @@ void issuing_asset::sum_payment_amount() throw asset_symbol_notfound_exception{symbol_ + " not created"}; } - if (payment_etp_ < 1000000000) { // 10 etp now - throw asset_issue_poundage_exception{"fee must at least 1000000000 satoshi == 10 etp"}; + uint64_t min_fee = bc::min_fee_to_issue_asset; + if (payment_etp_ < min_fee) { + throw asset_issue_poundage_exception("fee must at least " + + std::to_string(min_fee) + " satoshi == " + + std::to_string(min_fee/100000000) + " etp"); } if (!attenuation_model_param_.empty()) { check_model_param_initial(attenuation_model_param_, unissued_asset_->get_maximum_supply()); } + + uint64_t amount = (uint64_t)std::floor(payment_etp_ * ((100 - fee_percentage_to_miner_) / 100.0)); + if (amount > 0) { + auto&& address = bc::get_developer_community_address(blockchain_.chain_settings().use_testnet_rules); + auto&& did = blockchain_.get_did_from_address(address); + receiver_list_.push_back({address, "", amount, 0, utxo_attach_type::etp, attachment("", did)}); + } } chain::operation::stack @@ -1544,8 +1615,19 @@ void issuing_asset_cert::sum_payment_amount() void registering_did::sum_payment_amount() { base_transfer_common::sum_payment_amount(); - if (payment_etp_ < 100000000) { - throw did_register_poundage_exception{"fee must at least 100000000 satoshi == 1 etp"}; + + uint64_t min_fee = bc::min_fee_to_register_did; + if (payment_etp_ < min_fee) { + throw did_register_poundage_exception("fee must at least " + + std::to_string(min_fee) + " satoshi == " + + std::to_string(min_fee/100000000) + " etp"); + } + + uint64_t amount = (uint64_t)std::floor(payment_etp_ * ((100 - fee_percentage_to_miner_) / 100.0)); + if (amount > 0) { + auto&& address = bc::get_developer_community_address(blockchain_.chain_settings().use_testnet_rules); + auto&& did = blockchain_.get_did_from_address(address); + receiver_list_.push_back({address, "", amount, 0, utxo_attach_type::etp, attachment("", did)}); } } diff --git a/src/lib/explorer/extensions/commands/burn.cpp b/src/lib/explorer/extensions/commands/burn.cpp index dbfed13d7..7a4630e38 100644 --- a/src/lib/explorer/extensions/commands/burn.cpp +++ b/src/lib/explorer/extensions/commands/burn.cpp @@ -45,7 +45,7 @@ console_result burn::invoke(Json::Value& jv_output, std::stringstream sout(""); const char* cmds[]{"didsendasset", auth_.name.c_str(), auth_.auth.c_str(), blackhole_did.c_str(), argument_.symbol.c_str(), amount.c_str()}; - if (dispatch_command(6, cmds, jv_output, node, 2) != console_result::okay) { + if (dispatch_command(6, cmds, jv_output, node, get_api_version()) != console_result::okay) { throw address_generate_exception(sout.str()); } diff --git a/src/lib/explorer/extensions/commands/changepasswd.cpp b/src/lib/explorer/extensions/commands/changepasswd.cpp index 6288ff9d3..10e621b1f 100644 --- a/src/lib/explorer/extensions/commands/changepasswd.cpp +++ b/src/lib/explorer/extensions/commands/changepasswd.cpp @@ -63,7 +63,7 @@ console_result changepasswd::invoke(Json::Value& jv_output, auto& jv = jv_output; jv["name"] = auth_.name; - jv["status"] = "password changed"; + jv["status"] = "changed successfully"; return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/createasset.cpp b/src/lib/explorer/extensions/commands/createasset.cpp index ce4666bbe..7f26325d0 100644 --- a/src/lib/explorer/extensions/commands/createasset.cpp +++ b/src/lib/explorer/extensions/commands/createasset.cpp @@ -107,9 +107,15 @@ console_result createasset::invoke(Json::Value& jv_output, blockchain.store_account_asset(acc, auth_.name); - Json::Value asset_data = config::json_helper(get_api_version()).prop_list(*acc, true); - asset_data["status"] = "unissued"; - jv_output["asset"] = asset_data; + if (get_api_version() <= 2) { + Json::Value asset_data = config::json_helper(get_api_version()).prop_list(*acc, true); + asset_data["status"] = "unissued"; + jv_output["asset"] = asset_data; + } + else { + jv_output = config::json_helper(get_api_version()).prop_list(*acc, true); + jv_output["status"] = "unissued"; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/createmultisigtx.cpp b/src/lib/explorer/extensions/commands/createmultisigtx.cpp index cb75f9f5a..b51238946 100644 --- a/src/lib/explorer/extensions/commands/createmultisigtx.cpp +++ b/src/lib/explorer/extensions/commands/createmultisigtx.cpp @@ -94,15 +94,7 @@ console_result createmultisigtx::invoke( // output json auto && tx = sp_send_helper->get_transaction(); - std::ostringstream tx_buf; - tx_buf << config::transaction(tx); - if (get_api_version() <= 2) { - jv_output = tx_buf.str(); - } - else { - // TODO support restful API format - jv_output["raw"] = tx_buf.str(); - } + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false, true); return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/createrawtx.cpp b/src/lib/explorer/extensions/commands/createrawtx.cpp index c2ad93bf6..24de0b42e 100644 --- a/src/lib/explorer/extensions/commands/createrawtx.cpp +++ b/src/lib/explorer/extensions/commands/createrawtx.cpp @@ -19,6 +19,7 @@ */ +#include #include #include #include @@ -137,15 +138,7 @@ console_result createrawtx::invoke(Json::Value& jv_output, auto&& tx = sp_send_helper->get_transaction(); // output json - std::ostringstream tx_buf; - tx_buf << config::transaction(tx); - if (get_api_version() <= 2) { - jv_output["hex"] = tx_buf.str(); - } - else { - // TODO support restful API format - jv_output["raw"] = tx_buf.str(); - } + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false); return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/decoderawtx.cpp b/src/lib/explorer/extensions/commands/decoderawtx.cpp index 057fbd280..6da644168 100644 --- a/src/lib/explorer/extensions/commands/decoderawtx.cpp +++ b/src/lib/explorer/extensions/commands/decoderawtx.cpp @@ -33,7 +33,7 @@ console_result decoderawtx::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { tx_type tx_ = argument_.transaction; - jv_output = config::json_helper(get_api_version()).prop_tree(tx_, true); + jv_output = config::json_helper(get_api_version()).prop_tree(tx_, true); return console_result::okay; } @@ -41,4 +41,3 @@ console_result decoderawtx::invoke(Json::Value& jv_output, } // namespace commands } // namespace explorer } // namespace libbitcoin - diff --git a/src/lib/explorer/extensions/commands/deleteaccount.cpp b/src/lib/explorer/extensions/commands/deleteaccount.cpp index 9b757831d..8b687903d 100644 --- a/src/lib/explorer/extensions/commands/deleteaccount.cpp +++ b/src/lib/explorer/extensions/commands/deleteaccount.cpp @@ -46,10 +46,8 @@ console_result deleteaccount::invoke(Json::Value& jv_output, // delete account blockchain.delete_account(acc->get_name()); - auto& jv = jv_output; - jv["name"] = acc->get_name(); - jv["status"]= "removed successfully"; - + jv_output["name"] = acc->get_name(); + jv_output["status"]= "removed successfully"; return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/deletelocalasset.cpp b/src/lib/explorer/extensions/commands/deletelocalasset.cpp index 70bc1e1c8..37a7a5f08 100644 --- a/src/lib/explorer/extensions/commands/deletelocalasset.cpp +++ b/src/lib/explorer/extensions/commands/deletelocalasset.cpp @@ -86,12 +86,15 @@ console_result deletelocalasset::invoke(Json::Value& jv_output, throw asset_notfound_exception{"asset " + option_.symbol + " does not existed or do not belong to " + auth_.name + "."}; } - Json::Value result; - result["symbol"] = option_.symbol; - result["operate"] = "delete"; - result["result"] = "success"; - - jv_output = result; + if (get_api_version() <= 2) { + jv_output["symbol"] = option_.symbol; + jv_output["operate"] = "delete"; + jv_output["result"] = "success"; + } + else { + jv_output["symbol"] = option_.symbol; + jv_output["status"]= "deleted successfully"; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/deletemultisig.cpp b/src/lib/explorer/extensions/commands/deletemultisig.cpp index 14d195098..1c93677ca 100644 --- a/src/lib/explorer/extensions/commands/deletemultisig.cpp +++ b/src/lib/explorer/extensions/commands/deletemultisig.cpp @@ -88,20 +88,20 @@ console_result deletemultisig::invoke(Json::Value& jv_output, } // output json + jv_output.resize(0); auto helper = config::json_helper(get_api_version()); if (get_api_version() <= 2) { auto& acc_multisig = *(multisig_vec->begin()); jv_output = helper.prop_list(acc_multisig); } else { - // TODO support new functionality Json::Value nodes; for(auto& acc_multisig : *multisig_vec) { Json::Value node = helper.prop_list(acc_multisig); nodes.append(node); } - jv_output["multisig"] = nodes; + jv_output = nodes; } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/didchangeaddress.cpp b/src/lib/explorer/extensions/commands/didchangeaddress.cpp index 72281b8d2..66317ce29 100644 --- a/src/lib/explorer/extensions/commands/didchangeaddress.cpp +++ b/src/lib/explorer/extensions/commands/didchangeaddress.cpp @@ -111,11 +111,7 @@ console_result didchangeaddress::invoke(Json::Value& jv_output, send_helper.exec(); // json output auto && tx = send_helper.get_transaction(); - std::ostringstream tx_buf; - tx_buf << config::transaction(tx); - jv_output = tx_buf.str(); - // TODO support restful API format - // jv_output["raw"] = tx_buf.str(); + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false, true); } else { diff --git a/src/lib/explorer/extensions/commands/dumpkeyfile.cpp b/src/lib/explorer/extensions/commands/dumpkeyfile.cpp index 02c373fe7..77c80f683 100644 --- a/src/lib/explorer/extensions/commands/dumpkeyfile.cpp +++ b/src/lib/explorer/extensions/commands/dumpkeyfile.cpp @@ -56,6 +56,7 @@ console_result dumpkeyfile::invoke(Json::Value& jv_output, //file_root["accounts"] = ss.str(); Json::Value multisig_lst; + multisig_lst.resize(0); for (auto ms : acc->get_multisig_vec()) { Json::Value multisig; multisig["m"] = ms.get_m(); diff --git a/src/lib/explorer/extensions/commands/getaccount.cpp b/src/lib/explorer/extensions/commands/getaccount.cpp index c5f2f7fd4..9d0471906 100644 --- a/src/lib/explorer/extensions/commands/getaccount.cpp +++ b/src/lib/explorer/extensions/commands/getaccount.cpp @@ -33,7 +33,7 @@ using namespace bc::explorer::config; /************************ getaccount *************************/ console_result getaccount::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); auto acc = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); @@ -42,14 +42,23 @@ console_result getaccount::invoke(Json::Value& jv_output, std::string&& mnemonic = blockchain.is_account_lastwd_valid(*acc, auth_.auth, argument_.last_word); auto& root = jv_output; - root["name"] = acc->get_name(); - root["mnemonic-key"] = mnemonic; + if (get_api_version() == 1) { - root["address-count"] += acc->get_hd_index(); - root["user-status"] += acc->get_user_status(); - } else { - root["address-count"] = acc->get_hd_index(); - root["user-status"] = acc->get_user_status(); + root["name"] = acc->get_name(); + root["mnemonic-key"] = mnemonic; + root["address-count"] += acc->get_hd_index() + 1; + root["user-status"] += (uint8_t)account_status::normal; + } + else if (get_api_version() == 2) { + root["name"] = acc->get_name(); + root["mnemonic-key"] = mnemonic; + root["address-count"] = acc->get_hd_index() + 1; + root["user-status"] = (uint8_t)account_status::normal; + } + else { + root["name"] = acc->get_name(); + root["mnemonic"] = mnemonic; + root["address_count"] = acc->get_hd_index() + 1; } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/getaccountasset.cpp b/src/lib/explorer/extensions/commands/getaccountasset.cpp index ffa28515c..46a5def46 100644 --- a/src/lib/explorer/extensions/commands/getaccountasset.cpp +++ b/src/lib/explorer/extensions/commands/getaccountasset.cpp @@ -66,6 +66,10 @@ console_result getaccountasset::invoke(Json::Value& jv_output, Json::Value asset_cert = json_helper.prop_list(elem); json_value.append(asset_cert); + + if (!argument_.symbol.empty()) { + break; + } } } else { @@ -91,6 +95,10 @@ console_result getaccountasset::invoke(Json::Value& jv_output, Json::Value asset_data = json_helper.prop_list(elem, *issued_asset); asset_data["status"] = "unspent"; json_value.append(asset_data); + + if (!argument_.symbol.empty()) { + break; + } } // 2. get asset in local database @@ -101,18 +109,29 @@ console_result getaccountasset::invoke(Json::Value& jv_output, // symbol filter if(!argument_.symbol.empty() && argument_.symbol != symbol) continue; + Json::Value asset_data = json_helper.prop_list(elem.detail, false); asset_data["status"] = "unissued"; json_value.append(asset_data); + + if (!argument_.symbol.empty()) { + break; + } } } if (get_api_version() == 1 && json_value.isNull()) { //compatible for v1 jv_output[json_key] = ""; } - else { + else if (get_api_version() <= 2) { jv_output[json_key] = json_value; } + else { + if(json_value.isNull()) + json_value.resize(0); + + jv_output = json_value; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getaddressasset.cpp b/src/lib/explorer/extensions/commands/getaddressasset.cpp index 7618c6ac3..38594b648 100644 --- a/src/lib/explorer/extensions/commands/getaddressasset.cpp +++ b/src/lib/explorer/extensions/commands/getaddressasset.cpp @@ -42,7 +42,7 @@ console_result getaddressasset::invoke(Json::Value& jv_output, std::string json_key; Json::Value json_value; - auto json_helper = config::json_helper(get_api_version()); + auto json_helper = config::json_helper(get_api_version());; if (option_.is_cert) { // only get asset certs json_key = "assetcerts"; @@ -75,9 +75,15 @@ console_result getaddressasset::invoke(Json::Value& jv_output, if (get_api_version() == 1 && json_value.isNull()) { //compatible for v1 jv_output[json_key] = ""; } - else { + else if (get_api_version() <= 2) { jv_output[json_key] = json_value; } + else { + if(json_value.isNull()) + json_value.resize(0); + + jv_output = json_value; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getaddressetp.cpp b/src/lib/explorer/extensions/commands/getaddressetp.cpp index bd4f50ca1..6443da16f 100644 --- a/src/lib/explorer/extensions/commands/getaddressetp.cpp +++ b/src/lib/explorer/extensions/commands/getaddressetp.cpp @@ -32,7 +32,7 @@ using namespace bc::explorer::config; /************************ getaddressetp *************************/ console_result getaddressetp::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); auto& addr = argument_.address; @@ -48,14 +48,20 @@ console_result getaddressetp::invoke(Json::Value& jv_output, jv["received"] = std::to_string(addr_balance.total_received); jv["unspent"] = std::to_string(addr_balance.unspent_balance); jv["frozen"] = std::to_string(addr_balance.frozen_balance); - } else { + } + else { jv["confirmed"] = addr_balance.confirmed_balance; jv["received"] = addr_balance.total_received; jv["unspent"] = addr_balance.unspent_balance; jv["frozen"] = addr_balance.frozen_balance; } - jv_output["balance"] = jv; + if (get_api_version() <= 2) { + jv_output["balance"] = jv; + } + else { + jv_output = jv; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getasset.cpp b/src/lib/explorer/extensions/commands/getasset.cpp index e9259921c..2c3ba3715 100644 --- a/src/lib/explorer/extensions/commands/getasset.cpp +++ b/src/lib/explorer/extensions/commands/getasset.cpp @@ -111,9 +111,15 @@ console_result getasset::invoke(Json::Value& jv_output, if (get_api_version() == 1 && json_value.isNull()) { //compatible for v1 jv_output[json_key] = ""; } - else { + else if (get_api_version() <= 2) { jv_output[json_key] = json_value; } + else { + if(json_value.isNull()) + json_value.resize(0); + + jv_output = json_value; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getbalance.cpp b/src/lib/explorer/extensions/commands/getbalance.cpp index 9050371d7..37c35ec79 100644 --- a/src/lib/explorer/extensions/commands/getbalance.cpp +++ b/src/lib/explorer/extensions/commands/getbalance.cpp @@ -60,19 +60,27 @@ console_result getbalance::invoke(Json::Value& jv_output, total_frozen += addr_balance.frozen_balance; } - if (get_api_version() == 1){ + if (get_api_version() == 1) { aroot["total-confirmed"] += total_confirmed; aroot["total-received"] += total_received; aroot["total-unspent"] += total_unspent; aroot["total-available"] += (total_unspent - total_frozen); aroot["total-frozen"] += total_frozen; - } else { + } + else if (get_api_version() == 2) { aroot["total-confirmed"] = total_confirmed; aroot["total-received"] = total_received; aroot["total-unspent"] = total_unspent; aroot["total-available"] = (total_unspent - total_frozen); aroot["total-frozen"] = total_frozen; } + else { + aroot["total_confirmed"] = total_confirmed; + aroot["total_received"] = total_received; + aroot["total_unspent"] = total_unspent; + aroot["total_available"] = (total_unspent - total_frozen); + aroot["total_frozen"] = total_frozen; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getblock.cpp b/src/lib/explorer/extensions/commands/getblock.cpp index 1fa552b57..212d78b9a 100644 --- a/src/lib/explorer/extensions/commands/getblock.cpp +++ b/src/lib/explorer/extensions/commands/getblock.cpp @@ -33,7 +33,7 @@ namespace commands { /************************ getblock *************************/ console_result getblock::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto json = option_.json; auto tx_json = option_.tx_json; @@ -46,18 +46,17 @@ console_result getblock::invoke(Json::Value& jv_output, std::promise p; auto& blockchain = node.chain_impl(); - blockchain.fetch_block(block_height, [&p, &jv_output, json, tx_json, this](const code& ec, chain::block::ptr block) { + blockchain.fetch_block(block_height, [&p, &jv_output, json, tx_json, this](const code & ec, chain::block::ptr block) { if (ec) { p.set_value(ec); return; } if (json) { - jv_output = config::json_helper(get_api_version()).prop_tree(*block, json, tx_json); + jv_output = config::json_helper(get_api_version()).prop_tree(*block, json, tx_json); } - else - { - jv_output = config::json_helper(get_api_version()).prop_tree(*block, false, false); + else { + jv_output = config::json_helper(get_api_version()).prop_tree(*block, false, false); } p.set_value(error::success); }); @@ -66,27 +65,25 @@ console_result getblock::invoke(Json::Value& jv_output, if (result) { throw block_height_get_exception{ result.message() }; } - } - else { + else { // fetch_block via hash bc::config::hash256 block_hash(argument_.hash_or_height); std::promise p; auto& blockchain = node.chain_impl(); - blockchain.fetch_block(block_hash, [&p, &jv_output, json, tx_json, this](const code& ec, chain::block::ptr block) { + blockchain.fetch_block(block_hash, [&p, &jv_output, json, tx_json, this](const code & ec, chain::block::ptr block) { if (ec) { p.set_value(ec); return; } if (json) { - jv_output = config::json_helper(get_api_version()).prop_tree(*block, json, tx_json); + jv_output = config::json_helper(get_api_version()).prop_tree(*block, json, tx_json); } - else - { - jv_output = config::json_helper(get_api_version()).prop_tree(*block, false, false); + else { + jv_output = config::json_helper(get_api_version()).prop_tree(*block, false, false); } p.set_value(error::success); }); @@ -95,7 +92,6 @@ console_result getblock::invoke(Json::Value& jv_output, if (result) { throw block_height_get_exception{ result.message() }; } - } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/getblockheader.cpp b/src/lib/explorer/extensions/commands/getblockheader.cpp index 251118a92..d73d04bbd 100644 --- a/src/lib/explorer/extensions/commands/getblockheader.cpp +++ b/src/lib/explorer/extensions/commands/getblockheader.cpp @@ -38,13 +38,14 @@ using namespace bc::explorer::config; /************************ getblockheader *************************/ console_result getblockheader::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { uint64_t height = 0; auto& blockchain = node.chain_impl(); - if(!blockchain.get_last_height(height)) + if (!blockchain.get_last_height(height)) { throw block_last_height_get_exception{"query last height failure."}; + } if (option_.height != std::numeric_limits::max()) { height = option_.height; @@ -54,38 +55,54 @@ console_result getblockheader::invoke(Json::Value& jv_output, obelisk_client client(connection); - if (!client.connect(connection)) - { + if (!client.connect(connection)) { throw connection_exception{"Could not connect to mvsd port 9921."}; } + encoding json_format{"json"}; std::ostringstream output; callback_state state(output, output, json_format); - auto on_done = [this, &jv_output](const chain::header& header) + auto on_done = [this, &jv_output](const chain::header & header) { auto&& jheader = config::json_helper(get_api_version()).prop_tree(header); - if( !jheader.isObject() - || !jheader["result"].isObject() - || !jheader["result"]["hash"].isString()) { + if (get_api_version() <= 2) { + if ( !jheader.isObject() + || !jheader["result"].isObject() + || !jheader["result"]["hash"].isString()) { + throw block_hash_get_exception{"getbestblockhash got parser exception."}; + } - throw block_hash_get_exception{"getbestblockhash got parser exception."}; + if (option_.is_getbestblockhash) { + auto&& blockhash = jheader["result"]["hash"].asString(); + jv_output = blockhash; + } + else { + if (get_api_version() == 1) { + jv_output = jheader; + } + else { + jv_output = jheader["result"]; + } + } } + else { + if (!jheader.isObject() || !jheader["hash"].isString()) { + throw block_hash_get_exception{"getbestblockhash parser exception."}; + } - if (option_.is_getbestblockhash) { - auto&& blockhash = jheader["result"]["hash"].asString(); - jv_output = blockhash; - } else { - if (get_api_version() == 1) { + if (option_.is_getbestblockhash) { + auto&& blockhash = jheader["hash"].asString(); + jv_output = blockhash; + } + else { jv_output = jheader; - } else { - jv_output = jheader["result"]; } } }; - auto on_error = [&state](const code& error) + auto on_error = [&state](const code & error) { state.succeeded(error); }; @@ -93,10 +110,12 @@ console_result getblockheader::invoke(Json::Value& jv_output, // Height is ignored if both are specified. // Use the null_hash as sentinel to determine whether to use height or hash. const hash_digest& hash = option_.hash; - if (hash == null_hash) + if (hash == null_hash) { client.blockchain_fetch_block_header(on_error, on_done, height); - else + } + else { client.blockchain_fetch_block_header(on_error, on_done, hash); + } client.wait(); diff --git a/src/lib/explorer/extensions/commands/getdid.cpp b/src/lib/explorer/extensions/commands/getdid.cpp index eb982b007..1d6919643 100644 --- a/src/lib/explorer/extensions/commands/getdid.cpp +++ b/src/lib/explorer/extensions/commands/getdid.cpp @@ -31,73 +31,72 @@ namespace explorer { namespace commands { console_result getdid::invoke (Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { Json::Value json_value; auto& blockchain = node.chain_impl(); - if(option_.symbol.empty()) - { + if (option_.symbol.empty()) { + auto sh_vec = blockchain.get_registered_dids(); + std::sort(sh_vec->begin(), sh_vec->end()); // add blockchain dids - for (auto& elem: *sh_vec) + for (auto& elem : *sh_vec) { json_value.append(elem.get_symbol()); + } - if (get_api_version() == 1 && json_value.isNull()) { //compatible for v1 - jv_output["dids"] = ""; + if (get_api_version() <= 2) { + jv_output["dids"] = json_value; } else { - jv_output["dids"] = json_value; + jv_output = json_value; } } - else - { - + else { auto didSymbol = option_.symbol; - if(blockchain.is_valid_address(didSymbol)) - { + if (blockchain.is_valid_address(didSymbol)) { didSymbol = blockchain.get_did_from_address(didSymbol); - if(didSymbol.empty()) + if (didSymbol.empty()) { throw address_not_bound_did_exception{"address is not binded with some did on the blockchain"}; + } } // check did symbol check_did_symbol(didSymbol); // check did exists - if (!blockchain.is_did_exist(didSymbol)) + if (!blockchain.is_did_exist(didSymbol)) { throw did_symbol_notfound_exception{"did symbol does not exist on the blockchain"}; + } auto blockchain_dids = blockchain.get_did_history_addresses(didSymbol); if (blockchain_dids) { Json::Value json_address; Json::Value did_data; - for (auto &did : *blockchain_dids){ + for (auto &did : *blockchain_dids) { did_data["address"] = did.get_did().get_address(); did_data["status"] = did.get_status_string(); + if (get_api_version() >= 3) { + did_data["symbol"] = didSymbol; + } json_value.append(did_data); } - - - if (get_api_version() == 1 && json_value.isNull()) { //compatible for v1 - jv_output["did"] = didSymbol; - jv_output["addresses"] = ""; - } - else { + + if (get_api_version() <= 2) { jv_output["did"] = didSymbol; jv_output["addresses"] = json_value; } + else { + if(json_value.isNull()) + json_value.resize(0); + jv_output = json_value; + } } - - } - - - return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getinfo.cpp b/src/lib/explorer/extensions/commands/getinfo.cpp index a5dd01fdd..20102e9a9 100644 --- a/src/lib/explorer/extensions/commands/getinfo.cpp +++ b/src/lib/explorer/extensions/commands/getinfo.cpp @@ -33,26 +33,17 @@ using namespace bc::explorer::config; /************************ getinfo *************************/ console_result getinfo::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); administrator_required_checker(node, auth_.name, auth_.auth); - auto& jv = jv_output; - jv["protocol-version"] = node.network_settings().protocol; - jv["wallet-version"] = MVS_EXPLORER_VERSION; - jv["database-version"] = MVS_DATABASE_VERSION; - jv["testnet"] = blockchain.chain_settings().use_testnet_rules; - jv["peers"] = get_connections_count(node); - auto sh_vec = blockchain.get_issued_assets(); std::set symbols; - for (const auto& elem: *sh_vec) { + for (const auto& elem : *sh_vec) { symbols.insert(elem.get_symbol()); } - jv["network-assets-count"] = static_cast(symbols.size()); - jv["wallet-account-count"] = static_cast(blockchain.get_accounts()->size()); uint64_t height; uint64_t rate; @@ -60,11 +51,37 @@ console_result getinfo::invoke(Json::Value& jv_output, bool is_solo_mining; node.miner().get_state(height, rate, difficulty, is_solo_mining); - jv["height"] = height; - jv["difficulty"] = difficulty; - jv["is-mining"] = is_solo_mining; - jv["hash-rate"] = rate; + auto& jv = jv_output; + if (get_api_version() <= 2) { + jv["protocol-version"] = node.network_settings().protocol; + jv["wallet-version"] = MVS_EXPLORER_VERSION; + jv["database-version"] = MVS_DATABASE_VERSION; + jv["testnet"] = blockchain.chain_settings().use_testnet_rules; + jv["peers"] = get_connections_count(node); + + jv["network-assets-count"] = static_cast(symbols.size()); + jv["wallet-account-count"] = static_cast(blockchain.get_accounts()->size()); + jv["height"] = height; + jv["difficulty"] = difficulty; + jv["is-mining"] = is_solo_mining; + jv["hash-rate"] = rate; + } + else { + jv["protocol_version"] = node.network_settings().protocol; + jv["wallet_version"] = MVS_EXPLORER_VERSION; + jv["database_version"] = MVS_DATABASE_VERSION; + jv["testnet"] = blockchain.chain_settings().use_testnet_rules; + jv["peers"] = get_connections_count(node); + + jv["asset_count"] = static_cast(symbols.size()); + jv["wallet_account_count"] = static_cast(blockchain.get_accounts()->size()); + + jv["height"] = height; + jv["difficulty"] = difficulty; + jv["is_mining"] = is_solo_mining; + jv["hash_rate"] = rate; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getmemorypool.cpp b/src/lib/explorer/extensions/commands/getmemorypool.cpp index 80ae00fef..1fa3dabae 100644 --- a/src/lib/explorer/extensions/commands/getmemorypool.cpp +++ b/src/lib/explorer/extensions/commands/getmemorypool.cpp @@ -34,7 +34,7 @@ namespace commands { /************************ getbalance *************************/ console_result getmemorypool::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { administrator_required_checker(node, auth_.name, auth_.auth); @@ -43,28 +43,29 @@ console_result getmemorypool::invoke(Json::Value& jv_output, using transaction_ptr = message::transaction_message::ptr ; auto& blockchain = node.chain_impl(); std::promise p; - blockchain.pool().fetch([&jv_output, &p, &json, this](const code& ec, const std::vector& txs){ - if (ec) - { + blockchain.pool().fetch([&jv_output, &p, &json, this](const code & ec, const std::vector& txs) { + if (ec) { p.set_value(ec); return; } + std::vector txs1; txs1.reserve(txs.size()); - for (auto tp:txs) { + for (auto tp : txs) { txs1.push_back(*tp); } - if(json) { - jv_output = config::json_helper(get_api_version()).prop_tree(txs1, true); - } else { - jv_output = config::json_helper(get_api_version()).prop_tree(txs1, false); + if (json) { + jv_output = config::json_helper(get_api_version()).prop_tree(txs1, true); + } + else { + jv_output = config::json_helper(get_api_version()).prop_tree(txs1, false); } p.set_value(ec); }); auto result = p.get_future().get(); - if(result){ + if (result) { throw tx_fetch_exception{result.message()}; } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/getmininginfo.cpp b/src/lib/explorer/extensions/commands/getmininginfo.cpp index c59246d4a..783b677ac 100644 --- a/src/lib/explorer/extensions/commands/getmininginfo.cpp +++ b/src/lib/explorer/extensions/commands/getmininginfo.cpp @@ -32,25 +32,32 @@ using namespace bc::explorer::config; /************************ getmininginfo *************************/ console_result getmininginfo::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { administrator_required_checker(node, auth_.name, auth_.auth); - auto& aroot = jv_output; - Json::Value info; - - auto& miner = node.miner(); - uint64_t height, rate; std::string difficulty; bool is_mining; + auto& miner = node.miner(); miner.get_state(height, rate, difficulty, is_mining); - info["is-mining"] = is_mining; - info["height"] += height; - info["rate"] += rate; - info["difficulty"] = difficulty; - aroot["mining-info"] = info; + + if (get_api_version() <= 2) { + auto& aroot = jv_output; + Json::Value info; + info["is-mining"] = is_mining; + info["height"] += height; + info["rate"] += rate; + info["difficulty"] = difficulty; + aroot["mining-info"] = info; + } + else { + jv_output["is_mining"] = is_mining; + jv_output["height"] += height; + jv_output["rate"] += rate; + jv_output["difficulty"] = difficulty; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getmit.cpp b/src/lib/explorer/extensions/commands/getmit.cpp index 8e6180675..498e64c36 100644 --- a/src/lib/explorer/extensions/commands/getmit.cpp +++ b/src/lib/explorer/extensions/commands/getmit.cpp @@ -78,7 +78,12 @@ console_result getmit::invoke(Json::Value& jv_output, json_value.append(elem.mit.get_symbol()); } - jv_output["mits"] = json_value; + if (get_api_version() <=2 ) { + jv_output["mits"] = json_value; + } + else { + jv_output = json_value; + } } else { if (option_.show_history) { @@ -88,7 +93,15 @@ console_result getmit::invoke(Json::Value& jv_output, json_value.append(asset_data); } - jv_output["mits"] = json_value; + if (get_api_version() <=2 ) { + jv_output["mits"] = json_value; + } + else { + if(json_value.isNull()) + json_value.resize(0); + + jv_output = json_value; + } } else { if (option_.show_current) { diff --git a/src/lib/explorer/extensions/commands/getnewaccount.cpp b/src/lib/explorer/extensions/commands/getnewaccount.cpp index 985583da2..4cd9cea42 100644 --- a/src/lib/explorer/extensions/commands/getnewaccount.cpp +++ b/src/lib/explorer/extensions/commands/getnewaccount.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ - +#include #include #include #include @@ -36,7 +36,6 @@ using namespace bc::explorer::config; console_result getnewaccount::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { - #ifdef NDEBUG if (auth_.name.length() > 128 || auth_.name.length() < 3 || auth_.auth.length() > 128 || auth_.auth.length() < 6) @@ -48,8 +47,6 @@ console_result getnewaccount::invoke(Json::Value& jv_output, throw account_existed_exception{"account already exist"}; } - auto& root = jv_output; - auto acc = std::make_shared(); acc->set_name(auth_.name); acc->set_passwd(auth_.auth); @@ -59,7 +56,6 @@ console_result getnewaccount::invoke(Json::Value& jv_output, auto&& words_list = get_mnemonic_new(opt_language , seed); auto&& words = bc::join(words_list); - root["mnemonic"] = words; acc->set_mnemonic(words, auth_.auth); // flush to db @@ -70,17 +66,27 @@ console_result getnewaccount::invoke(Json::Value& jv_output, Json::Value jv_temp; const char* cmds2[]{"getnewaddress", auth_.name.c_str(), auth_.auth.c_str()}; - if (dispatch_command(3, cmds2, jv_temp, node, 2) != console_result::okay) { + if (dispatch_command(3, cmds2, jv_temp, node, get_api_version()) != console_result::okay) { throw address_generate_exception(sout.str()); } - root["default-address"] = jv_temp["addresses"][0].asString(); + if (get_api_version() == 1) { + jv_output["mnemonic"] = words; + jv_output["default-address"] = jv_temp; + } + else if (get_api_version() == 2) { + jv_output["mnemonic"] = words; + jv_output["default-address"] = jv_temp["addresses"][0].asString(); + } + else { + config::json_helper::account_info acc(auth_.name, words, jv_temp); + jv_output = config::json_helper(get_api_version()).prop_list(acc); + } return console_result::okay; } - } // namespace commands } // namespace explorer } // namespace libbitcoin diff --git a/src/lib/explorer/extensions/commands/getnewaddress.cpp b/src/lib/explorer/extensions/commands/getnewaddress.cpp index 58323bc2f..a1f8b1a7c 100644 --- a/src/lib/explorer/extensions/commands/getnewaddress.cpp +++ b/src/lib/explorer/extensions/commands/getnewaddress.cpp @@ -54,7 +54,7 @@ console_result getnewaddress::invoke(Json::Value& jv_output, } Json::Value addresses; - + std::vector> account_addresses; account_addresses.reserve(option_.count); const auto seed = decode_mnemonic(words); @@ -108,9 +108,14 @@ console_result getnewaddress::invoke(Json::Value& jv_output, if (get_api_version() == 1 && option_.count == 1) { jv_output = addresses[0]; } - else { + else if (get_api_version() <= 2) { jv_output["addresses"] = addresses; } + else { + if(addresses.isNull()) + addresses.resize(0); + jv_output = addresses; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getpeerinfo.cpp b/src/lib/explorer/extensions/commands/getpeerinfo.cpp index a2145322a..30457023f 100644 --- a/src/lib/explorer/extensions/commands/getpeerinfo.cpp +++ b/src/lib/explorer/extensions/commands/getpeerinfo.cpp @@ -34,20 +34,28 @@ using namespace bc::explorer::config; /************************ getpeerinfo *************************/ console_result getpeerinfo::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { - administrator_required_checker(node, auth_.name, auth_.auth); - auto& root = jv_output; Json::Value array; - for(auto authority : node.connections_ptr()->authority_list()) { + for (auto authority : node.connections_ptr()->authority_list()) { // invalid authority if (authority.to_hostname() == "[::]" && authority.port() == 0) continue; array.append(authority.to_string()); } - root["peers"] = array; + + if (get_api_version() <= 2) { + auto& root = jv_output; + root["peers"] = array; + } + else { + if(array.isNull()) + array.resize(0); + + jv_output = array; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getpublickey.cpp b/src/lib/explorer/extensions/commands/getpublickey.cpp index 318b3475b..0c9f2513b 100644 --- a/src/lib/explorer/extensions/commands/getpublickey.cpp +++ b/src/lib/explorer/extensions/commands/getpublickey.cpp @@ -71,8 +71,14 @@ console_result getpublickey::invoke(Json::Value& jv_output, } auto& root = jv_output; - root["public-key"] = pub_key; - root["address"] = argument_.address; + if (get_api_version() <= 2) { + root["public-key"] = pub_key; + root["address"] = argument_.address; + } + else { + root["public_key"] = pub_key; + root["address"] = argument_.address; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/gettx.cpp b/src/lib/explorer/extensions/commands/gettx.cpp index 5d8595e77..7a48f3891 100644 --- a/src/lib/explorer/extensions/commands/gettx.cpp +++ b/src/lib/explorer/extensions/commands/gettx.cpp @@ -33,25 +33,27 @@ namespace commands { /************************ gettx *************************/ /// extent fetch-tx command , add tx height in tx content console_result gettx::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { bc::chain::transaction tx; uint64_t tx_height = 0; auto& blockchain = node.chain_impl(); auto exist = blockchain.get_transaction(argument_.hash, tx, tx_height); - if(!exist) + if (!exist) { throw tx_notfound_exception{"transaction does not exist!"}; + } + auto json_helper = config::json_helper(get_api_version()); if (option_.json) { if (get_api_version() == 1 && option_.is_fetch_tx) { // compatible for v1 fetch-tx - jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); - } else { - jv_output = config::json_helper(get_api_version()).prop_list(tx, tx_height, true); + jv_output = json_helper.prop_tree(tx, true); } - - } else { - config::transaction config_tx{tx}; - jv_output = config::json_helper(get_api_version()).prop_tree(config_tx, false); + else { + jv_output = json_helper.prop_list(tx, tx_height, true); + } + } + else { + jv_output = json_helper.prop_tree(tx, false); } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/importaccount.cpp b/src/lib/explorer/extensions/commands/importaccount.cpp index 4a3ee33b0..eddefc537 100644 --- a/src/lib/explorer/extensions/commands/importaccount.cpp +++ b/src/lib/explorer/extensions/commands/importaccount.cpp @@ -47,8 +47,7 @@ console_result importaccount::invoke(Json::Value& jv_output, throw argument_exceed_limit_exception{"name length in [3, 128], password length in [6, 128]"}; #endif - if (argument_.words.size() == 1) - { + if (argument_.words.size() == 1) { argument_.words = bc::split(argument_.words[0], " ", true); } @@ -70,25 +69,38 @@ console_result importaccount::invoke(Json::Value& jv_output, blockchain.store_account(acc); // generate all account address - auto& root = jv_output; - root["name"] = auth_.name; - root["mnemonic"] = mnemonic; - if (get_api_version() == 1) { - root["hd_index"] += option_.hd_index; - } else { - root["hd_index"] = option_.hd_index; - } - - uint32_t idx = 0; auto&& str_idx = std::to_string(option_.hd_index); const char* cmds2[]{"getnewaddress", auth_.name.c_str(), option_.passwd.c_str(), "-n", str_idx.c_str()}; Json::Value addresses; - if (dispatch_command(5, cmds2, addresses, node, 2) != console_result::okay) { + if (dispatch_command(5, cmds2, addresses, node, get_api_version()) != console_result::okay) { throw address_generate_exception{"getnewaddress got exception."}; } - root["addresses"] = addresses["addresses"]; + if (get_api_version() <= 2) { + if (get_api_version() == 1) { + jv_output["hd_index"] += option_.hd_index; + if (option_.hd_index == 1) { + Json::Value addr; + addr.append(addresses.asString()); + jv_output["addresses"] = addr; + } + else { + jv_output["addresses"] = addresses["addresses"]; + } + } + else if (get_api_version() == 2) { + jv_output["hd_index"] = option_.hd_index; + jv_output["addresses"] = addresses["addresses"]; + } + + jv_output["name"] = auth_.name; + jv_output["mnemonic"] = mnemonic; + } + else { + config::json_helper::account_info acc(auth_.name, mnemonic, addresses); + jv_output = config::json_helper(get_api_version()).prop_list(acc); + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/importkeyfile.cpp b/src/lib/explorer/extensions/commands/importkeyfile.cpp index 8f35ba393..a0d069915 100644 --- a/src/lib/explorer/extensions/commands/importkeyfile.cpp +++ b/src/lib/explorer/extensions/commands/importkeyfile.cpp @@ -102,7 +102,7 @@ console_result importkeyfile::invoke(Json::Value& jv_output, const char *cmds2[]{"getnewaddress", auth_.name.c_str(), auth_.auth.c_str(), "--number", address_count.c_str()}; - if (dispatch_command(5, cmds2, jv_temp, node, 2) != console_result::okay) { + if (dispatch_command(5, cmds2, jv_temp, node, get_api_version()) != console_result::okay) { throw address_generate_exception{std::string("Failed to generate address.")}; } } @@ -138,14 +138,22 @@ console_result importkeyfile::invoke(Json::Value& jv_output, vec_cmds.push_back(s.c_str()); } - if (dispatch_command(vec_cmds.size(), vec_cmds.data(), jv_temp, node, 2) != console_result::okay) { + if (dispatch_command(vec_cmds.size(), vec_cmds.data(), jv_temp, node, get_api_version()) != console_result::okay) { throw address_generate_exception{std::string("Failed to generate address.")}; } - - } } + Json::Value jv_temp; + std::vector vec_cmds = {"listaddresses", auth_.name.c_str(), auth_.auth.c_str()}; + if (dispatch_command(vec_cmds.size(), vec_cmds.data(), jv_temp, node, get_api_version()) != console_result::okay) { + throw account_address_get_exception{std::string("Failed to list account addresses.")}; + } + + auto& root = jv_output; + config::json_helper::account_info acc(auth_.name, "", jv_temp); + root = config::json_helper(get_api_version()).prop_list(acc); + return console_result::okay; } else { @@ -170,13 +178,17 @@ console_result importkeyfile::invoke(Json::Value& jv_output, if (get_api_version() == 1) { root["address-count"] += acc.get_hd_index(); root["unissued-asset-count"] += all_info.get_account_asset().size(); - } else { + } + else if (get_api_version() <= 2) { root["address-count"] = acc.get_hd_index(); root["unissued-asset-count"] = static_cast(all_info.get_account_asset().size()); } + else { + root["address_count"] = acc.get_hd_index(); + root["unissued_asset_count"] = static_cast(all_info.get_account_asset().size()); + } return console_result::okay; - } } diff --git a/src/lib/explorer/extensions/commands/issue.cpp b/src/lib/explorer/extensions/commands/issue.cpp index 776ebf2cb..336dad86d 100644 --- a/src/lib/explorer/extensions/commands/issue.cpp +++ b/src/lib/explorer/extensions/commands/issue.cpp @@ -35,7 +35,7 @@ namespace commands { console_result issue::invoke (Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); @@ -45,15 +45,32 @@ console_result issue::invoke (Json::Value& jv_output, // check asset symbol check_asset_symbol(argument_.symbol); - if (argument_.fee < 1000000000) - throw asset_issue_poundage_exception{"issue asset fee less than 1000000000!"}; + // check fee + if (argument_.fee < bc::min_fee_to_issue_asset) { + throw asset_issue_poundage_exception{ + "issue asset fee less than " + + std::to_string(bc::min_fee_to_issue_asset) + " that's " + + std::to_string(bc::min_fee_to_issue_asset / 100000000) + " ETPs"}; + } + + if (argument_.percentage < bc::min_fee_percentage_to_miner || argument_.percentage > 100) { + throw asset_issue_poundage_exception{ + "issue asset minimum percentage of fee to miner less than " + + std::to_string(bc::min_fee_percentage_to_miner) + + " or greater than 100."}; + } + // fail if asset is already in blockchain - if (blockchain.is_asset_exist(argument_.symbol, false)) - throw asset_symbol_existed_exception{"asset symbol is already exist in blockchain"}; + if (blockchain.is_asset_exist(argument_.symbol, false)) { + throw asset_symbol_existed_exception{ + "asset " + argument_.symbol + " already exists in blockchain"}; + } + // local database asset check auto sh_asset = blockchain.get_account_unissued_asset(auth_.name, argument_.symbol); - if (!sh_asset) - throw asset_symbol_notfound_exception{argument_.symbol + " not found"}; + if (!sh_asset) { + throw asset_symbol_notfound_exception{"asset " + argument_.symbol + " not found"}; + } auto to_did = sh_asset->get_issuer(); auto to_address = get_address_from_did(to_did, blockchain); @@ -113,23 +130,28 @@ console_result issue::invoke (Json::Value& jv_output, auto certs = sh_asset->get_asset_cert_mask(); if (!certs.empty()) { for (auto each_cert_type : certs) { - receiver.push_back({to_address, argument_.symbol, 0, 0, - each_cert_type, utxo_attach_type::asset_cert_autoissue, attachment("", to_did)}); + receiver.push_back( + { to_address, argument_.symbol, 0, 0, + each_cert_type, utxo_attach_type::asset_cert_autoissue, attachment("", to_did) + }); } } // domain cert or naming cert if (asset_cert::is_valid_domain(domain)) { - receiver.push_back({to_address, cert_symbol, 0, 0, cert_type, + receiver.push_back( + { to_address, cert_symbol, 0, 0, cert_type, (is_domain_cert_exist ? utxo_attach_type::asset_cert : utxo_attach_type::asset_cert_autoissue), - attachment("", to_did)}); + attachment("", to_did) + }); } - auto issue_helper = issuing_asset(*this, blockchain, - std::move(auth_.name), std::move(auth_.auth), - "", std::move(argument_.symbol), - std::move(option_.attenuation_model_param), - std::move(receiver), argument_.fee); + auto issue_helper = issuing_asset( + *this, blockchain, + std::move(auth_.name), std::move(auth_.auth), + "", std::move(argument_.symbol), + std::move(option_.attenuation_model_param), + std::move(receiver), argument_.fee, argument_.percentage); issue_helper.exec(); diff --git a/src/lib/explorer/extensions/commands/listaddresses.cpp b/src/lib/explorer/extensions/commands/listaddresses.cpp index 44a92793a..29ed068d1 100644 --- a/src/lib/explorer/extensions/commands/listaddresses.cpp +++ b/src/lib/explorer/extensions/commands/listaddresses.cpp @@ -51,9 +51,15 @@ console_result listaddresses::invoke(Json::Value& jv_output, if (get_api_version() == 1 && addresses.isNull()) { // compatible for v1 aroot["addresses"] = ""; } - else { + else if (get_api_version() <= 2) { aroot["addresses"] = addresses; } + else { + if(addresses.isNull()) + addresses.resize(0); + + aroot = addresses; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/listassets.cpp b/src/lib/explorer/extensions/commands/listassets.cpp index 99e443fc1..a8ad4dd8b 100644 --- a/src/lib/explorer/extensions/commands/listassets.cpp +++ b/src/lib/explorer/extensions/commands/listassets.cpp @@ -40,6 +40,7 @@ console_result listassets::invoke(Json::Value& jv_output, std::string json_key; Json::Value json_value; + auto json_helper = config::json_helper(get_api_version()); if (option_.is_cert) { // only get asset certs @@ -123,9 +124,15 @@ console_result listassets::invoke(Json::Value& jv_output, if (get_api_version() == 1 && json_value.isNull()) { //compatible for v1 jv_output[json_key] = ""; } - else { + else if (get_api_version() <= 2) { jv_output[json_key] = json_value; } + else { + if(json_value.isNull()) + json_value.resize(0); + + jv_output = json_value; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/listbalances.cpp b/src/lib/explorer/extensions/commands/listbalances.cpp index 13efb79bb..03506edd7 100644 --- a/src/lib/explorer/extensions/commands/listbalances.cpp +++ b/src/lib/explorer/extensions/commands/listbalances.cpp @@ -40,61 +40,109 @@ console_result listbalances::invoke(Json::Value& jv_output, auto& blockchain = node.chain_impl(); blockchain.is_account_passwd_valid(auth_.name, auth_.auth); - auto& aroot = jv_output; Json::Value all_balances; - Json::Value address_balances; - + auto vaddr = blockchain.get_account_addresses(auth_.name); - if(!vaddr) throw address_list_nullptr_exception{"nullptr for address list"}; - - for (auto& i: *vaddr){ - Json::Value address_balance; - balances addr_balance{0, 0, 0, 0}; - auto waddr = wallet::payment_address(i.get_address()); - sync_fetchbalance(waddr, blockchain, addr_balance); - address_balance["address"] = i.get_address(); - - if (get_api_version() == 1) { - address_balance["confirmed"] += addr_balance.confirmed_balance; - address_balance["received"] += addr_balance.total_received; - address_balance["unspent"] += addr_balance.unspent_balance; - address_balance["available"] += (addr_balance.unspent_balance - addr_balance.frozen_balance); - address_balance["frozen"] += addr_balance.frozen_balance; - } else { - address_balance["confirmed"] = addr_balance.confirmed_balance; - address_balance["received"] = addr_balance.total_received; - address_balance["unspent"] = addr_balance.unspent_balance; - address_balance["available"] = (addr_balance.unspent_balance - addr_balance.frozen_balance); - address_balance["frozen"] = addr_balance.frozen_balance; + if(!vaddr) { + throw address_list_nullptr_exception{"nullptr for address list"}; + } + + if (!option_.greater && option_.non_zero) { + option_.greater = 1; + } + + if (option_.deposited) { + auto deposited_balances = std::make_shared(); + + for (auto& i: *vaddr){ + auto waddr = wallet::payment_address(i.get_address()); + sync_fetch_deposited_balance(waddr, blockchain, deposited_balances); } - Json::Value target_balance; + for (auto& balance : *deposited_balances) { + // non-zero lesser + if (option_.lesser) { + if (balance.balance > option_.lesser || balance.balance < option_.greater) { + continue; + } + } + else { + if (balance.balance < option_.greater) { + continue; + } + } + + Json::Value json_balance; + json_balance["address"] = balance.address; + json_balance["deposited_balance"] = balance.balance; + json_balance["deposited_height"] = balance.deposited_height; + json_balance["expiration_height"] = balance.expiration_height; - if (!option_.greater && option_.non_zero) { - option_.greater = 1; + all_balances.append(json_balance); } - // non-zero lesser - if (option_.lesser){ - if (addr_balance.unspent_balance <= option_.lesser && - addr_balance.unspent_balance >= option_.greater){ + } + else { + for (auto& i: *vaddr){ + balances addr_balance{0, 0, 0, 0}; + auto waddr = wallet::payment_address(i.get_address()); + sync_fetchbalance(waddr, blockchain, addr_balance); + + // non-zero lesser + if (option_.lesser) { + if (addr_balance.unspent_balance > option_.lesser || addr_balance.unspent_balance < option_.greater) { + continue; + } + } + else { + if (addr_balance.unspent_balance < option_.greater) { + continue; + } + } + + Json::Value address_balance; + address_balance["address"] = i.get_address(); + + if (get_api_version() == 1) { + address_balance["confirmed"] += addr_balance.confirmed_balance; + address_balance["received"] += addr_balance.total_received; + address_balance["unspent"] += addr_balance.unspent_balance; + address_balance["available"] += (addr_balance.unspent_balance - addr_balance.frozen_balance); + address_balance["frozen"] += addr_balance.frozen_balance; + } + else { + address_balance["confirmed"] = addr_balance.confirmed_balance; + address_balance["received"] = addr_balance.total_received; + address_balance["unspent"] = addr_balance.unspent_balance; + address_balance["available"] = (addr_balance.unspent_balance - addr_balance.frozen_balance); + address_balance["frozen"] = addr_balance.frozen_balance; + } + + if (get_api_version() <= 2) { + Json::Value target_balance; target_balance["balance"] = address_balance; all_balances.append(target_balance); } - } else { - if (addr_balance.unspent_balance >= option_.greater){ - target_balance["balance"] = address_balance; - all_balances.append(target_balance); + else { + all_balances.append(address_balance); } } } + auto& aroot = jv_output; if (get_api_version() == 1 && all_balances.isNull()) { //compatible for v1 aroot["balances"] = ""; - } else { + } + else if (get_api_version() <= 2) { aroot["balances"] = all_balances; } - return console_result::okay; + else { + if(all_balances.isNull()) + all_balances.resize(0); + + aroot = all_balances; + } + return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/listdids.cpp b/src/lib/explorer/extensions/commands/listdids.cpp index 31a2e33ca..90fcef3c6 100644 --- a/src/lib/explorer/extensions/commands/listdids.cpp +++ b/src/lib/explorer/extensions/commands/listdids.cpp @@ -64,9 +64,15 @@ console_result listdids::invoke(Json::Value& jv_output, if (get_api_version() == 1 && dids.isNull()) { //compatible for v1 jv_output["dids"] = ""; } - else { + else if (get_api_version() <= 2) { jv_output["dids"] = dids; } + else { + if(dids.isNull()) + dids.resize(0); + + jv_output = dids; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/listmits.cpp b/src/lib/explorer/extensions/commands/listmits.cpp index a54c589f8..cbca4e3b3 100644 --- a/src/lib/explorer/extensions/commands/listmits.cpp +++ b/src/lib/explorer/extensions/commands/listmits.cpp @@ -73,7 +73,15 @@ console_result listmits::invoke(Json::Value& jv_output, } } - jv_output["mits"] = json_value; + if (get_api_version() <= 2) { + jv_output["mits"] = json_value; + } + else { + if(json_value.isNull()) + json_value.resize(0); + + jv_output = json_value; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/listmultisig.cpp b/src/lib/explorer/extensions/commands/listmultisig.cpp index 83b32bb91..480b1f9fd 100644 --- a/src/lib/explorer/extensions/commands/listmultisig.cpp +++ b/src/lib/explorer/extensions/commands/listmultisig.cpp @@ -39,7 +39,6 @@ console_result listmultisig::invoke(Json::Value& jv_output, auto acc = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); Json::Value nodes; - auto multisig_vec = acc->get_multisig_vec(); auto helper = config::json_helper(get_api_version()); for(auto& acc_multisig : multisig_vec) { @@ -50,9 +49,15 @@ console_result listmultisig::invoke(Json::Value& jv_output, if (get_api_version() == 1 && nodes.isNull()) { // compatible for v1 jv_output["multisig"] = ""; } - else { + else if (get_api_version() <= 2) { jv_output["multisig"] = nodes; } + else { + if(nodes.isNull()) + nodes.resize(0); + + jv_output = nodes; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/listtxs.cpp b/src/lib/explorer/extensions/commands/listtxs.cpp index 1bf3ca5b5..8d4765f74 100644 --- a/src/lib/explorer/extensions/commands/listtxs.cpp +++ b/src/lib/explorer/extensions/commands/listtxs.cpp @@ -198,7 +198,8 @@ console_result listtxs::invoke(Json::Value& jv_output, if (get_api_version() == 1 && input_addrs.isNull()) { // compatible for v1 tx_item["inputs"] = ""; - } else { + } + else { tx_item["inputs"] = input_addrs; } @@ -234,10 +235,15 @@ console_result listtxs::invoke(Json::Value& jv_output, if (get_api_version() == 1) { pt_output["locked_height_range"] += lock_height; pt_output["etp-value"] += op.value; - } else { + } + else if (get_api_version() == 2) { pt_output["locked_height_range"] = lock_height; pt_output["etp-value"] = op.value; } + else { + pt_output["locked_height_range"] = lock_height; + pt_output["etp_value"] = op.value; + } if (chain::operation::is_pay_key_hash_with_attenuation_model_pattern(op.script.operations)) { const auto& model_param = op.get_attenuation_model_param(); @@ -274,7 +280,8 @@ console_result listtxs::invoke(Json::Value& jv_output, if (get_api_version() == 1 && pt_outputs.isNull()) { // compatible for v1 tx_item["outputs"] = ""; - } else { + } + else { tx_item["outputs"] = pt_outputs; } @@ -304,9 +311,16 @@ console_result listtxs::invoke(Json::Value& jv_output, if (get_api_version() == 1 && balances.isNull()) { // compatible for v1 aroot["transactions"] = ""; - } else { + } + else if (get_api_version() <= 2) { aroot["transactions"] = balances; } + else { + if(balances.isNull()) + balances.resize(0); + + aroot = balances; + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/registerdid.cpp b/src/lib/explorer/extensions/commands/registerdid.cpp index 08d476077..86eb59385 100644 --- a/src/lib/explorer/extensions/commands/registerdid.cpp +++ b/src/lib/explorer/extensions/commands/registerdid.cpp @@ -33,7 +33,7 @@ namespace commands { console_result registerdid::invoke(Json::Value &jv_output, - libbitcoin::server::server_node &node) + libbitcoin::server::server_node &node) { auto &blockchain = node.chain_impl(); auto acc = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); @@ -41,13 +41,29 @@ console_result registerdid::invoke(Json::Value &jv_output, // check did symbol check_did_symbol(argument_.symbol, true); - if (blockchain.is_valid_address(argument_.symbol)) + if (blockchain.is_valid_address(argument_.symbol)) { throw address_invalid_exception{"symbol cannot be an address!"}; + } + + // check fee + if (argument_.fee < bc::min_fee_to_register_did) { + throw did_register_poundage_exception{ + "register did: fee less than " + + std::to_string(bc::min_fee_to_register_did) + " that's " + + std::to_string(bc::min_fee_to_register_did / 100000000) + " ETPs"}; + } - if (argument_.fee < 100000000) - throw did_register_poundage_exception{"register did fee must be at least 100000000 satoshi = 1 etp!"}; - if (!blockchain.is_valid_address(argument_.address)) + if (argument_.percentage < bc::min_fee_percentage_to_miner || argument_.percentage > 100) { + throw did_register_poundage_exception{ + "register did minimum percentage of fee to miner less than " + + std::to_string(bc::min_fee_percentage_to_miner) + + " or greater than 100."}; + } + + // check address + if (!blockchain.is_valid_address(argument_.address)) { throw address_invalid_exception{"invalid address parameter!"}; + } if (!blockchain.get_account_address(auth_.name, argument_.address)) { throw address_dismatch_account_exception{ @@ -78,24 +94,19 @@ console_result registerdid::invoke(Json::Value &jv_output, {argument_.address, argument_.symbol, 0, 0, utxo_attach_type::did_register, attachment()}}; auto register_helper = registering_did( - *this, blockchain, std::move(auth_.name), std::move(auth_.auth), - std::move(argument_.address), std::move(argument_.symbol), - std::move(receiver), argument_.fee, - std::move(acc_multisig)); + *this, blockchain, std::move(auth_.name), std::move(auth_.auth), + std::move(argument_.address), std::move(argument_.symbol), + std::move(receiver), argument_.fee, argument_.percentage, + std::move(acc_multisig)); register_helper.exec(); // output json + auto && tx = register_helper.get_transaction(); if (is_multisig_address) { - auto && tx = register_helper.get_transaction(); - std::ostringstream tx_buf; - tx_buf << config::transaction(tx); - jv_output = tx_buf.str(); - // TODO support restful API format - // jv_output["raw"] = tx_buf.str(); + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false, true); } else { - auto &&tx = register_helper.get_transaction(); jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); } diff --git a/src/lib/explorer/extensions/commands/sendrawtx.cpp b/src/lib/explorer/extensions/commands/sendrawtx.cpp index c6d26eff3..a7a0fb6f4 100644 --- a/src/lib/explorer/extensions/commands/sendrawtx.cpp +++ b/src/lib/explorer/extensions/commands/sendrawtx.cpp @@ -52,7 +52,12 @@ console_result sendrawtx::invoke(Json::Value& jv_output, if (blockchain.broadcast_transaction(tx_)) throw tx_broadcast_exception{"broadcast transaction failure"}; - jv_output["hash"] = encode_hash(tx_.hash()); + if (get_api_version() <= 2) { + jv_output["hash"] = encode_hash(tx_.hash()); + } + else { + jv_output = encode_hash(tx_.hash()); + } return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/signmultisigtx.cpp b/src/lib/explorer/extensions/commands/signmultisigtx.cpp index 7fefe6a08..e072aa4eb 100644 --- a/src/lib/explorer/extensions/commands/signmultisigtx.cpp +++ b/src/lib/explorer/extensions/commands/signmultisigtx.cpp @@ -212,18 +212,15 @@ console_result signmultisigtx::invoke( } // output json - std::ostringstream tx_buf; - tx_buf << config::transaction(tx_); if (get_api_version() <= 2) { - jv_output = tx_buf.str(); + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx_, false, true); } else { - // TODO support restful API format - jv_output["raw"] = tx_buf.str(); + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx_, true); } if (option_.broadcast_flag /* TODO && fullfilled */) { - log::trace("multisig") << "validate and broadcast multisig transaction: " << tx_buf.str(); + log::trace("multisig") << "validate and broadcast multisig transaction." << tx_.to_string(1); if (blockchain.validate_transaction(tx_)) { throw tx_validate_exception{"validate transaction failure"}; diff --git a/src/lib/explorer/extensions/commands/signrawtx.cpp b/src/lib/explorer/extensions/commands/signrawtx.cpp index 242634e9b..498a94717 100644 --- a/src/lib/explorer/extensions/commands/signrawtx.cpp +++ b/src/lib/explorer/extensions/commands/signrawtx.cpp @@ -100,21 +100,12 @@ console_result signrawtx::invoke(Json::Value& jv_output, } // get raw tx - if (blockchain.validate_transaction(tx_)) - throw tx_validate_exception{std::string("validate transaction failure")}; - - auto& aroot = jv_output; - aroot["hash"] = encode_hash(tx_.hash()); - std::ostringstream tx_buf; - tx_buf << config::transaction(tx_); - if (get_api_version() <= 2) { - jv_output["hex"] = tx_buf.str(); - } - else { - // TODO support restful API format - jv_output["raw"] = tx_buf.str(); + if (blockchain.validate_transaction(tx_)) { + throw tx_validate_exception{"validate transaction failure"}; } + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx_, true); + return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/startmining.cpp b/src/lib/explorer/extensions/commands/startmining.cpp index 977a73eca..87ba5c2e6 100644 --- a/src/lib/explorer/extensions/commands/startmining.cpp +++ b/src/lib/explorer/extensions/commands/startmining.cpp @@ -54,12 +54,21 @@ console_result startmining::invoke(Json::Value& jv_output, // get new address const char* cmds2[]{"getnewaddress", auth_.name.c_str(), auth_.auth.c_str()}; - if (dispatch_command(3, cmds2, jv_temp, node, 2) != console_result::okay) { + if (dispatch_command(3, cmds2, jv_temp, node, get_api_version()) != console_result::okay) { throw address_generate_exception(jv_temp.asString()); } - str_addr = jv_temp["addresses"][0].asString(); - } else { + if (get_api_version() == 1) { + str_addr = jv_temp.asString(); + } + else if (get_api_version() == 2) { + str_addr = jv_temp["addresses"][0].asString(); + } + else { + str_addr = jv_temp[0].asString(); + } + } + else { blockchain.is_account_passwd_valid(auth_.name, auth_.auth); if (!blockchain.is_valid_address(str_addr)) { diff --git a/src/lib/explorer/extensions/commands/submitwork.cpp b/src/lib/explorer/extensions/commands/submitwork.cpp index 42ba6267e..337510c48 100644 --- a/src/lib/explorer/extensions/commands/submitwork.cpp +++ b/src/lib/explorer/extensions/commands/submitwork.cpp @@ -53,21 +53,20 @@ console_result submitwork::invoke(Json::Value& jv_output, auto& root = jv_output; if (ret) { - if (get_api_version() == 1) { root["result"] = "true"; // boost json parser output as string, for compatible. - } else { + } + else { root = true; } - - } else { - + } + else { if (get_api_version() == 1) { root["result"] = "false"; // boost json parser output as string, for compatible. - } else { + } + else { root = false; } - } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/transfercert.cpp b/src/lib/explorer/extensions/commands/transfercert.cpp index 8fae83b32..9c93e3afe 100644 --- a/src/lib/explorer/extensions/commands/transfercert.cpp +++ b/src/lib/explorer/extensions/commands/transfercert.cpp @@ -107,17 +107,13 @@ console_result transfercert::invoke (Json::Value& jv_output, helper.exec(); - // json output + // json output + auto && tx = helper.get_transaction(); if (is_multisig_address) { - // out rawtx for multisig cert - auto && tx = helper.get_transaction(); - std::ostringstream tx_buf; - tx_buf << config::transaction(tx); - jv_output = tx_buf.str(); + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false, true); } else { - auto tx = helper.get_transaction(); - jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); + jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/transfermit.cpp b/src/lib/explorer/extensions/commands/transfermit.cpp index d645b1a5e..0ba77c180 100644 --- a/src/lib/explorer/extensions/commands/transfermit.cpp +++ b/src/lib/explorer/extensions/commands/transfermit.cpp @@ -85,16 +85,12 @@ console_result transfermit::invoke (Json::Value& jv_output, helper.exec(); // json output + auto tx = helper.get_transaction(); if (is_multisig_address) { - // out rawtx for multisig asset - auto && tx = helper.get_transaction(); - std::ostringstream tx_buf; - tx_buf << config::transaction(tx); - jv_output = tx_buf.str(); + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false, true); } else { - auto tx = helper.get_transaction(); - jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); + jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/validateaddress.cpp b/src/lib/explorer/extensions/commands/validateaddress.cpp index 4bec72b74..194a30d08 100644 --- a/src/lib/explorer/extensions/commands/validateaddress.cpp +++ b/src/lib/explorer/extensions/commands/validateaddress.cpp @@ -58,10 +58,18 @@ console_result validateaddress::invoke(Json::Value& jv_output, } auto& jv = jv_output; - jv["address-type"] = version_info; - jv["test-net"] = use_testnet_rules; - jv["is-valid"] = is_valid; - jv["message"] = message; + if (get_api_version() <= 2) { + jv["address-type"] = version_info; + jv["test-net"] = use_testnet_rules; + jv["is-valid"] = is_valid; + jv["message"] = message; + } + else { + jv["address_type"] = version_info; + jv["testnet"] = use_testnet_rules; + jv["is_valid"] = is_valid; + jv["message"] = message; + } return console_result::okay; } diff --git a/src/lib/explorer/json_helper.cpp b/src/lib/explorer/json_helper.cpp index 79b601774..5767f6cf2 100644 --- a/src/lib/explorer/json_helper.cpp +++ b/src/lib/explorer/json_helper.cpp @@ -78,8 +78,14 @@ Json::Value json_helper::prop_list(const header& header) tree["version"] += block_header.version; tree["number"] += block_header.number; tree["transaction_count"] += block_header.transaction_count; - } else { - tree["time_stamp"] = block_header.timestamp; + } + else { + if (version_ <= 2) { + tree["time_stamp"] = block_header.timestamp; + } + else { + tree["timestamp"] = block_header.timestamp; + } tree["version"] = block_header.version; tree["number"] = block_header.number; tree["transaction_count"] = block_header.transaction_count; @@ -87,17 +93,29 @@ Json::Value json_helper::prop_list(const header& header) return tree; } + Json::Value json_helper::prop_tree(const header& header) { - Json::Value tree; - tree["result"] = prop_list(header); - return tree; + if (version_ <= 2) { + Json::Value tree; + tree["result"] = prop_list(header); + return tree; + } + else { + return prop_list(header); + } } + Json::Value json_helper::prop_tree(const std::vector
& headers, bool json) { - Json::Value tree; - tree["headers"] = prop_tree_list("header", headers, json); - return tree; + if (version_ <= 2) { + Json::Value tree; + tree["headers"] = prop_tree_list("header", headers, json); + return tree; + } + else { + return prop_tree_list("header", headers, json); + } } // transfers @@ -199,7 +217,12 @@ Json::Value json_helper::prop_tree(const chain::history::list& rows, const payment_address& balance_address) { Json::Value tree; - tree["balance"] = prop_list(rows, balance_address); + if (version_ <= 2) { + tree["balance"] = prop_list(rows, balance_address); + } + else { + tree = prop_list(rows, balance_address); + } return tree; } @@ -488,9 +511,14 @@ Json::Value json_helper::prop_list(const bc::chain::asset_mit_info& mit_info, bo Json::Value tree; tree["height"] = mit_info.output_height; - tree["time_stamp"] = mit_info.timestamp; - tree["to_did"] = mit_info.to_did; + if (version_ <= 2) { + tree["time_stamp"] = mit_info.timestamp; + } + else { + tree["timestamp"] = mit_info.timestamp; + } + tree["to_did"] = mit_info.to_did; tree["symbol"] = mit_info.mit.get_symbol(); tree["address"] = mit_info.mit.get_address(); tree["status"] = mit_info.mit.get_status_name(); @@ -523,14 +551,25 @@ Json::Value json_helper::prop_list(const bc::chain::account_multisig& acc_multis if (version_ == 1 && pubkeys.isNull()) { //compatible for v1 tree["public-keys"] = ""; } - else { + else if (version_ <= 2) { tree["public-keys"] = pubkeys; } + else { + tree["public_keys"] = pubkeys; + } tree["address"] = acc_multisig.get_address(); - tree["self-publickey"] = acc_multisig.get_pub_key(); - tree["multisig-script"] = acc_multisig.get_multisig_script(); tree["description"] = acc_multisig.get_description(); + + if (version_ <= 2) { + tree["self-publickey"] = acc_multisig.get_pub_key(); + tree["multisig-script"] = acc_multisig.get_multisig_script(); + } + else { + tree["self_publickey"] = acc_multisig.get_pub_key(); + tree["multisig_script"] = acc_multisig.get_multisig_script(); + } + return tree; } @@ -655,6 +694,31 @@ Json::Value json_helper::prop_tree(const chain::points_info& points_info, bool j // transactions +Json::Value json_helper::prop_list_of_rawtx(const transaction& transaction, bool with_hash, bool ignore_compatibility) +{ + const tx_type& tx = transaction; + std::ostringstream sout; + sout << base16(tx.to_data()); + + Json::Value tree; + if (!ignore_compatibility && version_ <= 2) { + if (with_hash) { + tree["hash"] += hash256(tx.hash()); + } + tree["hex"] = sout.str(); + } + else { + if (with_hash) { + tree["hash"] += hash256(tx.hash()); + tree["rawtx"] = sout.str(); + } + else { + tree = sout.str(); + } + } + return tree; +} + Json::Value json_helper::prop_list(const transaction& transaction, bool json) { const tx_type& tx = transaction; @@ -667,13 +731,20 @@ Json::Value json_helper::prop_list(const transaction& transaction, bool json) tree["outputs"] = prop_tree(tx.outputs, json); // only used for output to add new field "index" tree["version"] += tx.version; return tree; - }else { + } + else { std::ostringstream sout; sout << base16(tx.to_data()); - tree["raw"] = sout.str(); + if (version_ <= 2) { + tree["raw"] = sout.str(); + } + else { + tree = sout.str(); + } } return tree; } + Json::Value json_helper::prop_list(const transaction& transaction, uint64_t tx_height, bool json) { const tx_type& tx = transaction; @@ -694,16 +765,26 @@ Json::Value json_helper::prop_list(const transaction& transaction, uint64_t tx_h Json::Value json_helper::prop_tree(const transaction& transaction, bool json) { - Json::Value tree; - tree["transaction"] = prop_list(transaction, json); - return tree; + if (version_ <= 2) { + Json::Value tree; + tree["transaction"] = prop_list(transaction, json); + return tree; + } + else { + return prop_list(transaction, json); + } } + Json::Value json_helper::prop_tree(const std::vector& transactions, bool json) { - Json::Value tree; - tree["transactions"] = - prop_tree_list_of_lists("transaction", transactions, json); - return tree; + if (version_ <= 2) { + Json::Value tree; + tree["transactions"] = prop_tree_list_of_lists("transaction", transactions, json); + return tree; + } + else { + return prop_tree_list_of_lists("transaction", transactions, json); + } } // wrapper @@ -864,20 +945,42 @@ Json::Value json_helper::prop_tree(const block& block, bool json, bool tx_json) Json::Value tree; if (json) { - tree["header"] = prop_tree(block.header); std::vector txs; txs.resize(block.transactions.size()); std::copy(block.transactions.begin(), block.transactions.end(), txs.begin()); - tree["txs"] = prop_tree(txs, tx_json); - } else { + if (version_ <= 2) { + tree["header"] = prop_tree(block.header); + tree["txs"] = prop_tree(txs, tx_json); + } + else { + tree = prop_tree(block.header); + tree["transactions"] = prop_tree(txs, tx_json); + } + } + else { std::ostringstream sout; sout << encode_base16(block.to_data()); - tree["raw"] = sout.str(); + if (version_ <= 2) { + tree["raw"] = sout.str(); + } + else { + tree = sout.str(); + } } return tree; } +Json::Value json_helper::prop_list(const account_info& acc) +{ + Json::Value tree; + tree["name"] = std::get<0>(acc); + if (std::get<1>(acc).size() > 0) tree["mnemonic"] = std::get<1>(acc); + tree["addresses"] = std::get<2>(acc); + + return tree; +} + } // namespace config } // namespace explorer } // namespace libbitcoin diff --git a/src/lib/network/hosts.cpp b/src/lib/network/hosts.cpp index 72c9efe3d..697663f09 100644 --- a/src/lib/network/hosts.cpp +++ b/src/lib/network/hosts.cpp @@ -297,7 +297,14 @@ code hosts::clear() mutex_.unlock_upgrade_and_lock(); - backup_ = std::move( buffer_ ); + + // if the buffer is already moved to backup, call this function again will lead to the loss of backup. + // backup_ = std::move( buffer_ ); + for (auto &host : buffer_) { + backup_.insert(host); + } + buffer_.clear(); + mutex_.unlock(); /////////////////////////////////////////////////////////////////////////// @@ -322,6 +329,17 @@ code hosts::after_reseeding() log::warning(LOG_NETWORK) << "Reseeding finished, but got address list: " << buffer_.size() << ", less than seed count: " << seed_count << ", roll back the hosts cache."; buffer_ = std::move(backup_); + } else { + // filter inactive hosts + for (auto &host : inactive_) { + auto iter = buffer_.find(host); + if (iter != buffer_.end()) { + buffer_.erase(iter); + } + } + + // clear the backup + backup_.clear(); } log::debug(LOG_NETWORK) << "Reseeding finished, and got addresses of count: " << buffer_.size(); diff --git a/src/lib/network/message_subscriber.cpp b/src/lib/network/message_subscriber.cpp index acdf9958b..f0f98d06e 100644 --- a/src/lib/network/message_subscriber.cpp +++ b/src/lib/network/message_subscriber.cpp @@ -53,7 +53,6 @@ using namespace message; message_subscriber::message_subscriber(threadpool& pool) : INITIALIZE_SUBSCRIBER(pool, address), - INITIALIZE_SUBSCRIBER(pool, alert), INITIALIZE_SUBSCRIBER(pool, block_message), INITIALIZE_SUBSCRIBER(pool, block_transactions), INITIALIZE_SUBSCRIBER(pool, compact_block), @@ -85,7 +84,6 @@ message_subscriber::message_subscriber(threadpool& pool) void message_subscriber::broadcast(const code& ec) { RELAY_CODE(ec, address); - RELAY_CODE(ec, alert); RELAY_CODE(ec, block_message); RELAY_CODE(ec, block_transactions); RELAY_CODE(ec, compact_block); @@ -119,7 +117,6 @@ code message_subscriber::load(message_type type, uint32_t version, switch (type) { CASE_RELAY_MESSAGE(stream, version, address); - CASE_RELAY_MESSAGE(stream, version, alert); CASE_HANDLE_MESSAGE(stream, version, block_message); CASE_RELAY_MESSAGE(stream, version, block_transactions); CASE_RELAY_MESSAGE(stream, version, compact_block); @@ -154,7 +151,6 @@ code message_subscriber::load(message_type type, uint32_t version, void message_subscriber::start() { START_SUBSCRIBER(address); - START_SUBSCRIBER(alert); START_SUBSCRIBER(block_message); START_SUBSCRIBER(block_transactions); START_SUBSCRIBER(compact_block); @@ -185,7 +181,6 @@ void message_subscriber::start() void message_subscriber::stop() { STOP_SUBSCRIBER(address); - STOP_SUBSCRIBER(alert); STOP_SUBSCRIBER(block_message); STOP_SUBSCRIBER(block_transactions); STOP_SUBSCRIBER(compact_block); diff --git a/src/lib/network/sessions/session_outbound.cpp b/src/lib/network/sessions/session_outbound.cpp index e1d1ce171..603e105c1 100644 --- a/src/lib/network/sessions/session_outbound.cpp +++ b/src/lib/network/sessions/session_outbound.cpp @@ -136,13 +136,14 @@ void session_outbound::delay_reseeding() auto pThis = shared_from_this(); auto action = [this, pThis](){ const int counter = outbound_counter; - in_reseeding = false; if (counter > 1) { log::debug(LOG_NETWORK) << "outbound channel counter recovered to [" << counter << "], re-seeding is canceled!"; + in_reseeding = false; return; } log::debug(LOG_NETWORK) << "start re-seeding!"; network__.restart_seeding(); + in_reseeding = false; }; pool_.service().post(action); }); diff --git a/src/mvs-cli/main.cpp b/src/mvs-cli/main.cpp index 5e32e63d7..273a309b8 100644 --- a/src/mvs-cli/main.cpp +++ b/src/mvs-cli/main.cpp @@ -66,7 +66,7 @@ int bc::main(int argc, char* argv[]) bc::set_utf8_stdout(); auto work_path = bc::default_data_path(); auto&& config_file = work_path / "mvs.conf"; - std::string url{"127.0.0.1:8820/rpc/v2"}; + std::string url{"127.0.0.1:8820/rpc/v3"}; if (boost::filesystem::exists(config_file)) { const auto& path = config_file.string(); @@ -91,7 +91,7 @@ int bc::main(int argc, char* argv[]) if (tmp.find("0.0.0.0") == 0) { tmp.replace(0, 7, "127.0.0.1"); } - url = tmp + "/rpc/v2"; + url = tmp + "/rpc/v3"; } } } diff --git a/src/mvsd/mgbubble/HttpServ.cpp b/src/mvsd/mgbubble/HttpServ.cpp index 70f1590ac..3d909226c 100644 --- a/src/mvsd/mgbubble/HttpServ.cpp +++ b/src/mvsd/mgbubble/HttpServ.cpp @@ -25,7 +25,7 @@ #include #include -namespace mgbubble{ +namespace mgbubble { thread_local OStream HttpServ::out_; thread_local Tokeniser<'/'> HttpServ::uri_; @@ -37,19 +37,19 @@ void HttpServ::reset(HttpMessage& data) noexcept const auto method = data.method(); if (method == "GET") { - state_ |= MethodGet; + state_ |= MethodGet; } else if (method == "POST") { - state_ |= MethodPost; + state_ |= MethodPost; } else if (method == "PUT") { - state_ |= MethodPut; + state_ |= MethodPut; } else if (method == "DELETE") { - state_ |= MethodDelete; + state_ |= MethodDelete; } auto uri = data.uri(); // Remove leading slash. if (uri.front() == '/') { - uri.remove_prefix(1); + uri.remove_prefix(1); } uri_.reset(uri); } @@ -62,7 +62,7 @@ void HttpServ::rpc_request(mg_connection& nc, HttpMessage data, uint8_t rpc_vers out_.reset(200, "OK"); const vector api20_ver_list = {2, 3}; - auto checkAPIVer = [](const vector &api_ver_list, const uint8_t &rpc_version){ + auto checkAPIVer = [](const vector &api_ver_list, const uint8_t &rpc_version) { return find(api_ver_list.begin(), api_ver_list.end(), rpc_version) != api_ver_list.end(); }; try { @@ -71,7 +71,7 @@ void HttpServ::rpc_request(mg_connection& nc, HttpMessage data, uint8_t rpc_vers Json::Value jv_output; auto retcode = explorer::dispatch_command(data.argc(), const_cast(data.argv()), - jv_output, node_, rpc_version); + jv_output, node_, rpc_version); if (retcode == console_result::failure) { // only orignal command if (rpc_version == 1 && !jv_output.isObject() && !jv_output.isArray()) { @@ -133,7 +133,7 @@ void HttpServ::ws_request(mg_connection& nc, WebsocketMessage ws) { Json::Value jv_output; - try{ + try { ws.data_to_arg(); console_result retcode = explorer::dispatch_command(ws.argc(), const_cast(ws.argv()), jv_output, node_); @@ -171,7 +171,7 @@ void HttpServ::spawn_to_mongoose(const std::function&& handler) void HttpServ::run() { log::info(LOG_HTTP) << "Http Service listen on " << node_.server_settings().mongoose_listen; - node_.subscribe_stop([this](const libbitcoin::code& ec) { stop(); }); + node_.subscribe_stop([this](const libbitcoin::code & ec) { stop(); }); base::run(); @@ -188,8 +188,9 @@ void HttpServ::on_http_req_handler(struct mg_connection& nc, http_message& msg) } else if ((mg_ncasecmp(msg.uri.p, "/rpc", 4) == 0) || (mg_ncasecmp(msg.uri.p, "/rpc/", 5) == 0)) { rpc_request(nc, HttpMessage(&msg), 1); //v1 rpc - } else { - std::shared_ptr con(&nc, [](struct mg_connection* ptr) { (void)(ptr); }); + } + else { + std::shared_ptr con(&nc, [](struct mg_connection * ptr) { (void)(ptr); }); serve_http_static(nc, msg); } } @@ -207,7 +208,7 @@ void HttpServ::on_notify_handler(struct mg_connection& nc, struct mg_event& ev) void HttpServ::on_ws_handshake_done_handler(struct mg_connection& nc) { - std::shared_ptr con(&nc, [](struct mg_connection* ptr) { (void)(ptr); }); + std::shared_ptr con(&nc, [](struct mg_connection * ptr) { (void)(ptr); }); send_frame(nc, "connected", 9); } diff --git a/src/mvsd/mgbubble/Mongoose.cpp b/src/mvsd/mgbubble/Mongoose.cpp index 80ac14398..78eaa63cb 100644 --- a/src/mvsd/mgbubble/Mongoose.cpp +++ b/src/mvsd/mgbubble/Mongoose.cpp @@ -35,7 +35,7 @@ void HttpMessage::data_to_arg(uint8_t rpc_version) { } argc_ = i; }; - + Json::Reader reader; Json::Value root; const char* begin = body().data(); @@ -62,10 +62,10 @@ void HttpMessage::data_to_arg(uint8_t rpc_version) { vargv_.emplace_back(param.asString()); } } else { - /* ***************** /rpc/v2 ********************** + /* ***************** /rpc/v2 or /rpc/v3 ********************** * application/json * { - * "method":"xxx", + * "method":"xxx", * "params":[ * { * k1:v1, ==> Command Option @@ -77,7 +77,12 @@ void HttpMessage::data_to_arg(uint8_t rpc_version) { * } * ******************************************/ - if (root["jsonrpc"].asString() != "2.0") { + const vector api20_ver_list = {"2.0", "3.0"}; + auto checkAPIVer = [](const vector &api_ver_list, const std::string &rpc_version){ + return find(api_ver_list.begin(), api_ver_list.end(), rpc_version) != api_ver_list.end(); + }; + + if (!checkAPIVer(api20_ver_list, root["jsonrpc"].asString())) { throw libbitcoin::explorer::jsonrpc_invalid_request(); } @@ -158,8 +163,8 @@ void WebsocketMessage::data_to_arg(uint8_t api_version) { void ToCommandArg::add_arg(std::string&& outside) { - vargv_.push_back(outside); - argc_++; + vargv_.push_back(outside); + argc_++; } } // mgbubble diff --git a/src/mvsd/mgbubble/utility/Stream_buf.cpp b/src/mvsd/mgbubble/utility/Stream_buf.cpp index 29df1c984..bea3e53aa 100644 --- a/src/mvsd/mgbubble/utility/Stream_buf.cpp +++ b/src/mvsd/mgbubble/utility/Stream_buf.cpp @@ -23,7 +23,7 @@ using namespace std; namespace mgbubble { -StreamBuf::StreamBuf(mbuf& buf) throw(bad_alloc) : buf_(buf) +StreamBuf::StreamBuf(mbuf& buf) : buf_(buf) { if (!buf_.buf) { // Pre-allocate buffer. diff --git a/test/test-rpc-v3/HTMLTestRunner.py b/test/test-rpc-v3/HTMLTestRunner.py new file mode 100644 index 000000000..0439bf488 --- /dev/null +++ b/test/test-rpc-v3/HTMLTestRunner.py @@ -0,0 +1,824 @@ +""" +A TestRunner for use with the Python unit testing framework. It +generates a HTML report to show the result at a glance. + +The simplest way to use this is to invoke its main method. E.g. + + import unittest + import HTMLTestRunner + + ... define your tests ... + + if __name__ == '__main__': + HTMLTestRunner.main() + + +For more customization options, instantiates a HTMLTestRunner object. +HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. + + # output to a file + fp = file('my_report.html', 'wb') + runner = HTMLTestRunner.HTMLTestRunner( + stream=fp, + title='My unit test', + description='This demonstrates the report output by HTMLTestRunner.' + ) + + # Use an external stylesheet. + # See the Template_mixin class for more customizable options + runner.STYLESHEET_TMPL = '' + + # run the test + runner.run(my_test_suite) + + +------------------------------------------------------------------------ +Copyright (c) 2004-2007, Wai Yip Tung +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name Wai Yip Tung nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +# URL: http://tungwaiyip.info/software/HTMLTestRunner.html + +__author__ = "Wai Yip Tung" +__version__ = "0.8.2" + + +""" +Change History + +Version 0.8.2 +* Show output inline instead of popup window (Viorel Lupu). + +Version in 0.8.1 +* Validated XHTML (Wolfgang Borgert). +* Added description of test classes and test cases. + +Version in 0.8.0 +* Define Template_mixin class for customization. +* Workaround a IE 6 bug that it does not treat + +%(heading)s +%(report)s +%(ending)s + + + +""" + # variables: (title, generator, stylesheet, heading, report, ending) + + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLESHEET_TMPL = """ + +""" + + + + # ------------------------------------------------------------------------ + # Heading + # + + HEADING_TMPL = """
+

%(title)s

+%(parameters)s +

%(description)s

+
+ +""" # variables: (title, parameters, description) + + HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

+""" # variables: (name, value) + + + + # ------------------------------------------------------------------------ + # Report + # + + REPORT_TMPL = """ +

Show +Summary +Failed +All +

+ ++++++++ + + + + + + + + +%(test_list)s + + + + + + + + +
Test Group/Test caseCountPassFailErrorView
Total%(count)s%(Pass)s%(fail)s%(error)s 
+""" # variables: (test_list, count, Pass, fail, error) + + REPORT_CLASS_TMPL = r""" + + %(desc)s + %(count)s + %(Pass)s + %(fail)s + %(error)s + Detail + +""" # variables: (style, desc, count, Pass, fail, error, cid) + + + REPORT_TEST_WITH_OUTPUT_TMPL = r""" + +
%(desc)s
+ + + + + %(status)s + + + + + + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_NO_OUTPUT_TMPL = r""" + +
%(desc)s
+ %(status)s + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_OUTPUT_TMPL = r""" +%(id)s: %(output)s +""" # variables: (id, output) + + + + # ------------------------------------------------------------------------ + # ENDING + # + + ENDING_TMPL = """
 
""" + +# -------------------- The end of the Template class ------------------- + + +TestResult = unittest.TestResult + +class _TestResult(TestResult): + # note: _TestResult is a pure representation of results. + # It lacks the output and reporting ability compares to unittest._TextTestResult. + + def __init__(self, verbosity=1): + TestResult.__init__(self) + self.stdout0 = None + self.stderr0 = None + self.success_count = 0 + self.failure_count = 0 + self.error_count = 0 + self.verbosity = verbosity + + # result is a list of result in 4 tuple + # ( + # result code (0: success; 1: fail; 2: error), + # TestCase object, + # Test output (byte string), + # stack trace, + # ) + self.result = [] + + + def startTest(self, test): + TestResult.startTest(self, test) + # just one buffer for both stdout and stderr + self.outputBuffer = StringIO.StringIO() + stdout_redirector.fp = self.outputBuffer + stderr_redirector.fp = self.outputBuffer + self.stdout0 = sys.stdout + self.stderr0 = sys.stderr + sys.stdout = stdout_redirector + sys.stderr = stderr_redirector + + + def complete_output(self): + """ + Disconnect output redirection and return buffer. + Safe to call multiple times. + """ + if self.stdout0: + sys.stdout = self.stdout0 + sys.stderr = self.stderr0 + self.stdout0 = None + self.stderr0 = None + return self.outputBuffer.getvalue() + + + def stopTest(self, test): + # Usually one of addSuccess, addError or addFailure would have been called. + # But there are some path in unittest that would bypass this. + # We must disconnect stdout in stopTest(), which is guaranteed to be called. + self.complete_output() + + + def addSuccess(self, test): + self.success_count += 1 + TestResult.addSuccess(self, test) + output = self.complete_output() + self.result.append((0, test, output, '')) + if self.verbosity > 1: + sys.stderr.write('ok ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('.') + + def addError(self, test, err): + self.error_count += 1 + TestResult.addError(self, test, err) + _, _exc_str = self.errors[-1] + output = self.complete_output() + self.result.append((2, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('E ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('E') + + def addFailure(self, test, err): + self.failure_count += 1 + TestResult.addFailure(self, test, err) + _, _exc_str = self.failures[-1] + output = self.complete_output() + self.result.append((1, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('F ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('F') + + +class HTMLTestRunner(Template_mixin): + """ + """ + def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): + self.stream = stream + self.verbosity = verbosity + if title is None: + self.title = self.DEFAULT_TITLE + else: + self.title = title + if description is None: + self.description = self.DEFAULT_DESCRIPTION + else: + self.description = description + + self.startTime = datetime.datetime.now() + + + def run(self, test): + "Run the given test case or test suite." + result = _TestResult(self.verbosity) + test(result) + self.stopTime = datetime.datetime.now() + self.generateReport(test, result) + print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) + return result + + + def sortResult(self, result_list): + # unittest does not seems to run in any particular order. + # Here at least we want to group them together by class. + rmap = {} + classes = [] + for n,t,o,e in result_list: + cls = t.__class__ + if not rmap.has_key(cls): + rmap[cls] = [] + classes.append(cls) + rmap[cls].append((n,t,o,e)) + r = [(cls, rmap[cls]) for cls in classes] + return r + + + def getReportAttributes(self, result): + """ + Return report attributes as a list of (name, value). + Override this to add custom attributes. + """ + startTime = str(self.startTime)[:19] + duration = str(self.stopTime - self.startTime) + status = [] + if result.success_count: status.append('Pass %s' % result.success_count) + if result.failure_count: status.append('Failure %s' % result.failure_count) + if result.error_count: status.append('Error %s' % result.error_count ) + if status: + status = ' '.join(status) + else: + status = 'none' + return [ + ('Start Time', startTime), + ('Duration', duration), + ('Status', status), + ] + + + def generateReport(self, test, result): + report_attrs = self.getReportAttributes(result) + generator = 'HTMLTestRunner %s' % __version__ + stylesheet = self._generate_stylesheet() + heading = self._generate_heading(report_attrs) + report = self._generate_report(result) + ending = self._generate_ending() + output = self.HTML_TMPL % dict( + title = saxutils.escape(self.title), + generator = generator, + stylesheet = stylesheet, + heading = heading, + report = report, + ending = ending, + ) + self.stream.write(output.encode('utf8')) + + + def _generate_stylesheet(self): + return self.STYLESHEET_TMPL + + + def _generate_heading(self, report_attrs): + a_lines = [] + for name, value in report_attrs: + line = self.HEADING_ATTRIBUTE_TMPL % dict( + name = saxutils.escape(name), + value = saxutils.escape(value), + ) + a_lines.append(line) + heading = self.HEADING_TMPL % dict( + title = saxutils.escape(self.title), + parameters = ''.join(a_lines), + description = saxutils.escape(self.description), + ) + return heading + + + def _generate_report(self, result): + rows = [] + sortedResult = self.sortResult(result.result) + for cid, (cls, cls_results) in enumerate(sortedResult): + # subtotal for a class + np = nf = ne = 0 + for n,t,o,e in cls_results: + if n == 0: np += 1 + elif n == 1: nf += 1 + else: ne += 1 + + # format class description + if cls.__module__ == "__main__": + name = cls.__name__ + else: + name = "%s.%s" % (cls.__module__, cls.__name__) + doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" + desc = doc and '%s: %s' % (name, doc) or name + + row = self.REPORT_CLASS_TMPL % dict( + style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', + desc = desc, + count = np+nf+ne, + Pass = np, + fail = nf, + error = ne, + cid = 'c%s' % (cid+1), + ) + rows.append(row) + + for tid, (n,t,o,e) in enumerate(cls_results): + self._generate_report_test(rows, cid, tid, n, t, o, e) + + report = self.REPORT_TMPL % dict( + test_list = ''.join(rows), + count = str(result.success_count+result.failure_count+result.error_count), + Pass = str(result.success_count), + fail = str(result.failure_count), + error = str(result.error_count), + ) + return report + + + def _generate_report_test(self, rows, cid, tid, n, t, o, e): + # e.g. 'pt1.1', 'ft1.1', etc + has_output = bool(o or e) + tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) + name = t.id().split('.')[-1] + doc = t.shortDescription() or "" + desc = doc and ('%s: %s' % (name, doc)) or name + tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL + + # o and e should be byte string because they are collected from stdout and stderr? + if isinstance(o,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # uo = unicode(o.encode('string_escape')) + uo = o.decode('latin-1') + else: + uo = o + if isinstance(e,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # ue = unicode(e.encode('string_escape')) + ue = e.decode('latin-1') + else: + ue = e + + script = self.REPORT_TEST_OUTPUT_TMPL % dict( + id = tid, + output = saxutils.escape(uo+ue), + ) + + row = tmpl % dict( + tid = tid, + Class = (n == 0 and 'hiddenRow' or 'none'), + style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), + desc = desc, + script = script, + status = self.STATUS[n], + ) + rows.append(row) + if not has_output: + return + + def _generate_ending(self): + return self.ENDING_TMPL + + +############################################################################## +# Facilities for running tests from the command line +############################################################################## + +# Note: Reuse unittest.TestProgram to launch test. In the future we may +# build our own launcher to support more specific command line +# parameters like test title, CSS, etc. +class TestProgram(unittest.TestProgram): + """ + A variation of the unittest.TestProgram. Please refer to the base + class for command line parameters. + """ + def runTests(self): + # Pick HTMLTestRunner as the default test runner. + # base class's testRunner parameter is not useful because it means + # we have to instantiate HTMLTestRunner before we know self.verbosity. + if self.testRunner is None: + self.testRunner = HTMLTestRunner(verbosity=self.verbosity) + unittest.TestProgram.runTests(self) + +main = TestProgram + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/test/test-rpc-v3/MOCs.py b/test/test-rpc-v3/MOCs.py new file mode 100644 index 000000000..32d9edaa5 --- /dev/null +++ b/test/test-rpc-v3/MOCs.py @@ -0,0 +1,182 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +from utils import mvs_rpc + +class CertType: + NONE = 0 + ISSUE = 1 << 0 + DOMAIN = 1 << 1 + NAMING = 1 << 2 + ALL = 0xffffffffffffffff + + @classmethod + def get_type_name(cls, type): + type2name = { + cls.NONE: "NONE", + cls.ISSUE: "ISSUE", + cls.DOMAIN: "DOMAIN", + cls.NAMING: "NAMING", + cls.ALL: "ALL" + } + + + +class Asset: + def __init__(self, symbol): + self.symbol = symbol + + self.address = None + self.status = None + self.description = None + self.decimal_number = None + + self.issuer = None + self.is_secondaryissue = None + self.secondaryissue_threshold = None + + self.quantity = None + self.maximum_supply = None + + @classmethod + def init(cls, json_report): + + obj = Asset(json_report['symbol']) + obj.address = json_report['address'] + obj.status = json_report['status'] + obj.description = json_report['description'] + obj.decimal_number = json_report['decimal_number'] + + obj.issuer = json_report['issuer'] + obj.is_secondaryissue = json_report.get('is_secondaryissue', None) + obj.secondaryissue_threshold = json_report['secondaryissue_threshold'] + + obj.quantity = json_report['quantity'] + obj.maximum_supply = json_report.get('maximum_supply', None) + + return obj + +class Cert: + def __init__(self, symbol): + self.address = None + self.cert = None + self.owner = None + self.symbol = symbol + + @classmethod + def init(cls, json_report): + obj = Asset(json_report['symbol']) + obj.address = json_report['address'] + obj.cert = json_report['cert'] + obj.owner = json_report['owner'] + return obj + +class Did: + def __init__(self, symbol): + self.address = None + self.status = None + self.symbol = symbol + + @classmethod + def init(cls, json_report): + obj = Asset(json_report['symbol']) + obj.address = json_report['address'] + obj.status = json_report['status'] + return obj + +class Attachment: + def __init__(self): + self.type = "" # etp/message/... + self.content = "" + + @classmethod + def from_json(cls, json_report): + a = cls() + + a.type = json_report["type"] + if a.type == "message": + a.content = json_report["content"] + + return a + +class PrevOutput: + def __init__(self): + self.index = 0 + self.hash = "" + + @classmethod + def from_json(cls, json_report): + p = cls() + p.index = json_report["index"] + p.hash = json_report["hash"] + + return p + +class Input: + def __init__(self): + self.previous_output = None + self.script = "" + self.sequence = 0 + self.address = "" + + @classmethod + def from_json(cls, json_report): + i = cls() + i.previous_output = PrevOutput.from_json( json_report["previous_output"] ) + i.script = json_report["script"] + i.sequence = json_report["sequence"] + if i.previous_output.index <> 0xFFFFFFFF: + i.address = json_report["address"] + + return i + +class Output: + def __init__(self): + self.index = 0 + self.script = '' + self.value = 0 + self.attachment = None + self.address = "" + self.locked_height_range = 0 + + @classmethod + def from_json(cls, json_report): + o = cls() + o.index = json_report["index"] + o.script = json_report["script"] + o.value = json_report["value"] + o.attachment = Attachment.from_json(json_report["attachment"]) + o.address = json_report["address"] + o.locked_height_range = json_report["locked_height_range"] + + return o + +class Transaction: + def __init__(self): + self.inputs = [] + self.outputs = [] + self.hash = "" + self.lock_time = 0 + self.height = 0 + self.version = 0 + + @classmethod + def from_json(cls, json_report): + tx = cls() + for input in json_report['inputs']: + tx.inputs.append( Input.from_json(input) ) + + for output in json_report['outputs']: + tx.outputs.append( Output.from_json(output) ) + + tx.hash = json_report['hash'] + tx.lock_time = json_report['lock_time'] + tx.height = json_report['height'] + tx.version = json_report['version'] + + return tx + + @classmethod + def from_hash(cls, hash): + ec, message = mvs_rpc.gettx(hash) + assert(ec == 0) + return cls.from_json(message) diff --git a/test/test-rpc-v3/Roles.py b/test/test-rpc-v3/Roles.py new file mode 100644 index 000000000..6f40703e4 --- /dev/null +++ b/test/test-rpc-v3/Roles.py @@ -0,0 +1,482 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import os, time +from utils import mvs_rpc, common +import MOCs + +class Role: + def __init__(self, name, mnemonic, addresslist, keystore_file): + self.name = name + self.password = name[0] + "123456" + self.mnemonic = mnemonic.split() + self.addresslist = addresslist + self.keystore_file = keystore_file + + self.addresslist.reverse() + self.did_symbol = (name+".DIID").upper() + self.did_address = None + self.asset_symbol = "" + self.domain_symbol = "" + self.multisig_addresses = {} # desc : multisig-addr + + def ensure_balance(self, mount=500): + try: + while self.get_balance() < mount * (10**8): + self.mining(50) + finally: + print("ensure balance of {}".format(self.name)) + + def lastword(self): + return self.mnemonic[-1] + + def mainaddress(self): + return self.addresslist[0] + + def didaddress(self): + if None == self.did_address: + ec, message = mvs_rpc.list_dids(self.name, self.password) + assert (ec == 0) + if message: + dids = [MOCs.Did.init(i) for i in message if i] + found_dids = filter(lambda a: a.symbol == self.did_symbol, dids) + assert(len(found_dids) == 1) + self.did_address = found_dids[0].address + + return self.did_address + + def create(self): + ''' + create account by importkeyfile + ''' + #auto create a new asset name for each time create_asset is called + self.domain_symbol = (self.name + common.get_random_str()).upper() + self.asset_symbol = (self.domain_symbol + ".AST." + common.get_random_str()).upper() + return mvs_rpc.import_keyfile(self.name, self.password, self.keystore_file) + + def dump_keyfile(self, path): + return mvs_rpc.dump_keyfile(self.name, self.password, self.lastword(), path) + + def delete(self): + ''' + delete account by deleteaccount + ''' + return mvs_rpc.delete_account(self.name, self.password, self.lastword()) + + def get_balance(self): + ec, message = mvs_rpc.get_balance(self.name, self.password) + if ec != 0: + print("get_balance: {}".format(message)) + assert (ec == 0) + return message['total_available'] # spendable + + def register_did(self,address=None,symbol=None): + ''' + issue did to the main address. + ''' + if address == None: + address = self.mainaddress() + if symbol == None: + symbol = self.did_symbol + + return mvs_rpc.register_did(self.name, self.password, address, symbol) + + def create_random_asset(self, domain_symbol=None, did_symbol=None, is_issue=True, secondary=0): + if None == domain_symbol: + domain_symbol = (self.name + common.get_random_str()).upper() + asset_symbol = domain_symbol + ".AST" + self.create_asset_with_symbol(asset_symbol, is_issue, secondary, did_symbol) + return domain_symbol, asset_symbol + + def create_asset_with_symbol(self, symbol, is_issue=True, secondary=0, did_symbol=None): + if did_symbol == None: + did_symbol = self.did_symbol + result, message = mvs_rpc.create_asset(self.name, self.password, symbol, 300000, did_symbol, description="%s's Asset" % self.name, rate=secondary) + if (result): + print("#ERROR#: failed to create asset: {}".format(message)) + assert (result == 0) + if is_issue: + result, message = mvs_rpc.issue_asset(self.name, self.password, symbol) + if (result): + print("#ERROR#: failed to issue asset: {}".format(message)) + assert (result == 0) + return result, message + + def issue_asset_with_symbol(self, symbol, attenuation_model=None): + return mvs_rpc.issue_asset(self.name, self.password, symbol, model=attenuation_model) + + def secondary_issue_asset_with_symbol(self, symbol, attenuation_model=None, volume=None): + if volume == None: + volume = 300000 + return mvs_rpc.secondary_issue(self.name, self.password, self.did_symbol, symbol, volume=volume, model=attenuation_model, fee=None) + + def create_asset(self, is_issue=True, secondary=0): + ''' + issue asset to the main address. + secondary: The rate of secondaryissue. Default to 0, means the + asset is not allowed to secondary issue forever; + otherwise, -1 means the asset can be secondary issue + freely; otherwise, the valid rate is in range of 1 + to 100, means the asset can be secondary issue when + own percentage greater than or equal to the rate + value. + ''' + self.domain_symbol = (self.name + common.get_random_str()).upper(); + self.asset_symbol = (self.domain_symbol + ".AST." + common.get_random_str()).upper() + result, message = mvs_rpc.create_asset(self.name, self.password, self.asset_symbol, 300000, self.did_symbol, description="%s's Asset" % self.name, rate=secondary) + assert (result == 0) + if is_issue: + result, message = mvs_rpc.issue_asset(self.name, self.password, self.asset_symbol) + assert (result == 0) + + def issue_cert(self, to_): + cert_symbol = (self.name + ".2%s." % to_.name + common.get_random_str()).upper() + result, message = mvs_rpc.issue_cert(self.name, self.password, to_.did_symbol, cert_symbol, "NAMING") + if result != 0: + print("failed to issue_cert: {}".format(message)) + assert (result == 0) + return cert_symbol + + def issue_naming_cert(self, domain_symbol,did_symbol=None): + if did_symbol==None: + did_symbol = self.did_symbol + + cert_symbol = (domain_symbol + "." + common.get_random_str()).upper() + result, message = mvs_rpc.issue_cert(self.name, self.password, did_symbol, cert_symbol, "NAMING") + if result != 0: + print("failed to issue_cert: {}".format(message)) + assert (result == 0) + return cert_symbol + + def delete_localasset(self, asset_symbol=None): + if None == asset_symbol: + asset_symbol = self.asset_symbol + result, message = mvs_rpc.delete_localasset(self.name, self.password, asset_symbol) + assert (result == 0) + + @classmethod + def get_asset(cls, asset_symbol=None, cert=False): + result, message = mvs_rpc.get_asset(asset_symbol) + assert (result == 0) + if cert: + if message: + return [MOCs.Cert.init(i) for i in message if i] + else: + if message: + return [MOCs.Asset.init(i) for i in message if i] + return [] + + def get_accountasset(self, asset_symbol=None, cert=False): + if None == asset_symbol: + asset_symbol = self.asset_symbol + result, message = mvs_rpc.get_accountasset(self.name, self.password, asset_symbol) + assert (result == 0) + if cert: + if message: + return [MOCs.Cert.init(i) for i in message if i] + else: + if message: + return [MOCs.Asset.init(i) for i in message if i] + return [] + + @classmethod + def get_addressasset(cls, address, cert=False): + result, message = mvs_rpc.get_addressasset(address, cert) + assert (result == 0) + if cert: + if message: + return [MOCs.Cert.init(i) for i in message if i] + else: + if message: + return [MOCs.Asset.init(i) for i in message if i] + return [] + + def send_asset(self, to_, amount, asset_symbol=None): + if not asset_symbol: + asset_symbol = self.asset_symbol + result, message = mvs_rpc.send_asset(self.name, self.password, to_, asset_symbol, amount) + if (result != 0): + print("failed to send_asset: {}, {}".format(result, message)) + assert (result == 0) + + def send_asset_from(self, from_, to_, amount, asset_symbol=None): + if not asset_symbol: + asset_symbol = self.asset_symbol + result, message = mvs_rpc.send_asset_from(self.name, self.password, from_, to_, asset_symbol, amount) + assert (result == 0) + + def burn_asset(self, amount, asset_symbol=None): + if not asset_symbol: + asset_symbol = self.asset_symbol + result, message = mvs_rpc.burn(self.name, self.password, asset_symbol, amount) + assert (result == 0) + + def send_etp(self, to_, amount): + result, message = mvs_rpc.send(self.name, self.password, to_, amount) + assert (result == 0) + return message["hash"] + + def sendmore_etp(self, receivers): + result, message = mvs_rpc.sendmore(self.name, self.password, receivers) + assert (result == 0) + + def didsend_etp(self, to_, amount): + ''' + :param to_: to did/address + ''' + result, message = mvs_rpc.didsend(self.name, self.password, to_, amount) + assert (result == 0) + return message["hash"] + + def didsend_etp_from(self, from_, to_, amount): + ''' + :param from_: did/address + :param to_: did/address + ''' + result, message = mvs_rpc.didsend_from(self.name, self.password, from_, to_, amount) + assert (result == 0) + return message["hash"] + + def didsend_asset(self, to_, amount, symbol): + result, message = mvs_rpc.didsend_asset(self.name, self.password, to_, symbol, amount) + assert (result == 0) + return message["hash"] + + def didsend_asset_from(self, from_, to_, amount, symbol): + result, message = mvs_rpc.didsend_asset_from(self.name, self.password, from_, to_, symbol, amount) + assert (result == 0) + return message["hash"] + + def mining(self, times=1): + ''' + use the mainaddress to mining x times. + do mining to get the main address rich. + + result, (height_origin, _) = mvs_rpc.get_info() + assert (result == 0) + mvs_rpc.start_mining(self.name, self.password, self.mainaddress(), times) + for i in range(10): + time.sleep(0.1) + result, (height_new, _) = mvs_rpc.get_info() + assert (result == 0) + if height_new == (height_origin + times): + break + + return + ''' + from ethereum.pow.ethpow import mine + result, message = mvs_rpc.set_miningaccount( + self.name, + self.password, + self.mainaddress() + ) + assert(result == 0) + + def __mine__(): + result, (header_hash, seed_hash, boundary) = mvs_rpc.eth_get_work() + assert (result == 0) + result, (height, difficulty) = mvs_rpc.get_info() + assert (result == 0) + + rounds = 100 + nonce = 0 + while True: + bin_nonce, mixhash = mine(block_number=height+1, difficulty=difficulty, mining_hash=header_hash, + rounds=rounds, start_nonce=nonce) + if bin_nonce: + break + nonce += rounds + return bin_nonce, '0x' + common.toString(header_hash), '0x' + common.toString(mixhash) + + for i in xrange(times): + bin_nonce, header_hash, mix_hash = __mine__() + result, message = mvs_rpc.eth_submit_work('0x' + common.toString(bin_nonce), header_hash, mix_hash) + assert (result == 0) + + def new_multisigaddress(self, description, others, required_key_num): + ''' + don't use the same description for different multisig_addresses. + ''' + result, message = mvs_rpc.getnew_multisig( + self.name, + self.password, + description, + self.get_publickey( self.mainaddress() ), + [i.get_publickey( i.mainaddress() ) for i in others], + required_key_num) + assert (result == 0) + assert(message["description"] == description) + self.multisig_addresses[description] = message["address"] + return message["address"] + + def get_publickey(self, address): + result, publickey = mvs_rpc.get_publickey(self.name, self.password, address) + assert (result == 0) + return publickey + + def get_multisigaddress(self, description): + return self.multisig_addresses[description] + + def get_didaddress(self, symbol): + ec, message = mvs_rpc.list_didaddresses(symbol) + assert(ec == 0) + return message[0]['address'] + + def register_mit(self, to_did, symbol=None, content=None, mits=None, fee=None): + if None == to_did: + to_did = self.did_symbol + ec, message = mvs_rpc.register_mit(self.name, self.password, to_did, symbol, content, mits, fee) + assert(ec == 0) + return symbol + + def transfer_mit(self, to_did, symbol, fee=None): + return mvs_rpc.transfer_mit(self.name, self.password, to_did, symbol, fee) + + def list_mits(self, name=None, password=None): + return mvs_rpc.list_mits(name, password) + + def get_mit(self, symbol=None, trace=False, page_index=1, page_limit=100): + return mvs_rpc.get_mit(symbol, trace, page_index, page_limit) + +class NewGuy(Role): + ''' + New guy means: + 1. account is newly created by getnewaccount each time create() is called; + 2. this new guy always have no money after created. + 3. the most effient way to get money is some other(Alice/Bob/...) send etp to him + ''' + + def __init__(self, name): + Role.__init__(self, name, "", [], "") + + + def create(self): + ''' + create account by getnewaccount + ''' + #auto create a new asset name for each time create_asset is called + self.asset_symbol = (self.name + ".AST." + common.get_random_str()).upper() + + result, self.mnemonic = mvs_rpc.new_account(self.name, self.password) + if result != 0: + print("create_new_account: {}".format(self.mnemonic)) + assert (result == 0) + + f = open('./Zac.txt', 'w') + print >> f, self.lastword() + f.close() + + result, _ = mvs_rpc.new_address(self.name, self.password, 9) + assert (result == 0) + result, self.addresslist = mvs_rpc.list_addresses(self.name, self.password) + assert (result == 0) + assert (len(self.addresslist) == 10) + self.addresslist.reverse() + return 0, "success" + + def delete(self): + if (not self.mnemonic) and os.path.exists('./Zac.txt'): + with open('./Zac.txt') as f: + lastword = f.read() + self.mnemonic = [lastword.strip()] + return Role.delete(self) + + +homedir = os.path.dirname( os.path.realpath(__file__) ) +keystoredir = 'resource/keystore' + +#注意, 此处粘贴的listaddresses的结果,与实际address的顺序相反,因此最后一个地址才是第一个 + +Alice = Role("Alice", "notice judge certain company novel quality plunge list blind library ride uncover fold wink biology original aim whale stand coach hire clinic fame robot", + [ + "MECD5XzG4Z1eQSyYwZB3YyK3qmct33jT7c", + "MAhmaLbfLFFMQF88xWAqibDaPfQfYqpv8Y", + "MNWyXfH6pD1RwfeBiRRqm5eyiu3yAyxyvC", + "MFjNXDxz8mnKfdTHUjyhLoVMsKY7MixV3V", + "MLixg7rxKmtPj9DT9wPKSy6WkJkoUDWUSv", + "MG4kcurE89NPhKX24PgntbTway7sPGmsU4", + "M9qeDursSJsxK9nHzTcRMnqpe78YXCKv48", + "MP5FoYQHiEQ52pcEURkaYmuqZMnYHNAZ83", + "M9L3ipy3Hcf6kdvknU3mH7mwH9ER3uCziu", + "MLasJFxZQnA49XEvhTHmRKi2qstkj9ppjo" + ], + os.path.join(homedir, keystoredir, "Alice.json"), + ) +Bob = Role("Bob", "umbrella social junk engine slender slam adult piece van eight high marriage honey clerk fox input perfect super net refuse connect kick retire sight", + [ + "MSGA7so2bwjYRpycD7fSUiBmQbjwhFgxmU", + "MSqVRTpY6r3deZckkHG5rdvfaeyrKtZkBb", + "MQJ3PCshNdkLvqdF9xu1wf4AssKssKEs3d", + "MA7yi8rAapTavi8vMnBB8smBiXJVqZVUEH", + "MGEupYHnCqEXYB5QTsaiySAeHARBqoqRq9", + "MAZNqQZdeVasSYPu9uFXpnt4JKq7VGYV7h", + "MQoQa3PPTGZyQDapjCmA39aKBmdRWTQTii", + "MHcHpynXXgNcVkBr34UNyK57KWwXAeyCSq", + "MENcHb2KfxjkobX9zTBsJLJ8QdeVW1RteP", + "MRyes39YXS3MJLf2fNVTtriVRY93v5HARr" + ], + os.path.join(homedir, keystoredir, "Bob.json"), + ) +Cindy = Role("Cindy", "small wreck custom end misery aspect spin cricket secret jar goose deliver lens siren wife stick weird enroll excess spin sure wheat april iron", + [ + "MJyvvycyGvtJdgwaJvHgckbpcwPuC8EuhY", + "MBStPH1pHHfuwwq4fQQWHjJYFHjsTbYV83", + "MVdmWZQdKxDTo6mum8N2j8v88Gj2ivexmb", + "MDNWXUEReJHNdG21JcWYAtaxRKRedu5WNw", + "MLJoSoK66j7pq94XGQC2sRSUyxErbarCNd", + "MV2oZAqY6nLXFh2W9YvL2mXE38k3vKoqyt", + "MHLSeFwxQoG9unD5ynXrf66Qbihe6FDUQZ", + "MP5RwD3YHQu9r3sAqwzZYY4SkfP9BDEu4W", + "MM3JMm5LWt3hvhsG63FdhwEiH9kCgcyudU", + "MVazverbZySt7XUdBjEgAoADHLc2tGML3o" + ], + os.path.join(homedir, keystoredir, "Cindy.json"), + ) +Dale = Role("Dale", "cute board similar kind retreat then permit endorse behind swim ribbon photo enjoy obey warrior wink level topple uphold equip suffer present galaxy cushion", + [ + "MRFRbfB6S6cAAPVQ9ED81SkFueiqipKZGB", + "MQRwHf7SUjBRYPKdoAynzhmuW48kWg4Brz", + "MBTJm2T832DDSYagDwx4jwowG2wiK8Bt6Y", + "MRKXE22Ztatzu8GKKaxz6aM1A62kQXUKM3", + "MJ64BWA76d2e8kiF1HM6FYzSRHYDqbkek5", + "MUrW4vuapsZjDBuXafCoZuiyW8FL3yZUbH", + "MQsZ5SobDzauZgucz4tK3x3H5jRX8i2xt8", + "ML4Kdra4mNN2vo1qRZtaVHdhmYnwq1cUYy", + "MApKhkMJzW6H3FfKwcBd6FZ3wcnbnGne8S", + "MSKFnUNc1njquyo9vGNgFnE7QuR8ygfA4s" + ], + os.path.join(homedir, keystoredir, "Dale.json"), + ) +Eric = Role("Eric", "burst tuition primary deer piece bird nose broccoli wasp mule since bubble ladder discover bleak guide brush kiwi luxury only fringe arrange panic shine", + [ + "MNeM7HX83myQUzr5cyHGQmaUD73ehnZjkD", + "ME2ekGYHbV9SFAsERZZXX2L3cttLyYWg4V", + "MJmuEUwjCJousWwHoDEWTkyHUGgSnwhtNt", + "MSjPxFJg3GYdgDBF41D8dxnEbDLCmAN7hK", + "MR1KDdvPe6k9eTutiyb3x8KDTEP22BCmFn", + "MLniaLzNzTxXHvouFcy1v5Rtjjbcj8d51w", + "MBj9uxesRQsxWWufjf2LpXsehs3LDsJ1PK", + "MKZbYVAoRVdUqveG33Wt2dsRfVrjYRM5JU", + "MJjT9TXtwCnaeF12xvWcSiGNC3EsGSQGQD", + "MAZcEqWkxCGDRoddMBnAqEYeAHgGzTJ4Zn" + ], + os.path.join(homedir, keystoredir, "Eric.json"), + ) +Frank = Role("Frank", "angle toddler glow message cart tired scissors violin wisdom stumble toe opera car danger erupt road tourist spirit prosper menu minimum spirit account garbage", + [ + "MWcLcop8RniSGyhjQtibxB79AJWwkk5b7C", + "MFUQzjnoDnT7uEaYqo4nnKUUyXo6fjE2m3", + "MTSWGXb6LFeJpjpFaG4NrUWo8gad9C37Ud", + "M8K9qWfesYahsyXEVkep9JGRUGqCTZSPmQ", + "MS3uvnk7JPenzBShJ2wrNE7yW6uwCHKvcB", + "MQDLNa6gAkyvVyp6KfpoPRTryv1t9PzB5z", + "MRiR4ey6vSHYqdj5eVdaeEy5UkHM5zYXZQ", + "MEsKFY6nMx5VJ1VVWaCTii6ghQPuKwXGQM", + "M9T5X7wWd2bGJT3BSs6FBpfEHdu5tCgsPh", + "MDdrddED3NKSQoat6AGUojZ1bA5hoxmE1P" + ], + os.path.join(homedir, keystoredir, "Frank.json"), + ) + +Zac = NewGuy("Zac") + +__all__ = ["Alice", "Bob", "Cindy", "Dale", "Eric", "Frank", "Zac"] \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/Account/__init__.py b/test/test-rpc-v3/TestCase/Account/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/Account/batch_account.py b/test/test-rpc-v3/TestCase/Account/batch_account.py new file mode 100644 index 000000000..4cdde7e4e --- /dev/null +++ b/test/test-rpc-v3/TestCase/Account/batch_account.py @@ -0,0 +1,54 @@ +from utils import common, database +from TestCase.MVSTestCase import * + +class TestAccount(MVSTestCaseBase): + roles = () + need_mine = False + def test_0_new_account(self): + '''create new account * 5000''' + account_table_file = '/home/%s/.metaverse/mainnet/account_table' % common.get_username() + origin_payload_size = database.get_payload_size(account_table_file) + + batch_amount = 5000 + lastwords = [] + for i in xrange(batch_amount): + ec, message = mvs_rpc.new_account("Account_%s" % i, "123456") + self.assertEqual(ec, 0, message) + lastwords.append( message[-1] ) + try: + current_payload_size = database.get_payload_size(account_table_file) + # each simple account record size < 300, but when getnew address, the account record will be create twice, so 600 is the reasonable record size. + self.assertGreater(600 * batch_amount, current_payload_size - origin_payload_size, "each account record size shall be less than 600.") + finally: + for i in xrange(batch_amount): + ec, message = mvs_rpc.delete_account("Account_%s" % i, "123456", lastwords[i]) + self.assertEqual(ec, 0, message) + + def test_1_new_address(self): + '''new address for Zac''' + max_duration = 0.01 + avg_duration = 0.002 + + round = 5000 + + Zac.create() + account_table_file = '/home/%s/.metaverse/mainnet/account_table' % common.get_username() + + try: + origin_payload_size = database.get_payload_size(account_table_file) + + durations = [] + for i in xrange(round): + duration, ret = common.duration_call(mvs_rpc.new_address, Zac.name, Zac.password) + self.assertEqual(ret[0], 0, "mvs_rpc.new_address failed!") + self.assertLess(duration, max_duration) + durations.append(duration) + self.assertLess(sum(durations), avg_duration*round) + + current_payload_size = database.get_payload_size(account_table_file) + + # each simple account record size < 300 + self.assertGreater(300 * round, current_payload_size - origin_payload_size, + "each account record size shall be less than 300.") + finally: + Zac.delete() \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/Account/test_account.py b/test/test-rpc-v3/TestCase/Account/test_account.py new file mode 100644 index 000000000..814c536c1 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Account/test_account.py @@ -0,0 +1,98 @@ +import random +from TestCase.MVSTestCase import * + +class TestAccount(MVSTestCaseBase): + roles = (Alice,) + need_mine = False + def test_0_new_account(self): + #account already exist + ec, message = mvs_rpc.new_account(Alice.name, "12345678") + self.assertEqual(ec, 3001, message) + + def test_1_get_account(self): + #password error + ec, message = mvs_rpc.get_account(Alice.name, Alice.password+'1', Alice.lastword()) + self.assertEqual(ec, 1000, message) + + #lastword error + ec, message = mvs_rpc.get_account(Alice.name, Alice.password, Alice.mnemonic[-2]) + self.assertEqual(ec, 1000, message) + + ec, message = mvs_rpc.get_account(Alice.name, Alice.password, Alice.lastword()) + self.assertEqual(ec, 0, message) + mnemonic, address_num = message + + self.assertEqual(mnemonic, ' '.join(Alice.mnemonic)) + self.assertEqual(address_num, 11) + + def test_2_delete_account(self): + # password error + ec, message = mvs_rpc.delete_account(Alice.name, Alice.password + '1', Alice.lastword()) + self.assertEqual(ec, 1000, message) + + # lastword error + ec, message = mvs_rpc.delete_account(Alice.name, Alice.password, Alice.mnemonic[-2]) + self.assertEqual(ec, 1000, message) + + ec, message = mvs_rpc.delete_account(Alice.name, Alice.password, Alice.lastword()) + self.assertEqual(ec, 0, message) + + #TODO: this shall access the database directly~~ + #check account-address + + # check account-asset + + Alice.create() + + def test_3_import_account(self): + # account already exist + ec, message = mvs_rpc.import_account(Alice.name, Alice.password, 'Alice.mnemonic') + self.assertEqual(ec, 3001, message) + + + Alice.delete() + # invalid mnemonic word at random position + i = random.randint(0, len(Alice.mnemonic) - 1) + mnemonic = Alice.mnemonic[:i] + ["@invalid"] + Alice.mnemonic[i+1:] + + ec, message = mvs_rpc.import_account(Alice.name, Alice.password, ' '.join(mnemonic)) + self.assertEqual(ec, 9202, message) + + #create 3 address + ec, message = mvs_rpc.import_account(Alice.name, Alice.password, ' '.join(Alice.mnemonic), 3) + self.assertEqual(ec, 0, message) + + ec, addresses = mvs_rpc.list_addresses(Alice.name, Alice.password) + self.assertEqual(ec, 0, addresses) + addresses.reverse() + self.assertEqual(addresses, Alice.addresslist[:3]) + + def test_4_changepasswd(self): + # password error + ec, message = mvs_rpc.change_passwd(Alice.name, Alice.password+'1', 'Alice.mnemonic') + self.assertEqual(ec, 1000, message) + + new_password = 'test_4_changepasswd' # -> the test case name + old_password = Alice.password + + ec, message = mvs_rpc.change_passwd(Alice.name, Alice.password, new_password) + self.assertEqual(ec, 0, message) + Alice.password = new_password # -> so that the account can be deleted when tearDown + + ec, addresses = mvs_rpc.list_addresses(Alice.name, new_password) + self.assertEqual(ec, 0, addresses) + self.assertEqual(addresses, Alice.addresslist) + + #try the old password + ec, message = mvs_rpc.list_addresses(Alice.name, old_password) + self.assertEqual(ec, 1000, message) + + Alice.delete() + Alice.password = old_password + Alice.create() + + + + + + diff --git a/test/test-rpc-v3/TestCase/Account/test_address.py b/test/test-rpc-v3/TestCase/Account/test_address.py new file mode 100644 index 000000000..c8be700bc --- /dev/null +++ b/test/test-rpc-v3/TestCase/Account/test_address.py @@ -0,0 +1,38 @@ +import random +from TestCase.MVSTestCase import * + +class TestAccount(MVSTestCaseBase): + roles = (Alice,) + need_mine = False + + def test_0_new_address(self): + #password error + ec, message = mvs_rpc.new_address(Alice.name, Alice.password+'1') + self.assertEqual(ec, 1000, message) + + #check address_count + ec, message = mvs_rpc.new_address(Alice.name, Alice.password, 0) + self.assertEqual(ec, 4004, message) + + ec, message = mvs_rpc.new_address(Alice.name, Alice.password, 0x00100000) + self.assertEqual(ec, 4004, message) + + ec, message = mvs_rpc.new_address(Alice.name, Alice.password, 11) + self.assertEqual(ec, 0, message) + + def test_1_list_addresses(self): + # password error + ec, message = mvs_rpc.list_addresses(Alice.name, Alice.password + '1') + self.assertEqual(ec, 1000, message) + + ec, addresses = mvs_rpc.list_addresses(Alice.name, Alice.password) + self.assertEqual(ec, 0, addresses) + addresses.sort() + alice_addresses = Alice.addresslist[:] + alice_addresses.sort() + self.assertEqual(addresses, alice_addresses) + + def test_2_check_address(self): + for address in Alice.addresslist: + ec, message = mvs_rpc.check_address(address) + self.assertEqual(ec, 0, message) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/Account/test_keyfile.py b/test/test-rpc-v3/TestCase/Account/test_keyfile.py new file mode 100644 index 000000000..a1a66173c --- /dev/null +++ b/test/test-rpc-v3/TestCase/Account/test_keyfile.py @@ -0,0 +1,75 @@ +import os +import utils.common as common +import utils.cryptojs as cryptojs +import json + +from TestCase.MVSTestCase import * + +class TestKeyfile(MVSTestCaseBase): + roles = MVSTestCaseBase.roles[:-1] # exclude Zac + need_mine = False + + def test_dumpkeyfile(self): + description = "Alice & Bob & Cindy's multi-sig address" + + multisigaddress = Alice.new_multisigaddress(description, [Bob, Cindy], 2) + + keyfile = os.path.abspath("./fullwallet_keystore.json") + common.remove_file(keyfile) + result, message = Alice.dump_keyfile(keyfile) + self.assertEqual(result, 0, message) + self.assertEqual(os.path.exists(keyfile), True, keyfile + " not exists!") + + f = open(keyfile) + keyfile_content = json.load(f) + f.close() + + mnemonic = cryptojs.AES_CBC_decrypt(keyfile_content['mnemonic'], Alice.password) + self.assertEqual(mnemonic, '"' + " ".join(Alice.mnemonic) + '"', "mnemonic not match") + + self.assertEqual(keyfile_content['multisigs'][0]['d'], description, "decription not match!") + self.assertEqual(keyfile_content['multisigs'][0]['s'], Alice.get_publickey(Alice.mainaddress())) + + #delete account + Alice.delete() + # ensure the account has been deleted + result, message = mvs_rpc.get_account( + Alice.name, + Alice.password, + Alice.mnemonic[-1]) + self.assertEqual(result, 1000, message) + + #recover the account by FullWalletKeyfile + result, message = mvs_rpc.import_keyfile(Alice.name, Alice.password, keyfile) + self.assertEqual(result, 0, message) + + common.remove_file(keyfile) + + result, message = mvs_rpc.list_multisig(Alice.name, Alice.password) + self.assertEqual(result, 0, message) + multisig = message + self.assertEqual(len(multisig), 1, message) + self.assertEqual(multisig[0]['self_publickey'], Alice.get_publickey(Alice.mainaddress())) + self.assertEqual(multisig[0]['description'], description, "decription not match!") + + self.assertEqual(multisig[0]['m'], 2) + self.assertEqual(multisig[0]['n'], 3) + + def test_importKeyFile_by_Content(self): + Alice.delete() + + f = open(Alice.keystore_file) + content = f.read() + f.close() + + result, message = mvs_rpc.import_keyfile( + Alice.name, + Alice.password, + "", + content + ) + self.assertEqual(result, 0, message) + + + + diff --git a/test/test-rpc-v3/TestCase/Asset/__init__.py b/test/test-rpc-v3/TestCase/Asset/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/Asset/batch_asset.py b/test/test-rpc-v3/TestCase/Asset/batch_asset.py new file mode 100644 index 000000000..d5cffac56 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Asset/batch_asset.py @@ -0,0 +1,36 @@ +from utils import common, mvs_rpc +from TestCase.MVSTestCase import * + +class TestAssetBatch(MVSTestCaseBase): + need_mine = False + def test_0_getaccountasset(self): + '''create 3000 address multisig address for Alice, and then ...''' + address_amount = 3000 + addresses = mvs_rpc.new_address(Alice.name, Alice.password, address_amount) + round = 5000 + + max_duration = 0.01 + avg_duration = 0.002 + durations = [] + + for i in xrange(round): + print i + duration, ret = common.duration_call(mvs_rpc.get_accountasset, Alice.name, Alice.password) + self.assertEqual(ret[0], 0, "mvs_rpc.get_accountasset failed!") + self.assertLess(duration, max_duration) + durations.append(duration) + self.assertLess(sum(durations), avg_duration * round) + + def test_1_getaddressasset(self): + round = 1000 + + max_duration = 0.01 + avg_duration = 0.005 + durations = [] + + for i in xrange(round): + duration, ret = common.duration_call(mvs_rpc.get_addressasset, Alice.mainaddress()) + self.assertEqual(ret[0], 0, "mvs_rpc.get_addressasset failed!") + self.assertLess(duration, max_duration) + durations.append(duration) + self.assertLess(sum(durations), avg_duration * round) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/Asset/test_asset.py b/test/test-rpc-v3/TestCase/Asset/test_asset.py new file mode 100644 index 000000000..8fc393536 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Asset/test_asset.py @@ -0,0 +1,107 @@ +from TestCase.MVSTestCase import * + +class TestAsset(MVSTestCaseBase): + + def test_1_create_asset(self): + Alice.ensure_balance() + + domain_symbol, asset_symbol = Alice.create_random_asset(is_issue=False) + Alice.mining() + + #validate result + assets = Alice.get_accountasset(asset_symbol) + self.assertEqual(len(assets), 1) + self.assertEqual(assets[0].symbol, asset_symbol) + self.assertEqual(assets[0].address, "") + self.assertEqual(assets[0].issuer, Alice.did_symbol) + self.assertEqual(assets[0].status, 'unissued') + + #delete_localasset + Alice.delete_localasset(asset_symbol) + assets = Alice.get_accountasset(asset_symbol) + self.assertEqual(len(assets), 0) + + def test_2_issue_asset(self): + Alice.create_asset() + Alice.mining() + + account_assets = Alice.get_accountasset() + found_assets = filter(lambda a: a.symbol == Alice.asset_symbol, account_assets) + self.assertEqual(len(found_assets), 1) + self.assertEqual(found_assets[0].symbol, Alice.asset_symbol) + self.assertEqual(found_assets[0].issuer, Alice.did_symbol) + self.assertEqual(found_assets[0].address, Alice.didaddress()) + self.assertEqual(found_assets[0].status, 'unspent') + + origin_amount = self.get_asset_amount(Alice) + self.assertGreater(origin_amount, 0) + + def get_asset_amount(self, role, address=None): + if address == None: + address = role.didaddress() + address_assets = role.get_addressasset(address) + + #we only consider Alice's Asset + found_assets = filter(lambda a: a.symbol == Alice.asset_symbol, address_assets) + self.assertEqual(len(found_assets), 1) + + previous_quantity = found_assets[0].quantity + previous_decimal = found_assets[0].decimal_number + return previous_quantity * (10 ** previous_decimal) + + def test_3_sendasset(self): + Alice.create_asset() + Alice.mining() + + origin_amount = self.get_asset_amount(Alice) + send_amount = 100 + #pre-set condition + self.assertGreater(origin_amount, send_amount) + Alice.send_asset(Zac.mainaddress(), send_amount) + Alice.mining() + + final_amount = self.get_asset_amount(Alice) + self.assertEqual(origin_amount - send_amount, final_amount) + self.assertEqual(send_amount, self.get_asset_amount(Zac, Zac.mainaddress())) + + def test_4_sendassetfrom(self): + Alice.create_asset() + Alice.mining() + + origin_amount = self.get_asset_amount(Alice) + send_amount = 100 + + # pre-set condition + self.assertGreater(origin_amount, send_amount) + + Alice.send_etp(Alice.didaddress(), 1 * 10 ** 8) + Alice.mining() + + Alice.send_asset_from(Alice.didaddress(), Zac.mainaddress(), send_amount) + Alice.mining() + + final_amount = self.get_asset_amount(Alice) + self.assertEqual(origin_amount - send_amount, final_amount) + self.assertEqual(send_amount, self.get_asset_amount(Zac, Zac.mainaddress())) + + def test_5_burn_asset(self): + Alice.create_asset() + Alice.mining() + + #use the asset created in the previous test case + #amout > previous_amount + amount = self.get_asset_amount(Alice) + ec, message = mvs_rpc.burn(Alice.name, Alice.password, Alice.asset_symbol, amount + 1) + self.assertEqual(ec, 5001, message) + + Alice.burn_asset(amount-1) + Alice.mining() + + current_amount = self.get_asset_amount(Alice) + self.assertEqual(current_amount, 1) + + Alice.burn_asset(1) + Alice.mining() + addressassets = Alice.get_addressasset(Alice.didaddress()) + addressasset = filter(lambda a: a.symbol == Alice.asset_symbol, addressassets) + self.assertEqual(len(addressasset), 0) diff --git a/test/test-rpc-v3/TestCase/Asset/test_asset_after_did_modified.py b/test/test-rpc-v3/TestCase/Asset/test_asset_after_did_modified.py new file mode 100644 index 000000000..6ced46104 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Asset/test_asset_after_did_modified.py @@ -0,0 +1,108 @@ +import time +from utils import common +import MOCs +from TestCase.MVSTestCase import * + +class TestAssetAfterDidModified(MVSTestCaseBase): + + def test_0_scenario_did_modified(self): + + # prepare address + # + Zac.mining() + Zac.mining() + Zac.mining() + result, addresslist = mvs_rpc.list_addresses(Zac.name, Zac.password) + + used_addresses = [] + ec, message = mvs_rpc.list_dids(Zac.name, Zac.password) + self.assertEqual(ec, code.success, message) + + if message: + dids = [MOCs.Did.init(i) for i in message if i] + used_addresses = [did.address for did in dids if did] + + addresslist = list(set(addresslist) ^ set(used_addresses)) + length = len(addresslist) + assert(length > 3) + + # create first did + fst_did_address = addresslist[length - 1] + fst_did_symbol = u"zacfirstdiid." + common.get_random_str() + + result, message = mvs_rpc.send(Alice.name, Alice.password, fst_did_address, 30 * 10 ** 8) + self.assertEqual(result, code.success, message) + Zac.mining() + + ec, message = Zac.register_did(fst_did_address, fst_did_symbol) + self.assertEqual(ec, code.success, message) + Zac.mining() + + # create second did + snd_did_address = addresslist[length - 2] + snd_did_symbol = u"zacmodifydiid." + common.get_random_str() + + result, message = mvs_rpc.send(Alice.name, Alice.password, snd_did_address, 15 * 10 ** 8) + self.assertEqual(result, code.success, message) + Zac.mining() + + ec, message = Zac.register_did(snd_did_address, snd_did_symbol) + self.assertEqual(ec, code.success, message) + Zac.mining() + + # prepare third address + # + trd_address = addresslist[length - 3] + + result, message = mvs_rpc.send(Alice.name, Alice.password, trd_address, 15 * 10 ** 8) + self.assertEqual(result, code.success, message) + Zac.mining() + + # create asset and cert + # + domain_symbol, fst_asset_symbol = Zac.create_random_asset(did_symbol=snd_did_symbol, secondary=-1) + Zac.mining() + + # change address of did + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, trd_address, snd_did_symbol) + self.assertEqual(ec, code.success, message) + Zac.mining() + + # test send asset + ec, message = mvs_rpc.didsend_asset(Zac.name, Zac.password, snd_did_symbol, fst_asset_symbol, 3000) + self.assertEqual(ec, code.success, message) + Zac.mining() + + # test secondary_issue + ec, message = mvs_rpc.secondary_issue(Zac.name, Zac.password, + to_did=fst_did_symbol, symbol=fst_asset_symbol, volume=3000) + self.assertEqual(ec, code.success, message) + Zac.mining() + + # test issue cert + naming_cert_symbol = domain_symbol + ".NAMING." + common.get_random_str() + ec, message = mvs_rpc.issue_cert(Zac.name, Zac.password, fst_did_symbol, naming_cert_symbol, "naming") + self.assertEqual(ec, code.success, message) + Zac.mining() + + # test transfer cert + ec, message = mvs_rpc.transfer_cert(Zac.name, Zac.password, fst_did_symbol, fst_asset_symbol, 'issue') + self.assertEqual(ec, code.success, message) + Zac.mining() + + # test issue new asset + # + snd_asset_symbol = domain_symbol + ".ASSEET." + common.get_random_str() + ec, message = mvs_rpc.create_asset(Zac.name, Zac.password, snd_asset_symbol, + volume=8000000, issuer=fst_did_symbol, rate=-1) + self.assertEqual(ec, code.success, message) + + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, snd_asset_symbol) + self.assertEqual(ec, code.success, message) + Zac.mining() + + # test secondary_issue + ec, message = mvs_rpc.secondary_issue(Zac.name, Zac.password, + to_did=fst_did_symbol, symbol=snd_asset_symbol, volume=3000) + self.assertEqual(ec, code.success, message) + Zac.mining() diff --git a/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py b/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py new file mode 100644 index 000000000..906968230 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py @@ -0,0 +1,261 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import random +from TestCase.MVSTestCase import * + +class TestAssetBoundary(MVSTestCaseBase): + ''' + create asset depends on did. + so Alice-Frank shall create their dids before running this test case + ''' + need_mine = False + + def getExistAssetSymbol(self): + # symbol is already used. + ec, message = mvs_rpc.get_asset() + self.assertEqual(ec, 0, message) + + exist_assets = message + if not exist_assets: + return None + # pickup existing asset symbol by random + i = random.randint(0, len(exist_assets) - 1) + return exist_assets[i] + + def test_0_check_asset_symbol(self): + spec_char_lst = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<>/?" + for char in spec_char_lst: + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, Alice.asset_symbol + char, 100, Alice.did_symbol) + self.assertEqual(ec, 1000, message) + self.assertEqual(message, "symbol must be alpha or number or dot", message) + + def test_1_create_asset(self): + #account password match error + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password + '1', Alice.asset_symbol, 100, Alice.did_symbol) + self.assertEqual(ec, 1000, message) + + #aasset symbol can not be empty. + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "", 100, Alice.did_symbol) + self.assertEqual(ec, 5011, message) + + #asset symbol length must be less than 64. + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 65, 100, Alice.did_symbol) + self.assertEqual(ec, 5011, message) + + #asset description length must be less than 64. + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 64, 100, Alice.did_symbol, "x"*65) + self.assertEqual(ec, 5007, message) + + #secondaryissue threshold value error, is must be -1 or in range of 0 to 100. + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 64, 100, Alice.did_symbol, "x" * 64, rate=-2) + self.assertEqual(ec, 5015, message) + + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 64, 100, Alice.did_symbol, "x" * 64, rate=101) + self.assertEqual(ec, 5015, message) + + #asset decimal number must less than 20. + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 64, 100, Alice.did_symbol, "x" * 64, rate=0, decimalnumber=-1) + self.assertEqual(ec, 5002, message) + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 64, 100, Alice.did_symbol, "x" * 64, rate=0, decimalnumber=20) + self.assertEqual(ec, 5002, message) + + #volume must not be zero. + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "x" * 64, 0, Alice.did_symbol, "x" * 64, rate=0, decimalnumber=19) + self.assertEqual(ec, 2003, message) + #contain sensitive words + + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, "JZM.xxx", 10, Alice.did_symbol, "x" * 64, rate=0, decimalnumber=19) + self.assertEqual(ec, 5012, message) + + exist_symbol = self.getExistAssetSymbol() + if exist_symbol: + ec, message = mvs_rpc.create_asset(Alice.name, Alice.password, exist_symbol, 10, Alice.did_symbol, "x" * 64, rate=0, decimalnumber=19) + self.assertEqual(ec, 5009, message) + + def test_2_issue_asset(self): + ten_etp = 10 * (10 ** 8) + # account password match error + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password + '2', Zac.asset_symbol, 1) + self.assertEqual(ec, 1000, message) + + #issue asset fee less than 10 etp + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, Zac.asset_symbol, 9) + self.assertEqual(ec, 5006, message) + + #invalid percentage + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, Zac.asset_symbol, 10, None, 19) + self.assertEqual(ec, 5006, message) + + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, Zac.asset_symbol, 10, None, 101) + self.assertEqual(ec, 5006, message) + + #asset symbol length must be less than 64 + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, "x"*65, ten_etp) + self.assertEqual(ec, 5011, message) + + #asset symbol is already exist in blockchain + exist_symbol = self.getExistAssetSymbol() + if exist_symbol: + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, exist_symbol, ten_etp) + self.assertEqual(ec, 5009, message) + + #asset not in local database + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, Zac.asset_symbol, ten_etp) + self.assertEqual(ec, 5010, message) + + def isDomainExist(domain): + ec, message = mvs_rpc.get_asset() + self.assertEqual(ec, 0, message) + + exist_assets = message + if not exist_assets: + return False + for i in exist_assets: + if i.startswith(domain): + return True + + domain = Alice.domain_symbol + "." + if isDomainExist(domain): + asset_symbol = domain + Bob.asset_symbol + # domain cert not belong to current account + ec, message = mvs_rpc.create_asset(Bob.name, Bob.password, asset_symbol, 100, Bob.did_symbol) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.issue_asset(Bob.name, Bob.password, asset_symbol, ten_etp) + self.assertEqual(ec, 5020, message) + + def test_3_list_assets(self): + # account password match error + ec, message = mvs_rpc.list_assets(Zac.name, Zac.password + '3') + self.assertEqual(ec, 1000, message) + + ec, message = mvs_rpc.list_assets(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.list_assets(cert=True) + self.assertEqual(ec, 0, message) + + def test_4_get_asset(self): + #Illegal asset symbol length. + ec, message = mvs_rpc.get_asset("x"*65) + self.assertEqual(ec, 5011, message) + + #asset not exist + ec, message = mvs_rpc.get_asset("JZM.xxx") + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 0, message) + + #get cert + ec, message = mvs_rpc.get_asset(cert=True) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.get_asset("JZM.xxx", cert=True) + self.assertEqual(ec, 0, message) + + def test_5_deletelocalasset(self): + # account password match error + ec, message = mvs_rpc.delete_localasset(Zac.name, Zac.password + '4', Zac.asset_symbol) + self.assertEqual(ec, 1000, message) + + # asset not exist + ec, message = mvs_rpc.delete_localasset(Zac.name, Zac.password, Zac.asset_symbol) + self.assertEqual(ec, 5003, message) + + # asset not belong to Zac + Frank.create_asset(False) + ec, message = mvs_rpc.delete_localasset(Zac.name, Zac.password, Frank.asset_symbol) + self.assertEqual(ec, 5003, message) + + Bob.create_asset(False) + ec, message = mvs_rpc.delete_localasset(Bob.name, Bob.password, Bob.asset_symbol) + self.assertEqual(ec, 0, message) + + def test_6_getaccountasset(self): + # account password match error + ec, message = mvs_rpc.get_accountasset(Zac.name, Zac.password + '5', Zac.asset_symbol) + self.assertEqual(ec, 1000, message) + + # no asset + ec, message = mvs_rpc.get_accountasset(Zac.name, Zac.password, Zac.asset_symbol) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 0, message) + + ec, message = mvs_rpc.get_accountasset(Bob.name, Bob.password) + self.assertEqual(ec, 0, message) + orign = 0 + if message: + orign = len(message) + + # with +1 asset + Bob.create_asset(False) + ec, message = mvs_rpc.get_accountasset(Bob.name, Bob.password) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), orign+1, message) + + # with +2 assets + ec, message = mvs_rpc.create_asset(Bob.name, Bob.password, Bob.asset_symbol + '1', 100, Bob.did_symbol) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.get_accountasset(Bob.name, Bob.password) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), orign+2, message) + + # asset_symbol sepecified + ec, message = mvs_rpc.get_accountasset(Bob.name, Bob.password, Bob.asset_symbol) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 1, message) + + # --cert specified + ec, message = mvs_rpc.get_accountasset(Zac.name, Zac.password, cert=True) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 0, message) + + def test_7_getaddressasset(self): + # invalid address + ec, message = mvs_rpc.get_addressasset(Zac.mainaddress()+'1') + self.assertEqual(ec, 4010, message) + + # no asset + ec, message = mvs_rpc.get_addressasset(Zac.mainaddress()) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 0, message) + + # --cert specified + ec, message = mvs_rpc.get_addressasset(Zac.mainaddress(), cert=True) + self.assertEqual(ec, 0, message) + + def test_8_sendasset(self): + # account password match error + ec, message = mvs_rpc.send_asset(Zac.name, Zac.password + '6', "", Zac.asset_symbol, 1) + self.assertEqual(ec, 1000, message) + + # invalid to address parameter + ec, message = mvs_rpc.send_asset(Zac.name, Zac.password, "111", Zac.asset_symbol, 1) + self.assertEqual(ec, 4010, message) + + # invalid amount parameter + ec, message = mvs_rpc.send_asset(Zac.name, Zac.password, Zac.mainaddress(), Zac.asset_symbol, 0) + self.assertEqual(ec, 5002, message) + + def test_9_sendassetfrom(self): + # account password match error + ec, message = mvs_rpc.send_asset_from(Zac.name, Zac.password + '6', "", "", Zac.asset_symbol, 1) + self.assertEqual(ec, 1000, message) + + # invalid from address + ec, message = mvs_rpc.send_asset_from(Zac.name, Zac.password, "111", "222", Zac.asset_symbol, 1) + self.assertEqual(ec, 4015, message) + + # invalid to address + ec, message = mvs_rpc.send_asset_from(Zac.name, Zac.password, Zac.mainaddress(), "", Zac.asset_symbol, 1) + self.assertEqual(ec, 4012, message) + + # invalid amount parameter + ec, message = mvs_rpc.send_asset_from(Zac.name, Zac.password, Zac.mainaddress(), Frank.mainaddress(), Zac.asset_symbol, 0) + self.assertEqual(ec, 5002, message) + + def test_A_burn(self): + # account password match error + ec, message = mvs_rpc.burn(Zac.name, Zac.password + '1', Zac.asset_symbol, 100) + self.assertEqual(ec, 1000, message) + diff --git a/test/test-rpc-v3/TestCase/Asset/test_cert.py b/test/test-rpc-v3/TestCase/Asset/test_cert.py new file mode 100644 index 000000000..e07529fca --- /dev/null +++ b/test/test-rpc-v3/TestCase/Asset/test_cert.py @@ -0,0 +1,440 @@ +import time +import MOCs +from utils import common +from TestCase.MVSTestCase import * + +class TestCert(MVSTestCaseBase): + need_mine = False + + def test_0_issuecert(self): + Alice.ensure_balance() + + ''' + Alice create asset and cert + ''' + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + exist_asset = asset_symbol + exist_domain_cert = domain_symbol + + test_cert_symbol = domain_symbol + ".CERT.TO.BOB" + invalid_naming_cert = common.get_random_str() + + + #account password error + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password + '1', Alice.did_symbol, test_cert_symbol, 'naming') + self.assertEqual(ec, 1000, message) + Alice.mining() + + #symbol check + # 1 -- length + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, "X"*65, 'naming') + self.assertEqual(ec, 5011, message) + + # 2 -- invalid char + spec_char_lst = "`~!@#$%^&*()-_=+[{]}\\|;:'\",<>/?" + for char in spec_char_lst: + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, test_cert_symbol + char, 'naming') + self.assertEqual(ec, 1000, message) + self.assertEqual(message, "symbol must be alpha or number or dot", message) + + # check cert symbol -- invalid format + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, invalid_naming_cert, 'naming', + fee=None) + self.assertEqual(ec, 5012, message) + + # did not exist + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol + "d", test_cert_symbol, 'naming') + self.assertEqual(ec, 7006, message) + + # did not owned + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Bob.did_symbol, test_cert_symbol, 'naming') + self.assertEqual(ec, 4003, message) + + # cert type error + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, test_cert_symbol, "naming1") + self.assertEqual(ec, 5017, message) + + # no domain cert owned + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, invalid_naming_cert + ".2BOB2", 'naming') + self.assertEqual(ec, 5019, message) + + # issue cert success + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, test_cert_symbol, "naming") + self.assertEqual(ec, 0, message) + Alice.mining() + + # cert already exist error + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, test_cert_symbol, "naming") + self.assertEqual(ec, 5018, message) + + + def test_1_issuecert_success(self): + ''' + Alice create asset and cert + ''' + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + ''' + Alice issue cert to Bob. + ''' + cert_symbol = Alice.issue_naming_cert(domain_symbol) + Alice.mining() + + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, cert_symbol, + 'naming', + fee=None) + self.assertEqual(ec, 0, message) + Alice.mining() + + ec, message = mvs_rpc.get_accountasset(Bob.name, Bob.password, cert_symbol, True) + self.assertEqual(ec, 0, message) + + expect = {u'owner': Bob.did_symbol, + u'symbol': cert_symbol, + u'cert': u'naming', + u'address': Bob.mainaddress()} + self.assertEqual(len(message), 1, message) + self.assertIn(expect, message) + + # Bob issue asset with naming cert + Alice.send_etp(Bob.mainaddress(), 12 * 10 ** 8); + Alice.mining() + + ec, message = Bob.create_asset_with_symbol(cert_symbol, is_issue=True); + self.assertEqual(ec, 0, message) + Alice.mining() + + # check asset + ec, message = mvs_rpc.get_accountasset(Bob.name, Bob.password, cert_symbol, False) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 1, message) + asset = MOCs.Asset.init(message[0]) + self.assertEqual(asset.issuer, Bob.did_symbol) + self.assertEqual(asset.address, Bob.mainaddress()) + self.assertEqual(asset.status, "unspent") + + def test_2_transfercert(self): + ''' + Alice create asset and cert + ''' + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + naming_cert_symbol = Alice.issue_naming_cert(domain_symbol) + Alice.mining() + + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, naming_cert_symbol, + 'naming', + fee=None) + self.assertEqual(ec, 0, message) + Alice.mining() + ''' + ''' + + not_issued_symbol = domain_symbol+'.2ND'+common.get_random_str() + + # account password match error + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password+'1', Bob.did_symbol, domain_symbol, 'naming', + fee=None) + self.assertEqual(ec, 1000, message) + + # did_symbol not exist + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, "InvalidDID", domain_symbol, 'naming', + fee=None) + self.assertEqual(ec, 7006, message) + + # check cert symbol -- length error + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, "X"*65, + 'naming', + fee=None) + self.assertEqual(ec, 5011, message) + + # check cert symbol -- not issued + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, not_issued_symbol, 'naming', + fee=None) + self.assertEqual(ec, 5019, message) + + # check cert symbol -- owned by some other + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, naming_cert_symbol, + 'naming', + fee=None) + self.assertEqual(ec, 5020, message) + + # check fee + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, domain_symbol, + 'domain', + fee=0) + self.assertEqual(ec, 5005, message) + + + def test_3_transfercert_success(self): + ''' + Alice create asset and cert + ''' + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + naming_cert_symbol = Alice.issue_naming_cert(domain_symbol) + Alice.mining() + + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, Bob.did_symbol, naming_cert_symbol, + 'naming', + fee=None) + self.assertEqual(ec, 0, message) + Alice.mining() + + Alice.send_etp(Bob.mainaddress(), 20 * 10 ** 8) + Alice.mining() + ''' + ''' + + ec, message = mvs_rpc.transfer_cert(Bob.name, Bob.password, Cindy.did_symbol, naming_cert_symbol, 'naming') + self.assertEqual(ec, 0, message) + + Alice.mining() + + ec, message = mvs_rpc.get_accountasset(Cindy.name, Cindy.password, naming_cert_symbol, True) + self.assertEqual(ec, 0, message) + + expect = {u'owner': Cindy.did_symbol, + u'symbol': naming_cert_symbol, + u'cert': u'naming', + u'address': Cindy.mainaddress()} + self.assertEqual(len(message), 1, message) + self.assertEqual(expect, message[0]) + + + def test_4_secondaryissue(self): + # account password match error + ec, message = mvs_rpc.secondary_issue(Alice.name, Alice.password+"1", Alice.did_symbol, Alice.asset_symbol, + volume=100, model=None, fee=None) + self.assertEqual(ec, 1000, message) + Alice.mining() + + # did_symbol not exist + ec, message = mvs_rpc.secondary_issue(Alice.name, Alice.password, Zac.did_symbol, Alice.asset_symbol, + volume=100, model=None, fee=None) + self.assertEqual(ec, 7006, message) + Alice.mining() + + # did_symbol belong to some other + ec, message = mvs_rpc.secondary_issue(Alice.name, Alice.password, Bob.did_symbol, Alice.asset_symbol, + volume=100, model=None, fee=None) + self.assertEqual(ec, 4003, message) + Alice.mining() + + # asset is not issued + ec, message = mvs_rpc.secondary_issue(Alice.name, Alice.password, Alice.did_symbol, Alice.asset_symbol, + volume=100, model=None, fee=None) + self.assertEqual(ec, 5010, message) + Alice.mining() + + # issue the asset + # not allowed to secondary_issue + domain_symbol, asset_symbol = Alice.create_random_asset(secondary=0) + Alice.mining() + + # asset not allowed to secondary_issue + ec, message = mvs_rpc.secondary_issue(Alice.name, Alice.password, Alice.did_symbol, asset_symbol, + volume=100, model=None, fee=None) + self.assertEqual(ec, 5015, message) + + # issue the asset + # asset can be secondary issue freely + domain_symbol, asset_symbol = Alice.create_random_asset(secondary=-1) + Alice.mining() + + # asset belong to some other + ec, message = mvs_rpc.secondary_issue(Bob.name, Bob.password, Bob.did_symbol, asset_symbol, volume=100, model=None, fee=None) + self.assertEqual(ec, 5020, message) + + # fee 0 + ec, message = mvs_rpc.secondary_issue(Alice.name, Alice.password, Alice.did_symbol, asset_symbol, volume=100, model=None, fee=0) + self.assertEqual(ec, 5005, message) + + + def test_5_secondaryissue_success(self): + domain_symbol, asset_symbol = Alice.create_random_asset(secondary=-1) # asset can be secondary issue freely + Alice.mining() + + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + + def test_6_issue_with_attenuation_model(self): + # + # attenuation_model type 1 + # + domain_symbol, asset_symbol = Alice.create_random_asset(is_issue=False, secondary=-1) + Alice.mining() + + # invalid model type + model_type = "invalid" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # invalid LQ + model_type = "TYPE=1;LQ=0;LP=6001;UN=3" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # invalid LP + model_type = "TYPE=1;LQ=9001;LP=0;UN=3" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # invalid UN + model_type = "TYPE=1;LQ=9001;LP=6001;UN=0" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + Alice.mining() + + # invalid model type + model_type = "TYPE=3;LQ=9001;LP=6001;UN=3" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # success + model_type = "TYPE=1;LQ=9001;LP=6001;UN=3" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 0, message) + Alice.mining() + + # + # attenuation_model type 2 + # + domain_symbol, asset_symbol = Alice.create_random_asset(is_issue=False, secondary=-1) + Alice.mining() + + # UC size dismatch UQ size + model_type = "TYPE=2;LQ=9001;LP=6001;UN=3;UC=2000,2000;UQ=3000,3000,3001" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + model_type = "TYPE=2;LQ=9001;LP=6001;UN=3;UC=2000,2000,2001;UQ=3000,3000,3001" + ec, message = Alice.issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 0, message) + Alice.mining() + + + def test_7_secondary_issue_with_attenuation_model(self): + # create asset + domain_symbol, asset_symbol = Alice.create_random_asset(is_issue=True, secondary=-1) + Alice.mining() + + # invalid model parem + model_type = "invalid" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # invalid model type + model_type = "TYPE=4;LQ=9001;LP=6001;UN=3" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # + # attenuation_model type 1 + # + + # invalid LQ + model_type = "TYPE=1;LQ=0;LP=6001;UN=3" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + model_type = "TYPE=1;LQ=9001;LP=6001;UN=3" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 8000) + print(ec) + self.assertEqual(ec, 5016, message) + + # invalid LP + model_type = "TYPE=1;LQ=9001;LP=0;UN=3" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # invalid UN + model_type = "TYPE=1;LQ=9001;LP=6001;UN=0" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + Alice.mining() + + # success + model_type = "TYPE=1;LQ=9001;LP=6001;UN=3" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 0, message) + Alice.mining() + + # + # attenuation_model type 2 + # + + # UN size dismatch UC size + model_type = "TYPE=2;LQ=9001;LP=6001;UN=3;UC=2000,2000;UQ=3000,3000,3001" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # UN size dismatch UQ size + model_type = "TYPE=2;LQ=9001;LP=6001;UN=3;UC=2000,2000,2000;UQ=3000,3000" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # UC size dismatch UQ size + model_type = "TYPE=2;LQ=9001;LP=6001;UN=3;UC=2000,2000;UQ=3000,3000,3001" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # success + model_type = "TYPE=2;LQ=9001;LP=6001;UN=3;UC=2000,2000,2001;UQ=3000,3000,3001" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 0, message) + Alice.mining() + + # + # attenuation_model type 3 + # + + # invalid LQ + model_type = "TYPE=3;LQ=0;LP=6001;UN=3;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type) + self.assertEqual(ec, 5016, message) + + # invalid LP + model_type = "TYPE=3;LQ=9001;LP=0;UN=3;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 9001) + self.assertEqual(ec, 5016, message) + + # invalid UN + model_type = "TYPE=3;LQ=9001;LP=6001;UN=0;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 9001) + self.assertEqual(ec, 5016, message) + + model_type = "TYPE=3;LQ=9001;LP=6001;UN=101;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 9001) + self.assertEqual(ec, 5016, message) + + # invalid IR + model_type = "TYPE=3;LQ=9001;LP=6001;UN=0;IR=0" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 9001) + self.assertEqual(ec, 5016, message) + + model_type = "TYPE=3;LQ=9001;LP=6001;UN=3;IR=100001" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 9001) + self.assertEqual(ec, 5016, message) + + # LQ size dismatch total size + model_type = "TYPE=3;LQ=9001;LP=6001;UN=3;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 10000) + self.assertEqual(ec, 5016, message) + + model_type = "TYPE=3;LQ=9001;LP=6001;UN=3;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 8000) + self.assertEqual(ec, 5016, message) + + # success + model_type = "TYPE=3;LQ=9001;LP=6001;UN=3;IR=8" + ec, message = Alice.secondary_issue_asset_with_symbol(asset_symbol, model_type, 9001) + self.assertEqual(ec, 0, message) + Alice.mining() diff --git a/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py b/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py new file mode 100644 index 000000000..67ca159dd --- /dev/null +++ b/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py @@ -0,0 +1,64 @@ +import time +from utils import common +from TestCase.MVSTestCase import * + +class TestTransferMultisigCert(MVSTestCaseBase): + + def test_0_transfer_multisig_cert(self): + # Alice create asset + domain_symbol, asset_symbol = Alice.create_random_asset(secondary=-1) + Alice.mining() + + # create multisig + # + description = "Alice & Zac's multi-sig address" + + multisig_address = Alice.new_multisigaddress(description, [Zac], 2) + multisig_address2 = Zac.new_multisigaddress(description, [Alice], 2) + self.assertEqual(multisig_address, multisig_address2, "multisig addresses dismatch.") + + # send etp to multisig_address + # + result, message = mvs_rpc.sendfrom(Alice.name, Alice.password, + Alice.mainaddress(), multisig_address, 3 * 10 ** 8) + assert (result == 0) + Alice.mining() + + # register did to multisig_address + # + multisig_did_symbol = "Multisig" + common.get_random_str() + ec, tx = mvs_rpc.register_did(Alice.name, Alice.password, multisig_address, multisig_did_symbol) + self.assertEqual(ec, code.success, tx) + + # sign multisig rawtx + ec, tx2 = mvs_rpc.sign_multisigtx(Zac.name, Zac.password, tx, True) + self.assertEqual(ec, 0, tx2) + Alice.mining() + + # transfer cert to multisig_did_symbol + # + ec, message = mvs_rpc.transfer_cert(Alice.name, Alice.password, multisig_did_symbol, asset_symbol, 'issue') + self.assertEqual(ec, code.success, message) + Alice.mining() + + # check cert + certs = Zac.get_addressasset(multisig_address, True); + self.assertGreater(len(certs), 0, "not cert found at " + multisig_address) + exist_symbols = filter(lambda a: a.symbol == asset_symbol and a.cert == "issue", certs) + self.assertEqual(len(exist_symbols), 1, "not cert found at " + multisig_address) + + # transfer cert to Cindy + # + ec, tx = mvs_rpc.transfer_cert(Alice.name, Alice.password, Cindy.did_symbol, asset_symbol, 'issue') + self.assertEqual(ec, code.success, tx) + + # sign multisig rawtx + ec, tx2 = mvs_rpc.sign_multisigtx(Zac.name, Zac.password, tx, True) + self.assertEqual(ec, 0, tx2) + Alice.mining() + + # check cert + certs = Cindy.get_addressasset(Cindy.didaddress(), True); + self.assertGreater(len(certs), 0, "not cert found at " + Cindy.didaddress()) + exist_symbols = filter(lambda a: a.symbol == asset_symbol and a.cert == "issue", certs) + self.assertEqual(len(exist_symbols), 1, "not cert found at " + Cindy.didaddress()) diff --git a/test/test-rpc-v3/TestCase/Block/__init__.py b/test/test-rpc-v3/TestCase/Block/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/Block/test_block.py b/test/test-rpc-v3/TestCase/Block/test_block.py new file mode 100644 index 000000000..ff52e5021 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Block/test_block.py @@ -0,0 +1,53 @@ +from utils import common +from TestCase.MVSTestCase import * + +class TestBlock(MVSTestCaseBase): + need_mine = False + roles = () + def test_0_getblock(self): + ec, message = mvs_rpc.get_blockheader() + self.assertEqual(ec, 0, message) + + hash = message["hash"] + height = message["number"] + + ec, message_by_hash = mvs_rpc.get_block(hash) + self.assertEqual(ec, 0, message) + + ec, message_by_height = mvs_rpc.get_block(height) + self.assertEqual(ec, 0, message) + + self.assertEqual(message_by_hash, message_by_height) + + # height error + ec, message_by_height = mvs_rpc.get_block(height+1) + self.assertEqual(ec, 5101, message) + + def test_1_getblockheader(self): + ec, message = mvs_rpc.get_blockheader() + self.assertEqual(ec, 0, message) + + # check keys + expect_keys = ["bits", "hash", "merkle_tree_hash", "mixhash", "nonce", "number", "previous_block_hash", "timestamp", "transaction_count", "version"] + self.checkResponseKeys(message, expect_keys) + + hash = message["hash"] + height = message["number"] + + # by hash + ec, message_by_hash = mvs_rpc.get_blockheader(hash=hash) + self.assertEqual(ec, 0) + self.assertEqual(message, message_by_hash) + + # by height + ec, message_by_height = mvs_rpc.get_blockheader(height=height) + self.assertEqual(ec, 0) + self.assertEqual(message, message_by_height) + + # check previous_block_hash + previous_block_hash = message["previous_block_hash"] + ec, previous_block = mvs_rpc.get_blockheader(hash=previous_block_hash) + self.assertEqual(ec, 0) + self.assertEqual(previous_block["number"], height-1) + + diff --git a/test/test-rpc-v3/TestCase/Blockchain/__init__.py b/test/test-rpc-v3/TestCase/Blockchain/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/Blockchain/test_work.py b/test/test-rpc-v3/TestCase/Blockchain/test_work.py new file mode 100644 index 000000000..fb4b8e6e4 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Blockchain/test_work.py @@ -0,0 +1,21 @@ +''' +dependency: pyethereum + +sudo apt-get install libssl-dev build-essential automake pkg-config libtool libffi-dev libgmp-dev libyaml-cpp-dev +git clone https://github.com/ethereum/pyethereum/ +cd pyethereum +python setup.py install + +bugfix 2018-05-02: +sudo pip install rlp=0.6.0 // the latest rlp does not work! +''' +from TestCase.MVSTestCase import * + +class TestWork(MVSTestCaseBase): + need_mine = False + def test_1_no_mining_account(self): + _, prev = mvs_rpc.getblockheader() + round_to_mine = 1 + Alice.mining(round_to_mine) + _, curr = mvs_rpc.getblockheader() + self.assertEqual(prev[1] + round_to_mine, curr[1]) diff --git a/test/test-rpc-v3/TestCase/ETP/__init__.py b/test/test-rpc-v3/TestCase/ETP/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/ETP/batch_etp.py b/test/test-rpc-v3/TestCase/ETP/batch_etp.py new file mode 100644 index 000000000..961049725 --- /dev/null +++ b/test/test-rpc-v3/TestCase/ETP/batch_etp.py @@ -0,0 +1,30 @@ +from TestCase.MVSTestCase import * +import time +import datetime + + +class TestSendETP(MVSTestCaseBase): + def test_0_send(self): + + before = time.clock() + print "start send transaction:" + + count = 20000 + while count > 0: + ec, message = mvs_rpc.send(Alice.name, Alice.password, Zac.mainaddress(), 10000, 10000, 'transaction no:'+str(count)) + if ec == 0: + count -=1 + + if count % 100 == 0: + nowTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print "current send transaction num:%d, time:%s" %(count,nowTime) + + + while True: + Alice.mining(1) + ec, message = mvs_rpc.get_memorypool() + if ec == 0 and message["transcation"] == "null": + break + + print "end send transaction, Elapsed time:",time.clock()-before + diff --git a/test/test-rpc-v3/TestCase/ETP/test_etp.py b/test/test-rpc-v3/TestCase/ETP/test_etp.py new file mode 100644 index 000000000..0930f0a46 --- /dev/null +++ b/test/test-rpc-v3/TestCase/ETP/test_etp.py @@ -0,0 +1,237 @@ +from TestCase.MVSTestCase import * +import MOCs + +class TestSendETP(MVSTestCaseBase): + def test_0_send(self): + ''' + Alice send etp to Zac + ''' + specific_amout = 123456 + specific_fee=12345 + specific_desc="test_0_send" + ec, message = mvs_rpc.send(Alice.name, Alice.password, Zac.mainaddress(), specific_amout, fee=specific_fee, desc=specific_desc) + self.assertEqual(ec, 0, message) + + Alice.mining() + hash = message["hash"] + ec, message = mvs_rpc.gettx(hash) + self.assertEqual(ec, 0, message) + + tx = MOCs.Transaction.from_json(message) + + self.assertEqual(tx.hash, hash) + self.assertEqual(tx.version, '4') + self.assertNotEqual(len(tx.inputs), 0) + + sum_payment = 0 + for i in tx.inputs: + ec, message = mvs_rpc.gettx(i.previous_output.hash) + self.assertEqual(ec, 0, message) + prev_tx = MOCs.Transaction.from_json(message) + o = prev_tx.outputs[i.previous_output.index] + self.assertEqual(o.index, i.previous_output.index) + self.assertEqual(o.attachment.type, 'etp') + self.assertIn(o.address, Alice.addresslist) + sum_payment += o.value + + self.assertEqual(tx.outputs[0].index, 0) + self.assertEqual(tx.outputs[0].address, Zac.mainaddress()) + self.assertEqual(tx.outputs[0].attachment.type, 'etp') + self.assertEqual(tx.outputs[0].value, specific_amout) + + self.assertEqual(tx.outputs[1].index, 1) + self.assertEqual(tx.outputs[1].address, Zac.mainaddress()) + self.assertEqual(tx.outputs[1].attachment.type, 'message') + self.assertEqual(tx.outputs[1].attachment.content, specific_desc) + self.assertEqual(tx.outputs[1].value, 0) + + if sum_payment > (specific_amout + specific_fee): + self.assertEqual(tx.outputs[2].index, 2) + self.assertEqual(tx.outputs[2].value, sum_payment - (specific_amout + specific_fee)) + self.assertIn(tx.outputs[2].address, Alice.addresslist) + self.assertEqual(tx.outputs[2].attachment.type, 'etp') + + def test_1_sendfrom(self): + ''' + Alice send etp to Zac + ''' + specific_amout = 123321 + specific_fee = 12321 + specific_desc = "test_1_sendfrom" + + ec, message = mvs_rpc.sendfrom(Alice.name, Alice.password, Alice.mainaddress(), Zac.mainaddress(), specific_amout, fee=specific_fee, + desc=specific_desc) + self.assertEqual(ec, 0, message) + Alice.mining() + hash = message["hash"] + ec, message = mvs_rpc.gettx(hash) + self.assertEqual(ec, 0, message) + + tx = MOCs.Transaction.from_json(message) + + self.assertEqual(tx.hash, hash) + self.assertEqual(tx.version, '4') + self.assertNotEqual(len(tx.inputs), 0) + + sum_payment = 0 + for i in tx.inputs: + ec, message = mvs_rpc.gettx(i.previous_output.hash) + self.assertEqual(ec, 0, message) + prev_tx = MOCs.Transaction.from_json(message) + o = prev_tx.outputs[i.previous_output.index] + self.assertEqual(o.index, i.previous_output.index) + self.assertEqual(o.attachment.type, 'etp') + self.assertEqual(o.address, Alice.mainaddress()) + sum_payment += o.value + + self.assertEqual(tx.outputs[0].index, 0) + self.assertEqual(tx.outputs[0].address, Zac.mainaddress()) + self.assertEqual(tx.outputs[0].attachment.type, 'etp') + self.assertEqual(tx.outputs[0].value, specific_amout) + + self.assertEqual(tx.outputs[1].index, 1) + self.assertEqual(tx.outputs[1].address, Zac.mainaddress()) + self.assertEqual(tx.outputs[1].attachment.type, 'message') + self.assertEqual(tx.outputs[1].attachment.content, specific_desc) + self.assertEqual(tx.outputs[1].value, 0) + + if sum_payment > (specific_amout + specific_fee): + self.assertEqual(tx.outputs[2].index, 2) + self.assertEqual(tx.outputs[2].value, sum_payment - (specific_amout + specific_fee)) + self.assertEqual(tx.outputs[2].address, Alice.mainaddress()) + self.assertEqual(tx.outputs[2].attachment.type, 'etp') + + def test_2_sendmore(self): + ''' + Alice send etp to Zac's multi address + ''' + receivers = { + Zac.addresslist[0]: 100000, + Zac.addresslist[1]: 100001, + Zac.addresslist[2]: 100002, + Zac.addresslist[3]: 100003, + Zac.addresslist[4]: 100004, + Zac.addresslist[5]: 100005, + } + specific_fee = 12421 + ec, message = mvs_rpc.sendmore(Alice.name, Alice.password, receivers, Alice.addresslist[1], specific_fee) + self.assertEqual(ec, 0, message) + Alice.mining() + hash = message["hash"] + ec, message = mvs_rpc.gettx(hash) + self.assertEqual(ec, 0, message) + + tx = MOCs.Transaction.from_json(message) + + self.assertEqual(tx.hash, hash) + self.assertEqual(tx.version, '4') + self.assertNotEqual(len(tx.inputs), 0) + + sum_payment = 0 + for i in tx.inputs: + ec, message = mvs_rpc.gettx(i.previous_output.hash) + self.assertEqual(ec, 0, message) + prev_tx = MOCs.Transaction.from_json(message) + o = prev_tx.outputs[i.previous_output.index] + self.assertEqual(o.index, i.previous_output.index) + self.assertEqual(o.attachment.type, 'etp') + self.assertIn(o.address, Alice.addresslist) + sum_payment += o.value + + max_output = len(receivers) + addr2value = {} + for i in range(max_output): + self.assertEqual(tx.outputs[i].index, i) + #self.assertEqual(tx.outputs[i].address, Zac.addresslist[i]) + self.assertEqual(tx.outputs[i].attachment.type, 'etp') + #self.assertEqual(tx.outputs[i].value, receivers[Zac.addresslist[i]]) + addr2value[tx.outputs[i].address] = tx.outputs[i].value + self.assertEqual(receivers, addr2value, str(addr2value)) + + total_out = sum([receivers[i] for i in receivers]) + if sum_payment > total_out: + self.assertEqual(tx.outputs[ max_output ].index, max_output) + self.assertEqual(tx.outputs[ max_output ].value, sum_payment - (total_out + specific_fee)) + self.assertEqual(tx.outputs[ max_output ].address, Alice.addresslist[1]) + self.assertEqual(tx.outputs[ max_output ].attachment.type, 'etp') + + + def test_3_deposit(self): + # account not found or incorrect password + ec, message = mvs_rpc.deposit(Alice.name, Alice.password+'1', 10**8) + self.assertEqual(ec, 1000, message) + + # amazing! the amount 0 is valid for deposit + ec, message = mvs_rpc.deposit(Alice.name, Alice.password, 0) + self.assertEqual(ec, 0, message) + Alice.mining() + + # invalid address! + invalid_address = common.gen_invalid_address( Alice.addresslist[1] ) + ec, message = mvs_rpc.deposit(Alice.name, Alice.password, 10**8, address=invalid_address) + self.assertEqual(ec, 4010, message) + + # invalid deposit + deposits = [7, 30, 90, 182, 365] + for deposit in deposits: + for i in [-1, 1]: + ec, message = mvs_rpc.deposit(Alice.name, Alice.password, 10 ** 8, deposit=deposit+i) + self.assertEqual(ec, 3301, message) + + # invalid fee + for fee in [10**4 -1, 0, 100 * (10**8)+1]: + ec, message = mvs_rpc.deposit(Alice.name, Alice.password, 10 ** 8, fee=fee) + self.assertEqual(ec, 5005, message) + + address = Alice.addresslist[1] + ec, message = mvs_rpc.deposit(Alice.name, Alice.password, 10 ** 8, address=address) + self.assertEqual(ec, 0, message) + Alice.mining() + + address = Zac.addresslist[1] + ec, message = mvs_rpc.deposit(Alice.name, Alice.password, 10 ** 8, address=address) + self.assertEqual(ec, 0, message) + Alice.mining() + ec, message = mvs_rpc.list_balances(Zac.name, Zac.password) + self.assertEqual(ec, 0, message) + + balance =filter(lambda x: x["address"] == address, message) + self.assertEqual(len(balance), 1) + self.assertEqual(balance[0], { + "address": address, + "available": 0, + "confirmed": 100095890, + "frozen": 100095890, + "received": 100095890, + "unspent": 100095890 + }) + + # not enough balance + ec, message = mvs_rpc.deposit(Zac.name, Zac.password, 10 ** 8, address=address) + self.assertEqual(ec, 5302, message) + + # deposit again , this time we deposit from, to address belong to the same account + Alice.send_etp(Zac.mainaddress(), 10 ** 8 + 10**4) + Alice.mining() + + address = Zac.addresslist[2] + ec, message = mvs_rpc.deposit(Zac.name, Zac.password, 10 ** 8, address=address) + self.assertEqual(ec, 0, message) + Alice.mining() + ec, message = mvs_rpc.list_balances(Zac.name, Zac.password) + self.assertEqual(ec, 0, message) + + balance = filter(lambda x: x["address"] == address, message) + self.assertEqual(len(balance), 1) + self.assertEqual(balance[0], { + "address": address, + "available": 0, + "confirmed": 100095890, + "frozen": 100095890, + "received": 100095890, + "unspent": 100095890 + }) + + # get_balance will not cause coredump + ec, message = mvs_rpc.get_balance(Zac.name, Zac.password) + self.assertEqual(ec, 0, message) diff --git a/test/test-rpc-v3/TestCase/ETP/test_etp_boundary.py b/test/test-rpc-v3/TestCase/ETP/test_etp_boundary.py new file mode 100644 index 000000000..5695a0484 --- /dev/null +++ b/test/test-rpc-v3/TestCase/ETP/test_etp_boundary.py @@ -0,0 +1,49 @@ +from TestCase.MVSTestCase import * + +class TestSendETP(MVSTestCaseBase): + def test_0_send(self): + #account password mismatch + ec, message = mvs_rpc.send(Alice.name, Alice.password+"1", Zac.mainaddress(), 1) + self.assertEqual(ec, 1000, message) + + # check to_address + ec, message = mvs_rpc.send(Alice.name, Alice.password, Zac.mainaddress()+'1', 1) + self.assertEqual(ec, 4010, message) + + # not enough balance + ec, message = mvs_rpc.send(Zac.name, Zac.password, Alice.mainaddress(), 1) + self.assertEqual(ec, 5302, message) + + def test_1_sendfrom(self): + # account password mismatch + ec, message = mvs_rpc.sendfrom(Alice.name, Alice.password + "1", Alice.mainaddress(), Zac.mainaddress(), 1) + self.assertEqual(ec, 1000, message) + + # check from_address + ec, message = mvs_rpc.sendfrom(Alice.name, Alice.password, Alice.mainaddress()+ "1", Zac.mainaddress(), 1) + self.assertEqual(ec, 4015, message) + + # check to_address + ec, message = mvs_rpc.sendfrom(Alice.name, Alice.password, Alice.mainaddress(), Zac.mainaddress() + "1", 1) + self.assertEqual(ec, 4012, message) + + # not enough balance + ec, message = mvs_rpc.send(Zac.name, Zac.password, Alice.mainaddress(), 1) + self.assertEqual(ec, 5302, message) + + def test_2_sendmore(self): + # account password mismatch + ec, message = mvs_rpc.sendmore(Zac.name, Zac.password + "1", {Alice.mainaddress():10000, Bob.mainaddress():20000}) + self.assertEqual(ec, 1000, message) + + #validate my change address + ec, message = mvs_rpc.sendmore(Zac.name, Zac.password, {Alice.mainaddress(): 10000, Bob.mainaddress(): 20000}, Zac.mainaddress() + '1') + self.assertEqual(ec, 4012, message) + + #my change address does not belong to me + #ec, message = mvs_rpc.sendmore(Zac.name, Zac.password, {Alice.mainaddress(): 10000, Bob.mainaddress(): 20000}, Alice.mainaddress()) + #self.assertEqual(ec, 4012, message) + + #not enough balance + ec, message = mvs_rpc.sendmore(Zac.name, Zac.password, {Alice.mainaddress(): 10000, Bob.mainaddress(): 20000}, Zac.mainaddress()) + self.assertEqual(ec, 5302, message) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/Identity/__init__.py b/test/test-rpc-v3/TestCase/Identity/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Identity/__init__.py @@ -0,0 +1 @@ + diff --git a/test/test-rpc-v3/TestCase/Identity/batch_did.py b/test/test-rpc-v3/TestCase/Identity/batch_did.py new file mode 100644 index 000000000..de0f94c88 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Identity/batch_did.py @@ -0,0 +1,64 @@ +from utils import common +from TestCase.MVSTestCase import * + +class TestDIDBatch(MVSTestCaseBase): + def test_0_registerdid_batch(self): + # create 5000 account + # send 1 etp to each account + # issue a did for each account + now = common.get_timestamp() + + def get_account_name(i, j): + return "Account_%s_%s" % (i, j) + def get_did_symbol(i, j): + return "tempdid_%s_%s.%s" % (i, j, now) + + lastwords = [] + addresses = [] + + batch_amount_i, batch_amount_j = 200, 10 # total 2000 + + def get_lastword(i, j): + return lastwords[i * batch_amount_j + j] + + def get_address(i, j): + return addresses[i * batch_amount_j + j] + + def get_did_count(): + ec, message = mvs_rpc.list_dids() + self.assertEqual(ec, 0) + return len(message) + + try: + for i in xrange(batch_amount_i): + receivers = {} + for j in xrange(batch_amount_j): + ec, (lastword, address) = mvs_rpc.easy_new_account(get_account_name(i, j), "123456") + self.assertEqual(ec, 0) + lastwords.append(lastword) + addresses.append( address ) + + receivers[address] = 10**8 # 1 etp for each + Alice.sendmore_etp(receivers) + Alice.mining() + + previous = get_did_count() + for i in xrange(batch_amount_i): + for j in xrange(batch_amount_j): + ec, message = mvs_rpc.register_did(get_account_name(i, j), "123456", get_address(i, j), get_did_symbol(i, j)) + self.assertEqual(ec, 0) + + current = 0 + mine_round = 0 + while current < previous + batch_amount_i*batch_amount_j: + Alice.mining() + current = get_did_count() + mine_round += 1 + + self.assertEqual(mine_round, 2) + + finally: + for i in xrange(batch_amount_i): + for j in xrange(batch_amount_j): + ec, message = mvs_rpc.delete_account(get_account_name(i, j), "123456", get_lastword(i, j)) + self.assertEqual(ec, 0, message) diff --git a/test/test-rpc-v3/TestCase/Identity/fork_did.py b/test/test-rpc-v3/TestCase/Identity/fork_did.py new file mode 100644 index 000000000..c4fe6dc6f --- /dev/null +++ b/test/test-rpc-v3/TestCase/Identity/fork_did.py @@ -0,0 +1,321 @@ +''' +test registerdid/modifydid when fork occured +''' +from utils import common +from TestCase.MVSTestCase import * + +class TestFork(ForkTestCase): + def test_0_fork_at_send(self): + self.make_partion() + # make transaction and mine + Alice.send_etp(Bob.mainaddress(), 10**8) + Alice.mining() + + self.fork() + + # check if Alice & Bob's address_did record is cleaned + for role in [Alice, Bob]: + ec, message = mvs_rpc.list_dids(role.name, role.password) + self.assertEqual(ec, 0, message) + + self.assertIn({'status':"registered", + 'address':role.mainaddress(), + 'symbol':role.did_symbol}, message) + + def test_1_fork_at_registerdid(self): + self.make_partion() + try: + target_address = Alice.addresslist[-1] + # registerdid and mine + did_symbol = "YouShouldNotSeeThis.Avatar" + Alice.send_etp(target_address, 10 ** 8) + Alice.mining() + + ec, message = Alice.register_did(target_address, did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + ec, message = mvs_rpc.list_dids(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + + self.assertIn({'status': "registered", + 'address': target_address, + 'symbol': did_symbol}, message) + finally: + self.fork() + + ec, message = mvs_rpc.list_dids(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + self.assertNotIn({'status': "registered", + 'address': target_address, + 'symbol': Alice.did_symbol}, message) + + def test_2_fork_at_issueasset(self): + self.make_partion() + + asset_symbol = None + domain_symbol = None + try: + domain = (u'Not1Exist' + common.get_random_str()).upper() + domain_symbol, asset_symbol = Alice.create_random_asset(domain_symbol=domain) + Alice.mining() + + # check asset + ec, message = mvs_rpc.get_asset( ) + self.assertEqual(ec, 0, message) + self.assertIn(asset_symbol, message) + + addressassets = Alice.get_addressasset(Alice.mainaddress()) + addressasset = filter(lambda a: a.symbol == asset_symbol, addressassets) + self.assertEqual(len(addressasset), 1) + + # check domain cert + certs = Alice.get_addressasset(Alice.mainaddress(), True) + cert = filter(lambda a: a.symbol == domain_symbol, certs) + self.assertEqual(len(cert), 1) + + finally: + self.fork() + + # check asset + ec, message = mvs_rpc.get_asset( ) + self.assertEqual(ec, 0, message) + self.assertNotIn(asset_symbol, message) + + addressassets = Alice.get_addressasset(Alice.mainaddress()) + addressasset = filter(lambda a: a.symbol == asset_symbol, addressassets) + self.assertEqual(len(addressasset), 0) + + # check domain cert + certs = Alice.get_addressasset(Alice.mainaddress(), True) + cert = filter(lambda a: a.symbol == domain_symbol, certs) + self.assertEqual(len(cert), 0) + + def test_3_fork_at_change_did(self): + self.make_partion() + try: + target_addr = Cindy.addresslist[-1] + Alice.send_etp(target_addr, 10**8) + Alice.mining() + + ec, message = mvs_rpc.change_did(Cindy.name, Cindy.password, target_addr, Cindy.did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + expect = { + u'status': u'registered', + u'symbol': Cindy.did_symbol, + u'address': target_addr + } + + ec, message = mvs_rpc.list_dids(Cindy.name, Cindy.password) + self.assertEqual(ec, 0, message) + self.assertIn(expect, message) + + ec, message = mvs_rpc.list_dids() + self.assertEqual(ec, 0, message) + self.assertIn(expect, message) + + + target_addr = Cindy.addresslist[-2] + Alice.send_etp(target_addr, 10**8) + Alice.mining() + + ec, message = mvs_rpc.change_did(Cindy.name, Cindy.password, target_addr, Cindy.did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + expect = { + u'status': u'registered', + u'symbol': Cindy.did_symbol, + u'address': target_addr + } + + ec, message = mvs_rpc.list_dids(Cindy.name, Cindy.password) + self.assertEqual(ec, 0, message) + self.assertIn(expect, message) + + + ec, message = mvs_rpc.list_dids() + self.assertEqual(ec, 0, message) + self.assertIn(expect, message) + + finally: + self.fork() + + expect = { + u'status': u'registered', + u'symbol': Cindy.did_symbol, + u'address': Cindy.mainaddress() + } + + ec, message = mvs_rpc.list_dids(Cindy.name, Cindy.password) + self.assertEqual(ec, 0, message) + self.assertIn(expect, message) + + ec, message = mvs_rpc.list_dids() + self.assertEqual(ec, 0, message) + self.assertIn(expect, message) + + def test_4_fork_at_issuecert(self): + self.make_partion() + + cert_symbol = None + try: + domain = (u'Not2Exist' + common.get_random_str()).upper() + domain_symbol, asset_symbol = Alice.create_random_asset(domain_symbol=domain) + Alice.mining() + + cert_symbol = (domain_symbol + ".NAMING").upper(); + ec, message = mvs_rpc.issue_cert(Alice.name, Alice.password, Alice.did_symbol, cert_symbol, "NAMING") + self.assertEqual(ec, 0, message) + Alice.mining() + + # check naming cert + certs = Alice.get_addressasset(Alice.didaddress(), True) + + + cert = filter(lambda a: a.symbol == cert_symbol, certs) + self.assertEqual(len(cert), 1) + + finally: + self.fork() + + # check cert + certs = Alice.get_addressasset(Alice.didaddress(), True) + cert = filter(lambda a: a.symbol == cert_symbol, certs) + self.assertEqual(len(cert), 0) + + + + def test_5_fork_at_did(self): + # + did_symbol = "test_fork_registerdiid"+common.get_random_str() + rmtName = Zac.name+common.get_random_str() + print rmtName + mvs_rpc.new_address(Zac.name,Zac.password, 2) + mvs_rpc.remote_call(self.remote_ip, mvs_rpc.import_account)(rmtName, "123456", ' '.join(Zac.mnemonic),2) + receivers={} + receivers[Zac.addresslist[0]] = (9**10) + receivers[Zac.addresslist[1]] = (9**10) + Alice.sendmore_etp(receivers) + Alice.mining() + + # asset + domain_symbol = ("Zacfork" + common.get_random_str()).upper() + asset_symbol = domain_symbol + ".AST" + + # mit + mit_symbol = ("MIT." + common.get_random_str()).upper() + + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + pre_height = message[0] + print "pre_height:"+str(pre_height) + + self.make_partion() + import time + try: + # fork chain + Alice.mining() + ec, message = Zac.register_did(Zac.addresslist[0], did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + Zac.create_asset_with_symbol(asset_symbol, True, 0, did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + Zac.send_asset(Zac.addresslist[1], 100 ,asset_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + + mvs_rpc.transfer_cert(Zac.name, Zac.password, Alice.did_symbol, domain_symbol, "DOMAIN") + self.assertEqual(ec, 0, message) + Alice.mining() + + mvs_rpc.transfer_cert(Alice.name, Alice.password, "BLACKHOLE", domain_symbol, "DOMAIN") + self.assertEqual(ec, 0, message) + Alice.mining() + + ec, message = mvs_rpc.register_mit(Zac.name, Zac.password, did_symbol, mit_symbol, "test fork mit") + self.assertEqual(ec, code.success, message) + Alice.mining(5) + + ec, message = mvs_rpc.transfer_mit(Zac.name, Zac.password, "BLACKHOLE", mit_symbol) + self.assertEqual(ec, code.success, message) + Alice.mining(5) + + finally: + # main chain + self.remote_ming(2) + ec, message = mvs_rpc.remote_call(self.remote_ip,mvs_rpc.register_did)(rmtName, "123456", Zac.addresslist[1],did_symbol) + self.assertEqual(ec, 0, message) + self.remote_ming(1) + + ec, message = mvs_rpc.remote_call(self.remote_ip,mvs_rpc.change_did)(rmtName, "123456", Zac.addresslist[0], did_symbol) + self.assertEqual(ec, 0, message) + self.remote_ming(1) + + ec, message = self.create_asset_with_symbol_rmt(rmtName, "123456", asset_symbol, True, 0, did_symbol) + self.assertEqual(ec, 0, message) + self.remote_ming(1) + + ec, message = mvs_rpc.remote_call(self.remote_ip, mvs_rpc.send_asset)(rmtName, "123456",Zac.addresslist[1], asset_symbol,100) + self.assertEqual(ec, 0, message) + self.remote_ming(1) + + ec, message = mvs_rpc.remote_call(self.remote_ip, mvs_rpc.transfer_cert)(rmtName, "123456", "BLACKHOLE", domain_symbol, "DOMAIN") + self.assertEqual(ec, 0, message) + self.remote_ming(1) + + ec, message = mvs_rpc.remote_call(self.remote_ip, mvs_rpc.register_mit)(rmtName, "123456", did_symbol, mit_symbol) + self.assertEqual(ec, code.success, message) + self.remote_ming(1) + + ec, message = mvs_rpc.remote_call(self.remote_ip,mvs_rpc.transfer_mit)(rmtName, "123456", "BLACKHOLE", mit_symbol) + self.assertEqual(ec, code.success, message) + self.remote_ming(20) + + + + + + + ec, message = mvs_rpc.add_node( self.remote_ip+':5251') + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.remote_call(self.remote_ip, mvs_rpc.get_info)() + self.assertEqual(ec, 0, message) + main_height = message[0] + + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + fork_height = message[0] + while fork_height < main_height: + time.sleep(1) + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + fork_height = message[0] + print "fork_height:"+str(fork_height) + ",main_height:"+str(main_height) + + ec, message = mvs_rpc.list_didaddresses(did_symbol) + self.assertEqual(ec, 0, message) + self.assertEqual(message[0]["address"], Zac.addresslist[0], message) + + + def create_asset_with_symbol_rmt(self, name, password, symbol, is_issue, secondary, did_symbol): + result, message = mvs_rpc.remote_call(self.remote_ip, mvs_rpc.create_asset)(name, password, symbol, 300000, did_symbol, description="%s's Asset" % name, rate=secondary) + if (result): + print("#ERROR#: failed to create asset: {}".format(message)) + assert (result == 0) + if is_issue: + result, message = mvs_rpc.remote_call(self.remote_ip,mvs_rpc.issue_asset)(name, password, symbol) + if (result): + print("#ERROR#: failed to issue asset: {}".format(message)) + assert (result == 0) + return result, message diff --git a/test/test-rpc-v3/TestCase/Identity/test_did.py b/test/test-rpc-v3/TestCase/Identity/test_did.py new file mode 100644 index 000000000..ceb8a0a2a --- /dev/null +++ b/test/test-rpc-v3/TestCase/Identity/test_did.py @@ -0,0 +1,281 @@ +import MOCs +from utils import validate + +from TestCase.MVSTestCase import * + +class TestDIDSend(MVSTestCaseBase): + @classmethod + def setUpClass(cls): + #check if the did are created. + ec, message = mvs_rpc.list_dids() + if ec != 0: + return + exist_symbols = [i["symbol"] for i in message] + + assert (Alice.did_symbol in exist_symbols) + assert (Bob.did_symbol in exist_symbols) + + def test_0_didsend_etp(self): + #send to did + tx_hash = Alice.didsend_etp(Bob.did_symbol, 12345) + Alice.mining() + validate.validate_tx(self, tx_hash, Alice, Bob, 12345, 10**4) + + #send to address + tx_hash = Alice.didsend_etp(Zac.mainaddress(), 54321) + Alice.mining() + validate.validate_tx(self, tx_hash, Alice, Zac, 54321, 10 ** 4) + + def test_1_didsend_etp_from(self): + # did -> did + tx_hash = Alice.didsend_etp_from(Alice.did_symbol, Bob.did_symbol, 12345) + Alice.mining() + validate.validate_tx(self, tx_hash, Alice, Bob, 12345, 10 ** 4) + + # did -> addr + tx_hash = Alice.didsend_etp_from(Alice.did_symbol, Zac.mainaddress(), 54321) + Alice.mining() + validate.validate_tx(self, tx_hash, Alice, Zac, 54321, 10 ** 4) + + # addr -> did + tx_hash = Alice.didsend_etp_from(Alice.mainaddress(), Bob.did_symbol, 56789) + Alice.mining() + validate.validate_tx(self, tx_hash, Alice, Bob, 56789, 10 ** 4) + + # addr -> addr + tx_hash = Alice.didsend_etp_from(Alice.mainaddress(), Bob.mainaddress(), 98765) + Alice.mining() + validate.validate_tx(self, tx_hash, Alice, Bob, 98765, 10 ** 4) + +class TestDIDSendMore(MVSTestCaseBase): + def test_0_didsend_more(self): + receivers = { + Bob.mainaddress(): 100000, + Cindy.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + specific_fee = 12421 + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Alice.addresslist[1], specific_fee) + self.assertEqual(ec, 0, message) + Alice.mining() + # change is did + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Frank.did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + # change is None + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers) + self.assertEqual(ec, 0, message) + Alice.mining() + + def test_1_didsend_more(self): + did_symbol = 'Zac@'+common.get_random_str() + Alice.send_etp(Zac.mainaddress(), 10**8) + Alice.mining() + Zac.register_did(symbol=did_symbol) + Alice.mining() + + receivers = { + Zac.mainaddress(): 100000, + did_symbol: 200000, + Cindy.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Alice.did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + self.assertEqual(300000,Zac.get_balance(),"sendmore failed") + + + + +class TestDIDSendAsset(MVSTestCaseBase): + @classmethod + def setUpClass(cls): + # check if the did are created. + ec, message = mvs_rpc.list_dids() + if ec != 0: + return + exist_symbols = [i["symbol"] for i in message] + + assert (Alice.did_symbol in exist_symbols) + assert (Bob.did_symbol in exist_symbols) + + def get_asset_amount(self, role, asset_symbol): + addressassets = role.get_addressasset(role.mainaddress()) + + addressasset = filter(lambda a: a.symbol == asset_symbol, addressassets) + if len(addressasset) == 1: + previous_quantity = addressasset[0].quantity + previous_decimal = addressasset[0].decimal_number + return previous_quantity * (10 ** previous_decimal) + elif len(addressasset) == 0: + return 0 + self.assertEqual(0,1,addressasset) + + def test_2_didsend_asset(self): + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + # send to did + pA = self.get_asset_amount(Alice, asset_symbol) + pB = self.get_asset_amount(Bob, asset_symbol) + tx_hash = Alice.didsend_asset(Bob.did_symbol, 1, asset_symbol) + Alice.mining() + cA = self.get_asset_amount(Alice, asset_symbol) + cB = self.get_asset_amount(Bob, asset_symbol) + + self.assertEqual(pA, cA + 1) + self.assertEqual(pB, cB - 1) + + def test_3_didsend_asset_from(self): + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + # send to did + pA = self.get_asset_amount(Alice, asset_symbol) + pB = self.get_asset_amount(Bob, asset_symbol) + + tx_hash = Alice.didsend_asset_from(Alice.did_symbol, Bob.did_symbol, 1, asset_symbol) + Alice.mining() + + cA = self.get_asset_amount(Alice, asset_symbol) + cB = self.get_asset_amount(Bob, asset_symbol) + + self.assertEqual(pA, cA + 1) + self.assertEqual(pB, cB - 1) + +class Testdidcommon(MVSTestCaseBase): + def test_1_registerdid(self): + special_symbol=['@','.','-','_'] + optional = {} + for i in xrange(len(special_symbol)): + optional[Zac.addresslist[i]] = 10**8 + + mvs_rpc.sendmore(Alice.name, Alice.password, optional) + Alice.mining() + + for i ,symbol in enumerate(special_symbol): + did_symbol = '%s%stest%d%s'%(Zac.did_symbol,symbol,i,common.get_random_str()) + ec, message = Zac.register_did(Zac.addresslist[i], did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + self.assertEqual(Zac.get_didaddress(did_symbol), Zac.addresslist[i], 'Failed when registerdid with:'+symbol) + + def test_2_didchangeaddress(self): + did_symbol = 'Zac@'+common.get_random_str() + Alice.send_etp(Zac.mainaddress(), 10**8) + Alice.mining() + + ec, message = Zac.register_did(symbol=did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + self.assertEqual(Zac.get_didaddress(did_symbol), Zac.mainaddress(), 'Failed when registerdid with:'+did_symbol) + + Alice.send_etp(Zac.addresslist[1], 10**4) + Alice.mining() + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + self.assertEqual(Zac.get_didaddress(did_symbol), Zac.addresslist[1], 'Failed when registerdid with:'+did_symbol) + + Alice.send_etp(Zac.mainaddress(), 10**4) + Alice.mining() + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.mainaddress(), did_symbol) + self.assertEqual(ec, 0, message) + Alice.mining() + self.assertEqual(Zac.get_didaddress(did_symbol), Zac.mainaddress(), 'Failed when registerdid with:'+did_symbol) + +class TestdidUTXOcommon(MVSTestCaseBase): + def test_didsend_twice(self): + Alice.send_etp(Zac.mainaddress(), 10**10) + Alice.mining() + + ##registerdid + did_symbol = 'Zac@'+common.get_random_str() + ec, message = Zac.register_did(symbol=did_symbol) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #didsendfrom + ec, message = mvs_rpc.didsend_from(Zac.name,Zac.password, did_symbol, Alice.mainaddress(),10000) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend_from(Zac.name,Zac.password, did_symbol, Alice.mainaddress(),10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #didsend + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #didsendmore + receivers = { + Bob.mainaddress(): 100000, + Cindy.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + ec, message = mvs_rpc.didsendmore(Zac.name, Zac.password, receivers, did_symbol, 10000) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsendmore(Zac.name, Zac.password, receivers, did_symbol, 10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #create asset + domain_symbol, asset_symbol = Zac.create_random_asset(did_symbol=did_symbol) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #sendasset + ec, message = mvs_rpc.didsend_asset(Zac.name,Zac.password, Alice.did_symbol,asset_symbol, 100) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #sendassetfrom + ec, message = mvs_rpc.didsend_asset_from(Zac.name,Zac.password, did_symbol,Alice.did_symbol,asset_symbol, 100) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #register mit + mit_symbol = ("MIT." + common.get_random_str()).upper() + content = "MIT of Zac: " + mit_symbol + ec, message = mvs_rpc.register_mit(Zac.name, Zac.password, did_symbol, mit_symbol, content) + self.assertEqual(ec, code.success, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + # transfer mit + ec, message = Zac.transfer_mit(Bob.did_symbol, mit_symbol) + self.assertEqual(ec, code.success, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + + #issue cert + cert_symbol = Zac.issue_naming_cert(domain_symbol,did_symbol) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #transfer cert + ec, message = mvs_rpc.transfer_cert(Zac.name, Zac.password, Alice.did_symbol, cert_symbol, + 'naming', + fee=None) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() diff --git a/test/test-rpc-v3/TestCase/Identity/test_did_boundary.py b/test/test-rpc-v3/TestCase/Identity/test_did_boundary.py new file mode 100644 index 000000000..3245ed9d2 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Identity/test_did_boundary.py @@ -0,0 +1,323 @@ +import random +import MOCs +from utils import mvs_rpc, validate, common +from TestCase.MVSTestCase import * + +class TestDID(MVSTestCaseBase): + need_mine = False + + def test_0_boundary(self): + # account password match error + ec, message = mvs_rpc.register_did(Zac.name, Zac.password+'1', Zac.mainaddress(), Zac.did_symbol) + self.assertEqual(ec, 1000, message) + + #symbol is address + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), Zac.addresslist[1]) + self.assertEqual(ec, 4010, message) + + #not enough fee + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), Zac.did_symbol, 10 ** 8 -1) + self.assertEqual(ec, 7005, message) + + #invalid percentage + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), Zac.did_symbol, 10 ** 8, 19) + self.assertEqual(ec, 7005, message) + + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), Zac.did_symbol, 10 ** 8, 101) + self.assertEqual(ec, 7005, message) + + #not enough balance + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), Zac.did_symbol, 10 ** 8) + self.assertEqual(ec, 3302, message) + + #did symbol duplicated + did_exist=[Alice, Bob, Cindy] + for role in did_exist: + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), role.did_symbol, 10 ** 8) + self.assertEqual(ec, 7002, message) + + Alice.send_etp(Zac.mainaddress(), 10**8) + Alice.mining() + + blackHoles = ['BlackHole','BLACKHOLE','blackhole','blackHole','Blackhole'] + for bh in blackHoles: + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), bh, 10 ** 8) + self.assertEqual(ec, 7001, message) + + + + def test_1_register_did(self): + ''' + this test case will create did for all roles. If not created before. + ''' + + #address in use + random_did_symbol = common.get_random_str() + ec, message = mvs_rpc.register_did(Alice.name, Alice.password, Alice.mainaddress(), random_did_symbol) + self.assertEqual(ec, 7002, message) + + Alice.send_etp(Zac.mainaddress(), 10**8) + Alice.mining() + #symbol contain special symbol + special_symbol='''~`!#$%^&*()=+|\:;'"?/>''' + for chr in special_symbol: + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), "%s%stest"%(Zac.did_symbol,chr)) + self.assertEqual(ec, 7001, "did symol contains:"+chr) + + def test_2_didsend_etp(self): + # account password match error + ec, message = mvs_rpc.didsend(Alice.name, Alice.password + '1', Zac.mainaddress(), 10**5, 10**4, "test_2_didsend_etp") + self.assertEqual(ec, 1000, message) + + # not enough balance + ec, message = mvs_rpc.didsend(Zac.name, Zac.password, Alice.did_symbol, 10 ** 5, 10 ** 4, "test_2_didsend_etp") + self.assertEqual(ec, 5302, message) + + def test_3_didsend_etp_from(self): + # account password match error + ec, message = mvs_rpc.didsend_from(Alice.name, Alice.password + '1', Alice.mainaddress(), Zac.mainaddress(), 10 ** 5, 10 ** 4, "test_3_didsend_etp_from") + self.assertEqual(ec, 1000, message) + + # not enough balance + ec, message = mvs_rpc.didsend_from(Zac.name, Zac.password, Zac.mainaddress(), Alice.did_symbol, 10 ** 5, 10 ** 4, "test_3_didsend_etp_from") + self.assertEqual(ec, 3302, message) + + def test_4_didsend_asset(self): + # account password match error + ec, message = mvs_rpc.didsend_asset(Alice.name, Alice.password + '1', Zac.mainaddress(), Alice.asset_symbol, 10 ** 5, 10 ** 4) + self.assertEqual(ec, 1000, message) + + def test_5_didsend_asset_from(self): + # account password match error + ec, message = mvs_rpc.didsend_asset_from(Alice.name, Alice.password + '1', Alice.mainaddress(), Zac.mainaddress(), Alice.asset_symbol, 10 ** 5, 10 ** 4) + self.assertEqual(ec, 1000, message) + + def test_6_change_did_boundary(self): + # account password match error + ec, message = mvs_rpc.change_did(Alice.name, Alice.password + '1', Alice.addresslist[1], Alice.did_symbol) + self.assertEqual(ec, 1000, message) + + # Did 'ZAC.DID' does not exist in blockchain + ec, message = mvs_rpc.change_did(Alice.name, Alice.password, Alice.addresslist[1], Zac.did_symbol) + self.assertEqual(ec, 7006, message) + + # Did 'BOB.DID' is not owned by Alice + ec, message = mvs_rpc.change_did(Alice.name, Alice.password, Alice.addresslist[1], Bob.did_symbol) + self.assertEqual(ec, 7009, message) + + # Target address is not owned by account. + ec, message = mvs_rpc.change_did(Alice.name, Alice.password, Bob.addresslist[1], Alice.did_symbol) + self.assertEqual(ec, 4003, message) + + # Target address is already binded with some did in blockchain + ec, message = mvs_rpc.change_did(Alice.name, Alice.password, Alice.mainaddress(), Alice.did_symbol) + self.assertEqual(ec, 7002, message) + + def test_7_change_did(self): + '''modify did between Zac's addresses''' + temp_did = "ZAC.DIID@" + common.get_timestamp() + Alice.send_etp(Zac.mainaddress(), 10**8) + Alice.mining() + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), temp_did) + self.assertEqual(ec, 0, message) + Alice.mining() + + # no enough balance, unspent = 0, payment = 10000 + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], temp_did) + self.assertEqual(ec, 3302, message) + + Alice.send_etp(Zac.addresslist[1], 10 ** 4) + Alice.mining() + + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], temp_did) + self.assertEqual(ec, 0, message) + Alice.mining() + + ec, message = mvs_rpc.list_dids(Zac.name, Zac.password) + self.assertEqual(ec, 0, message) + + self.assertEqual(message[0]['symbol'], temp_did, message) + self.assertEqual(message[0]['address'], Zac.addresslist[1], message) + + # confirm the modification procedure by list_didaddresses + ec, message = mvs_rpc.list_didaddresses(temp_did) + self.assertEqual(ec, 0, message) + + self.assertEqual(message[0]["address"], Zac.addresslist[1]) + self.assertEqual(message[0]["status"], "current") + + self.assertEqual(message[1]["address"], Zac.addresslist[0]) + self.assertEqual(message[1]["status"], "history") + + + def test_8_list_didaddresses_boundary(self): + # did symbol does not exist in blockchain + ec, message = mvs_rpc.list_didaddresses(Zac.did_symbol) + self.assertEqual(ec, 7006, message) + + ec, message = mvs_rpc.list_didaddresses(Zac.mainaddress()) + self.assertEqual(ec, 4017, message) + + + def test_9_change_did_multisig(self): + did_normal_symbal = "Zac@"+common.get_timestamp() + Alice.send_etp(Zac.mainaddress(), 10 ** 8) + Alice.mining() + + ec, message = mvs_rpc.register_did(Zac.name, Zac.password, Zac.mainaddress(), did_normal_symbal) + self.assertEqual(ec, 0, message) + Alice.mining() + + group = [Alice, Cindy, Dale,Frank, Zac] + + did_symbol = '@'.join(r.name for r in group) + common.get_timestamp() + for i, role in enumerate(group): + addr = role.new_multisigaddress("Alice & Cindy & Zac's Multisig-DID", group[:i] + group[i+1:], 3) + + Alice.send_etp(addr, (10 ** 9)) + Alice.mining() + + ec, message = mvs_rpc.register_did(group[0].name, group[0].password, addr, did_symbol) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, message) + self.assertEqual(ec, 0, message) + + tx = message["rawtx"] + ec, message = mvs_rpc.sign_multisigtx(group[2].name, group[2].password, tx, True) + self.assertEqual(ec, 0, message) + Alice.mining() + + # did not find + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.mainaddress(), common.get_timestamp()) + self.assertEqual(ec, 7006, message) + + #did not owner by account + ec, message = mvs_rpc.change_did(Bob.name, Bob.password, Bob.mainaddress(), did_symbol) + self.assertEqual(ec, 7009, message) + + #did address invalid + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, "Test"*20, did_symbol) + self.assertEqual(ec, 4012, message) + + #address didn't owned by the account + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Bob.mainaddress(), did_symbol) + self.assertEqual(ec, 4003, message) + + #address is already binded with did + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.mainaddress(), did_symbol) + self.assertEqual(ec, 7002, message) + + + # no enough balance, unspent = 0, payment = 10000 + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], did_symbol) + self.assertEqual(ec, 3302, message) + + Alice.send_etp(Zac.addresslist[1], 10 ** 5) + Alice.mining() + + #signature must be large than 3 + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], did_symbol) + self.assertEqual(ec, 0, message) + + #cannot transfer to another multi-signature + ec, message = mvs_rpc.sign_multisigtx(group[0].name, group[0].password, message, True) + self.assertEqual(ec, 5304, message) + + group_new = [Bob, Dale, Zac] + for i, role in enumerate(group_new): + addr_new = role.new_multisigaddress( + "Bob & Dale & Zac's Multisig-DID", group_new[:i] + group_new[i+1:], 2) + + Alice.send_etp(addr_new, (10 ** 6)) + Alice.mining() + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, addr_new, did_symbol) + self.assertEqual(ec, 7010, message) + + + ec, message = mvs_rpc.change_did(Zac.name, Zac.password, Zac.addresslist[1], did_symbol) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.sign_multisigtx(group[0].name, group[0].password, message) + self.assertEqual(ec, 0, message) + + tx = message["rawtx"] + ec, message = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, tx, True) + self.assertEqual(ec, 0, message) + self.assertNotEqual(Zac.get_didaddress(symbol=did_symbol), Zac.addresslist[1], "Failed where modify did address from multi_signature to multi_signature address") + + + +class TestDIDSendMore(MVSTestCaseBase): + need_mine = False + + def test_0_didsend_more(self): + receivers = { + Bob.mainaddress(): 100000, + Zac.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + specific_fee = 12421 + + # password error + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password + '1', receivers, Alice.addresslist[1], specific_fee) + self.assertEqual(ec, 1000, message) + + # receivers contain not exits did -- Zac' did + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Alice.addresslist[1], specific_fee) + self.assertEqual(ec, 7006, message) + + #Zac -> Cindy + receivers = { + Bob.mainaddress(): 100000, + Cindy.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + # mychange -- not exits did + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Zac.did_symbol, specific_fee) + self.assertEqual(ec, 7006, message) + + def test_1_didsend_more(self): + receivers = { + Bob.mainaddress(): 100000, + Cindy.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + #no enough balance + ec, message = mvs_rpc.didsendmore(Zac.name, Zac.password, receivers, Alice.did_symbol) + self.assertEqual(ec, 5302, message) + + def test_2_didsend_more(self): + receivers = { + Bob.mainaddress(): 100000, + 'Invalid_address_'+ common.get_timestamp(): 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + #Invalid_address or did + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Alice.did_symbol) + self.assertEqual(ec, 7006, message) + + def test_3_didsend_more(self): + receivers = { + Bob.mainaddress(): 100000, + Cindy.did_symbol: 200000, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + min_fee = 10**4-1 + max_fee = 10*10+1 + + #Invalid fee + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Alice.did_symbol,min_fee) + self.assertEqual(ec, 5005, message) + + + ec, message = mvs_rpc.didsendmore(Alice.name, Alice.password, receivers, Alice.did_symbol,max_fee) + self.assertEqual(ec, 5005, message) + + + diff --git a/test/test-rpc-v3/TestCase/Identity/test_did_multisig.py b/test/test-rpc-v3/TestCase/Identity/test_did_multisig.py new file mode 100644 index 000000000..e35c0221b --- /dev/null +++ b/test/test-rpc-v3/TestCase/Identity/test_did_multisig.py @@ -0,0 +1,101 @@ +from utils import common +from TestCase.MVSTestCase import * + +class TestDIDMultiSig(MVSTestCaseBase): + + def test_0_registerdid(self): + group = [Alice, Bob, Zac] + did_symbol = "Alice.Bob.Zac.DIID." + common.get_random_str() + + for i, role in enumerate(group): + addr = role.new_multisigaddress("Alice & Bob & Zac's Multisig-DID", group[:i] + group[i+1:], 2) + + Alice.send_etp(addr, (10 ** 8)) + Alice.mining() + + ec, tx = mvs_rpc.register_did(group[0].name, group[0].password, addr, did_symbol) + self.assertEqual(ec, 0, tx) + + ec, tx = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, tx, True) + self.assertEqual(ec, 0, tx) + + Alice.mining() + + ec, message = mvs_rpc.list_dids() + self.assertEqual(ec, 0, message) + + for did_ in message: + if did_['symbol'] == did_symbol and did_["address"] == addr: + break + else: + self.assertEqual(0, 1, "did -> multi-sig addr not found error") + + + def test_1_modifydid(self): + #import pdb; pdb.set_trace() + group = [Alice, Cindy, Zac] + + did_symbol = '@'.join(r.name for r in group) + common.get_random_str() + for i, role in enumerate(group): + addr = role.new_multisigaddress("Alice & Cindy & Zac's Multisig-DID", group[:i] + group[i+1:], 2) + + Alice.send_etp(addr, (10 ** 9)) + Alice.mining() + + + ec, tx = mvs_rpc.register_did(group[0].name, group[0].password, addr, did_symbol) + self.assertEqual(ec, 0, tx) + + ec, tx = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, tx, True) + self.assertEqual(ec, 0, tx) + Alice.mining() + + + group_new = [ Bob, Dale, Zac] + for i, role in enumerate(group_new): + addr_new = role.new_multisigaddress( + "Bob & Dale & Zac's Multisig-DID", group_new[:i] + group_new[i+1:], 2) + + Alice.send_etp(addr_new, (10 ** 6)) + Alice.mining() + + normal_new = Zac.mainaddress() + Alice.send_etp(normal_new , 123456789) + Alice.mining() + + ec, tx = mvs_rpc.change_did(Zac.name, Zac.password, normal_new, did_symbol) + self.assertEqual(ec, 0, tx) + + ec, tx = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, tx, True) + self.assertEqual(ec, 0, tx) + Alice.mining() + + did_address = group[0].get_didaddress(did_symbol) + self.assertEqual(did_address, normal_new, "Failed where modify did address from multi_signature to normal ") + + normal_new = Zac.addresslist[1] + Alice.send_etp(normal_new , (10 ** 6) ) + Alice.mining() + ec, tx = mvs_rpc.change_did(Zac.name, Zac.password, normal_new, did_symbol) + self.assertEqual(ec, 0, tx) + Alice.mining() + + did_address = group[0].get_didaddress(did_symbol) + self.assertEqual(did_address, normal_new, "Failed where modify did address from normal to normal") + + ec, tx = mvs_rpc.change_did(Zac.name, Zac.password, addr_new, did_symbol) + self.assertEqual(ec, 0, tx) + + ec, tx = mvs_rpc.sign_multisigtx(group_new[0].name, group_new[0].password, tx, True) + self.assertEqual(ec, 0, tx) + Alice.mining() + + did_address = group[0].get_didaddress(did_symbol) + self.assertEqual(did_address, addr_new, "Failed where modify did address from normal to multi_signature address") + + +class TestWithExistDID(MultiSigDIDTestCase): + def test_0_todo(self): + pass + + diff --git a/test/test-rpc-v3/TestCase/MIT/__init__.py b/test/test-rpc-v3/TestCase/MIT/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/MIT/test_batch_mit.py b/test/test-rpc-v3/TestCase/MIT/test_batch_mit.py new file mode 100644 index 000000000..57437e21d --- /dev/null +++ b/test/test-rpc-v3/TestCase/MIT/test_batch_mit.py @@ -0,0 +1,64 @@ +import time +import MOCs +from utils import common +from TestCase.MVSTestCase import * + +class TestRegisterMIT(MVSTestCaseBase): + + def test_0_boundary(self): + test_symbol = common.get_random_str() + + # check symbol length + mits=["X"*61 + ":invalid"] + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.asset_symbol_length_exception, message) + + # check invalid char in symbol + spec_char_lst = "`~!#$%^&*()+[{]}\\|;'\",<>/?" + for char in spec_char_lst: + mits=[test_symbol + char + ":invalid"] + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.asset_symbol_name_exception, message) + + # check content length + mits=[test_symbol + ":" + "X"*257] + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.argument_size_invalid_exception, message) + + # check duplicate symbols + mits=[test_symbol + ":" + "M"*256, test_symbol + ":" + "Duplicate symbol"] + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.asset_symbol_existed_exception, message) + + # check empty symbol + mits=[] + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.argument_legality_exception, message) + + # check to did not exist + mits=[test_symbol + ":" + "M"*256] + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol + "1", mits=mits) + self.assertEqual(ec, code.did_symbol_notfound_exception, message) + + # check to did not owned + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Bob.did_symbol, mits=mits) + self.assertEqual(ec, code.address_dismatch_account_exception, message) + + # check symbol already exist + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.success, message) + Alice.mining() + + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.asset_symbol_existed_exception, message) + + def test_1_register_mits(self): + # set max_paramters in Mongoose.hpp to 208 + max_mit_count = 100 + + mits = [] + for i in range(0, max_mit_count): + mits.append("{}@{}:content of {}".format(common.get_random_str(), i, i)) + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, mits=mits) + self.assertEqual(ec, code.success, message) + Alice.mining() \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/MIT/test_mit.py b/test/test-rpc-v3/TestCase/MIT/test_mit.py new file mode 100644 index 000000000..7a5b4081e --- /dev/null +++ b/test/test-rpc-v3/TestCase/MIT/test_mit.py @@ -0,0 +1,159 @@ +import time +import MOCs +from utils import common +from TestCase.MVSTestCase import * + +class TestRegisterMIT(MVSTestCaseBase): + + def test_0_boundary(self): + + test_symbol = common.get_random_str() + + #account password error + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password + '1', Alice.did_symbol, test_symbol) + self.assertEqual(ec, 1000, message) + + # check symbol length + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, "X"*61) + self.assertEqual(ec, code.asset_symbol_length_exception, message) + + # check invalid char in symbol + spec_char_lst = "`~!#$%^&*()+[{]}\\|;:'\",<>/?" + for char in spec_char_lst: + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, test_symbol + char) + self.assertEqual(ec, code.asset_symbol_name_exception, message) + + # check content length + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, test_symbol, "X"*257) + self.assertEqual(ec, code.argument_size_invalid_exception, message) + + # check to did not exist + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol + "1", test_symbol) + self.assertEqual(ec, code.did_symbol_notfound_exception, message) + + # check to did not owned + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Bob.did_symbol, test_symbol) + self.assertEqual(ec, code.address_dismatch_account_exception, message) + + # check symbol already exist + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, test_symbol) + self.assertEqual(ec, code.success, message) + Alice.mining() + + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, test_symbol) + self.assertEqual(ec, code.asset_symbol_existed_exception, message) + + # check max length of content 256 + test_symbol = common.get_random_str() + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, test_symbol, "X"*256) + self.assertEqual(ec, code.success, message) + + + def test_1_register_mit(self): + symbol = ("MIT." + common.get_random_str()).upper() + content = "MIT of Alice: " + symbol + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, symbol, content) + self.assertEqual(ec, code.success, message) + Alice.mining() + + # test list_mits with account + ec, message = Alice.list_mits(Alice.name, Alice.password) + self.assertEqual(ec, code.success, message) + + mits = message + self.assertGreater(len(mits), 0) + found_mits = filter(lambda a: a["symbol"] == symbol, mits) + self.assertEqual(len(found_mits), 1) + + mit = found_mits[0] + self.assertEqual(mit["symbol"], symbol) + self.assertEqual(mit["content"], content) + self.assertEqual(mit["status"], "registered") + + # test list_mits without account + ec, message = Alice.list_mits() + self.assertEqual(ec, code.success, message) + + mits = message + self.assertGreater(len(mits), 0) + found_mits = filter(lambda a: a["symbol"] == symbol, mits) + self.assertEqual(len(found_mits), 1) + + mit = found_mits[0] + self.assertEqual(mit["symbol"], symbol) + self.assertEqual(mit["content"], content) + self.assertEqual(mit["status"], "registered") + + # test get_mit with symbol + ec, message = Alice.get_mit(symbol) + self.assertEqual(ec, code.success, message) + self.assertEqual(message["symbol"], symbol) + self.assertEqual(message["content"], content) + self.assertEqual(message["status"], "registered") + + # test get_mit without symbol + ec, message = Alice.get_mit() + self.assertEqual(ec, code.success, message) + + mits = message + self.assertGreater(len(mits), 0) + found_mits = filter(lambda a: a == symbol, mits) + self.assertEqual(len(found_mits), 1) + + + def test_2_transfer_mit(self): + symbol = ("MIT." + common.get_random_str()).upper() + content = "MIT of Alice: " + symbol + + # not enough mit + ec, message = Alice.transfer_mit(Bob.did_symbol, symbol) + self.assertEqual(ec, code.asset_lack_exception, message) + + # register mit + ec, message = mvs_rpc.register_mit(Alice.name, Alice.password, Alice.did_symbol, symbol, content) + self.assertEqual(ec, code.success, message) + Alice.mining() + + # transfer mit + ec, message = Alice.transfer_mit(Bob.did_symbol, symbol) + self.assertEqual(ec, code.success, message) + Alice.mining() + + # test get_mit with symbol + ec, message = Bob.get_mit(symbol) + self.assertEqual(ec, code.success, message) + self.assertEqual(message["symbol"], symbol) + self.assertEqual(message["content"], content) + self.assertEqual(message["status"], "registered") + + # test get_mit with symbol and history + ec, message = Bob.get_mit("", True) + self.assertEqual(ec, code.argument_legality_exception, message) + + ec, message = Bob.get_mit(symbol, True, 0, 100) + self.assertEqual(ec, code.argument_legality_exception, message) + + ec, message = Bob.get_mit(symbol, True, 1, 101) + self.assertEqual(ec, code.argument_legality_exception, message) + + # success + ec, message = Bob.get_mit(symbol, True) + self.assertEqual(ec, code.success, message) + + mits = message + self.assertGreater(len(mits), 0) + found_mits = filter(lambda a: a["symbol"] == symbol, mits) + self.assertEqual(len(found_mits), 2) + + mit = found_mits[0] + self.assertEqual(mit["symbol"], symbol) + self.assertEqual(mit["status"], "transfered") + + # root + mit = found_mits[1] + self.assertEqual(mit["symbol"], symbol) + self.assertEqual(mit["status"], "registered") + + # not enough mit + ec, message = Alice.transfer_mit(Bob.did_symbol, symbol) + self.assertEqual(ec, code.asset_lack_exception, message) diff --git a/test/test-rpc-v3/TestCase/MVSTestCase.py b/test/test-rpc-v3/TestCase/MVSTestCase.py new file mode 100644 index 000000000..b9ed19756 --- /dev/null +++ b/test/test-rpc-v3/TestCase/MVSTestCase.py @@ -0,0 +1,156 @@ +import unittest +from utils import mvs_rpc, common, code +from Roles import Alice, Bob, Cindy, Dale, Eric, Frank, Zac + +class MVSTestCaseBase(unittest.TestCase): + roles = (Alice, Bob, Cindy, Dale, Eric, Frank, Zac) + need_mine = True + def setUp(self): + for role in self.roles: + try: + # check if the role exists by get_balance + role.delete() + finally: + result, message = role.create() + self.assertEqual(result, 0, message) + + # register did for role A~F, if not registered + for role in self.roles[:-1]: + ec, message = mvs_rpc.list_dids(role.name, role.password) + if ec == 0 and len(message) > 0: + pass + else: + if role != Alice: + Alice.send_etp(role.mainaddress(), 10 ** 8) + Alice.mining() + ec, message = role.register_did() + self.assertEqual(ec, 0, "error: {}, message: {}".format(ec, message)) + Alice.mining() + + if self.need_mine: + #mine to clear the tx pool + Alice.mining() + + #record current height + _, (hash, height) = mvs_rpc.getblockheader() + print "current block height=[%s], hash=[%s]" % (height, hash) + + def tearDown(self): + pass + #for role in self.roles: + # result, message = role.delete() + # self.assertEqual(result, 0, message) + + def checkResponseKeys(self, result, expect_keys): + if result != None and isinstance(result, dict): + expect_keys.sort() + actual_keys = result.keys() + actual_keys.sort() + self.assertEqual(actual_keys, expect_keys) + +class MultiSigDIDTestCase(MVSTestCaseBase): + def setUp(self): + MVSTestCaseBase.setUp(self) + self.group_ABC = [Alice, Bob, Cindy] + self.group_DEF = [Dale, Eric, Frank] + self.addr_ABC = common.create_multisig_address(self.group_ABC, 2) + self.addr_DEF = common.create_multisig_address(self.group_DEF, 2) + + # register did if not registered + self.did_ABC = "Alice.Bob.Cindy@DIID" + self.did_DEF = "Dale.Eric.Frank@DIID" + + for roles, addr, attr_name in [(self.group_ABC, self.addr_ABC, "did_ABC"), (self.group_DEF, self.addr_DEF, "did_DEF")]: + ec, message = mvs_rpc.list_dids(roles[-1].name, roles[-1].password) + self.assertEqual(ec, 0, message) + for did_info in message: + if did_info["address"] == addr: + setattr(self, attr_name, did_info["symbol"]) + break + else: + # not issued + Alice.send_etp(addr, 10**8) + Alice.mining() + + did_symbol = getattr(self, attr_name) + + ec, tx = mvs_rpc.register_did(roles[0].name, roles[0].password, addr, did_symbol) + self.assertEqual(ec, 0, tx) + ec, tx = mvs_rpc.sign_multisigtx(roles[1].name, roles[1].password, tx, True) + self.assertEqual(ec, 0, tx) + Alice.mining() + +class ForkTestCase(MVSTestCaseBase): + remote_ip = "10.10.10.92" + remote_ctrl = None + @classmethod + def setUpClass(cls): + cls.remote_ctrl = mvs_rpc.RemoteCtrl(cls.remote_ip) + + def setUp(self): + '''import Alice account to the remote''' + MVSTestCaseBase.setUp(self) + self.partion_height = -1 + try: + with open(Alice.keystore_file) as f: + ec, message = self.remote_ctrl.import_keyfile(Alice.name, Alice.password, "any", f.read() ) + # it still works if the account Alice already exist in remote wallet + #self.assertEqual(ec, 0, message) + except : + print 'unable to connect remote url:'+self.remote_ip + + + def tearDown(self): + try: + self.remote_ctrl.delete_account(Alice.name, Alice.password, Alice.lastword()) + + ec, message = mvs_rpc.add_node( self.remote_ip + ':5251') + self.assertEqual(ec, 0, message) + except : + print 'unable to connect remote url:'+self.remote_ip + + MVSTestCaseBase.tearDown(self) + + def make_partion(self): + '''make the p2p network partion into 2 seperate ones.''' + # record current block height + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + self.partion_height = message[0] + + ec, peers = mvs_rpc.getpeerinfo() + for peer in peers: + ec, message = mvs_rpc.ban_node( peer) + self.assertEqual(ec, 0, message) + + def remote_ming(self, times): + mining = mvs_rpc.remote_call(self.remote_ip, Alice.mining) + mining(times) + + get_info = mvs_rpc.remote_call(self.remote_ip, mvs_rpc.get_info) + ec, message = get_info() + self.assertEqual(ec, 0, message) + return message[0] # expect hight + + def fork(self): + try: + ming_round = 6 + expect_hight = self.remote_ming(ming_round) + + ec, message = mvs_rpc.add_node( self.remote_ip+':5251') + self.assertEqual(ec, 0, message) + import time + new_height = 0 + + # wait until the fork complete + timeout = 300 + while new_height < expect_hight:#self.partion_height + ming_round: + time.sleep(1) + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + new_height = message[0] + timeout -= 1 + self.assertGreater(timeout, 0, "wait fork timeout error!") + except : + print 'unable to connect remote url:'+self.remote_ip + diff --git a/test/test-rpc-v3/TestCase/MultiSignature/__init__.py b/test/test-rpc-v3/TestCase/MultiSignature/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/MultiSignature/test_multisig.py b/test/test-rpc-v3/TestCase/MultiSignature/test_multisig.py new file mode 100644 index 000000000..2efe15737 --- /dev/null +++ b/test/test-rpc-v3/TestCase/MultiSignature/test_multisig.py @@ -0,0 +1,103 @@ +from TestCase.MVSTestCase import * + +class TestMultiSig(MVSTestCaseBase): + def test_0_multisig_2p3(self): + #create multisig addr + addr_a = Alice.new_multisigaddress("A&B&Z", [Bob, Zac], 2) + addr_b = Bob.new_multisigaddress("B&A&Z", [Alice, Zac], 2) + addr_z = Zac.new_multisigaddress("Z&A&B", [Alice, Bob], 2) + + self.assertEqual(addr_a, addr_b) + self.assertEqual(addr_a, addr_z) + + amount = 10**8 + fee = 10 ** 4 + + filter_balances = lambda x: x['address'] == addr_z + + #send to multisig-addr + Alice.send_etp(addr_z, amount) + Alice.mining() + ec, message = mvs_rpc.list_balances(Zac.name, Zac.password, (amount, amount)) + self.assertEqual(ec, 0 , message) + + balances = filter(filter_balances, message) + self.assertNotEqual(balances, None, balances) + self.assertEqual(len(balances), 1) + + self.assertEqual(balances[0]['address'], addr_z) + self.assertEqual(balances[0]['unspent'], amount) + + #send from multisig-addr + ec, tx = mvs_rpc.create_multisigtx(Alice.name, Alice.password, addr_a, Zac.mainaddress(), amount-fee) + self.assertEqual(ec, 0 , tx) + + ec, txs = mvs_rpc.delete_multisig(Alice.name, Alice.password, addr_a) + self.assertEqual(ec, 0 , txs) + self.assertEqual(len(txs), 1 , txs) + + addr_a = Alice.new_multisigaddress("A&B&Z", [Bob, Zac], 2) + + ec, tx = mvs_rpc.create_multisigtx(Alice.name, Alice.password, addr_a, Zac.mainaddress(), amount-fee) + self.assertEqual(ec, 0 , tx) + + ec, message = mvs_rpc.sign_multisigtx(Bob.name, Bob.password, tx, True) + self.assertEqual(ec, 0, message) + + Alice.mining() + + ec, message = mvs_rpc.list_balances(Zac.name, Zac.password, (0, amount)) + self.assertEqual(ec, 0, message) + + balances = filter(filter_balances, message) + self.assertEqual(len(balances), 1) + self.assertEqual(balances[0]['unspent'], 0) + + + def test_1_multisig_3p3(self): + addr_a = Alice.new_multisigaddress("A&B&Z", [Bob, Zac], 3) + addr_b = Bob.new_multisigaddress("B&A&Z", [Alice, Zac], 3) + addr_z = Zac.new_multisigaddress("Z&A&B", [Alice, Bob], 3) + self.assertEqual(addr_a, addr_b) + self.assertEqual(addr_a, addr_z) + + amount = 10 ** 8 + fee = 10 ** 4 + + filter_balances = lambda x: x['address'] == addr_z + + #send to multisig-addr + Alice.send_etp(addr_z, amount) + Alice.mining() + ec, message = mvs_rpc.list_balances(Zac.name, Zac.password, (amount, amount)) + self.assertEqual(ec, 0 , message) + + balances = filter(filter_balances, message) + self.assertNotEqual(balances, None, balances) + self.assertEqual(len(balances), 1) + + self.assertEqual(balances[0]['address'], addr_z) + self.assertEqual(balances[0]['unspent'], amount) + + # send from multisig-addr + ec, tx = mvs_rpc.create_multisigtx(Alice.name, Alice.password, addr_a, Zac.mainaddress(), amount - fee) + self.assertEqual(ec, 0, tx) + + ec, message = mvs_rpc.sign_multisigtx(Bob.name, Bob.password, tx, True) + self.assertEqual(ec, 5304, message) + + ec, message = mvs_rpc.sign_multisigtx(Bob.name, Bob.password, tx, False) + self.assertEqual(ec, 0, tx) + + tx = message["rawtx"] + ec, message = mvs_rpc.sign_multisigtx(Zac.name, Zac.password, tx, True) + self.assertEqual(ec, 0, tx) + + Alice.mining() + + ec, message = mvs_rpc.list_balances(Zac.name, Zac.password, (0, amount)) + self.assertEqual(ec, 0, message) + + balances = filter(filter_balances, message) + self.assertEqual(len(balances), 1) + self.assertEqual(balances[0]['unspent'], 0) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/MultiSignature/test_multisig_asset.py b/test/test-rpc-v3/TestCase/MultiSignature/test_multisig_asset.py new file mode 100644 index 000000000..f47ffcc88 --- /dev/null +++ b/test/test-rpc-v3/TestCase/MultiSignature/test_multisig_asset.py @@ -0,0 +1,47 @@ +from TestCase.MVSTestCase import * + +class TestMultiSigasset(MVSTestCaseBase): + def test_0_multisig_asset(self): + Alice.ensure_balance() + + #create multisig addr + domain_symbol, asset_symbol = Alice.create_random_asset(secondary=-1) + Alice.mining() + + amount_total = self.get_asset_amount(Alice, asset_symbol) + amount = 2000 + amount_ret = 1000 + + group = [Alice, Bob, Zac] + address = common.create_multisig_address(group, 2) + Alice.send_etp(address, 10**4) + Alice.mining() + + # send amount asset to multi-signature address + result, message = mvs_rpc.send_asset(Alice.name, Alice.password, address, asset_symbol, amount) + self.assertEqual(result, 0 , message) + Alice.mining() + self.assertEqual(amount, self.get_asset_amount(group[0], asset_symbol, address) , "Failed when send asset from Alice to multi-signature address") + + #send from multisig-addr + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), amount_ret, 3, asset_symbol) + self.assertEqual(result, 0 , message) + + ec, message = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, message, True) + self.assertEqual(ec, 0, message) + Alice.mining() + + self.assertEqual(amount_total-amount+amount_ret, + self.get_asset_amount(Alice, asset_symbol), "Failed when send asset from multi-signature address to Alice") + + + def get_asset_amount(self, role, asset_symbol, addr=None): + if addr == None: + addr = role.didaddress() + addressassets = role.get_addressasset(addr) + + addressasset = filter(lambda a: a.symbol == asset_symbol, addressassets) + self.assertEqual(len(addressasset), 1) + previous_quantity = addressasset[0].quantity + previous_decimal = addressasset[0].decimal_number + return previous_quantity * (10 ** previous_decimal) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/MultiSignature/test_multisig_boundary.py b/test/test-rpc-v3/TestCase/MultiSignature/test_multisig_boundary.py new file mode 100644 index 000000000..fff3f25d8 --- /dev/null +++ b/test/test-rpc-v3/TestCase/MultiSignature/test_multisig_boundary.py @@ -0,0 +1,145 @@ +from TestCase.MVSTestCase import * + +class TestMultiSig(MVSTestCaseBase): + need_mine = False + def test_0_getnew(self): + '''test getnew(_multisig''' + # account password match error + ec, message = mvs_rpc.getnew_multisig(Alice.name, Alice.password + '1', "test", Alice.mainaddress(), [Bob.mainaddress(), Cindy.mainaddress()], 2) + self.assertEqual(ec, 1000, message) + + #public key empty + ec, message = mvs_rpc.getnew_multisig(Alice.name, Alice.password, "test", Alice.mainaddress(), [], 2) + self.assertEqual(ec, 5201, message) + + # m = 0 + ec, message = mvs_rpc.getnew_multisig(Alice.name, Alice.password, "test", Alice.mainaddress(), [Bob.mainaddress(), Cindy.mainaddress()], 0) + self.assertEqual(ec, 5220, message) + + # n > 20 + #ec, message = mvs_rpc.getnew_multisig(Alice.name, Alice.password, "test", Alice.get_publickey(Alice.mainaddress()), [Bob.get_publickey(Bob.mainaddress())]*20, 2) + #self.assertEqual(ec, 1000, message) + + #not belongs to this account + ec, message = mvs_rpc.getnew_multisig(Alice.name, Alice.password, "test", + Alice.mainaddress(), + [Bob.get_publickey(Bob.mainaddress()), + Cindy.get_publickey(Cindy.mainaddress())], 2) + self.assertEqual(ec, 5231, message) + + #multisig already exists. + Alice.new_multisigaddress("test_getnew", [Bob, Cindy], 2) + + ec, message = mvs_rpc.getnew_multisig(Alice.name, Alice.password, "test", Alice.get_publickey( Alice.mainaddress() ), + [Bob.get_publickey( Bob.mainaddress() ), Cindy.get_publickey( Cindy.mainaddress() )], 2) + self.assertEqual(ec, 5202, message) + + def test_1_list(self): + '''test list_multisig''' + # account password match error + ec, message = mvs_rpc.list_multisig(Alice.name, Alice.password + '1') + self.assertEqual(ec, 1000, message) + + # no multisig addr + ec, message = mvs_rpc.list_multisig(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 0) + + # create 1 multi add + addr = Alice.new_multisigaddress('A&B&C', [Bob, Cindy], 2) + ec, message = mvs_rpc.list_multisig(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 1, message) + self.assertEqual(message[0]["address"], addr, message) + + def test_2_delete(self): + '''test delete multisig addr''' + # account password match error + ec, message = mvs_rpc.delete_multisig(Alice.name, Alice.password + '1', "x") + self.assertEqual(ec, 1000, message) + + # invalid address + ec, message = mvs_rpc.delete_multisig(Alice.name, Alice.password, "x") + self.assertEqual(ec, 4015, message) + + addr = Alice.new_multisigaddress('A&B&C', [Bob, Cindy], 2) + ec, message = mvs_rpc.delete_multisig(Alice.name, Alice.password, addr) + self.assertEqual(ec, 0, message) + + #confirm by list + ec, message = mvs_rpc.list_multisig(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + self.assertEqual(len(message), 0) + + def test_3_multi_sendasset(self): + domain_symbol, asset_symbol = Alice.create_random_asset() + Alice.mining() + + total_amount = self.get_asset_amount(Alice, asset_symbol) + self.assertGreater(total_amount, 0, "failed to create asset") + + group = [Alice, Bob, Cindy,Dale, Zac] + address = common.create_multisig_address(group, 3) + Alice.send_etp(address, 10**5) + Alice.mining() + + # send amount asset to multi-signature address + result, message = mvs_rpc.send_asset(Alice.name, Alice.password, address, asset_symbol, 2000) + self.assertEqual(result, 0 , message) + Alice.mining() + + #send from multisig-addr + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), 1500, 2 ,asset_symbol) + self.assertEqual(result, 2003 , message) + + #invalid parameter + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), -1, 3 ,asset_symbol) + self.assertEqual(result, 1021 , message) + + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), 200**10, 3 ,asset_symbol) + self.assertEqual(result, 1021 , message) + + #no enough amount + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), total_amount+1000, 3 ,asset_symbol) + self.assertEqual(result, 5001 , message) + + #invalid symbol + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), 100, 3 , "test"+common.get_timestamp()) + self.assertEqual(result, 5001 , message) + + #multisig of from address is not found + result, message = mvs_rpc.create_multisigtx(Frank.name, Frank.password, address, Alice.mainaddress(), total_amount+1000, 3 ,asset_symbol) + self.assertEqual(result, 5203 , message) + + #signature must be large than 3 + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), 100, 3 , asset_symbol) + self.assertEqual(result, 0 , message) + + result, message = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, message, True) + self.assertEqual(result, 5304 , message) + + #success test case + result, message = mvs_rpc.create_multisigtx(group[0].name, group[0].password, address, Alice.mainaddress(), 100, 3 , asset_symbol) + self.assertEqual(result, 0 , message) + + result, message = mvs_rpc.sign_multisigtx(group[1].name, group[1].password, message) + self.assertEqual(result, 0 , message) + + tx = message["rawtx"] + result, message = mvs_rpc.sign_multisigtx(group[2].name, group[2].password, tx, True) + self.assertEqual(result, 0 , message) + Alice.mining() + self.assertEqual(self.get_asset_amount(group[0], asset_symbol, address), 2000-100,"Failed when send asset from multi-signature to normal") + + + def get_asset_amount(self, role, asset_symbol, addr=None): + if addr == None: + addr = role.mainaddress() + addressassets = role.get_addressasset(addr) + + #we only consider Alice's Asset + addressasset = filter(lambda a: a.symbol == asset_symbol, addressassets) + self.assertEqual(len(addressasset), 1) + previous_quantity = addressasset[0].quantity + previous_decimal = addressasset[0].decimal_number + return previous_quantity * (10 ** previous_decimal) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/RawTx/__init__.py b/test/test-rpc-v3/TestCase/RawTx/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/RawTx/test_rawtx.py b/test/test-rpc-v3/TestCase/RawTx/test_rawtx.py new file mode 100644 index 000000000..6511c91e1 --- /dev/null +++ b/test/test-rpc-v3/TestCase/RawTx/test_rawtx.py @@ -0,0 +1,178 @@ +from TestCase.MVSTestCase import * + + +class TestRawTx(MVSTestCaseBase): + + def test_0_create_rawtx_boundary(self): + senders = {Alice.mainaddress()} + single_receiver = {Bob.mainaddress() : 10} + multi_receivers = {Bob.mainaddress() : 10, Cindy.mainaddress() : 20} + + # invalid type + ec, message = mvs_rpc.create_rawtx(4, senders, single_receiver); + self.assertEqual(ec, code.argument_legality_exception, message) + + # invalid senders address + ec, message = mvs_rpc.create_rawtx(0, {Alice.mainaddress() + "1"}, single_receiver,); + self.assertEqual(ec, code.fromaddress_invalid_exception, message) + + # invalid receiver address + ec, message = mvs_rpc.create_rawtx(0, + {Alice.mainaddress()}, + {Bob.mainaddress()+"1" : 10}, + mychange=Alice.mainaddress()); + self.assertEqual(ec, code.toaddress_invalid_exception, message) + + # invalid receiver amount + ec, message = mvs_rpc.create_rawtx(0, + {Alice.mainaddress()}, + {Bob.mainaddress() : 0}, + mychange=Alice.mainaddress()); + self.assertEqual(ec, code.argument_legality_exception, message) + + # invalid mychange address + ec, message = mvs_rpc.create_rawtx(0, senders, single_receiver, mychange=Alice.mainaddress()+"1"); + self.assertEqual(ec, code.toaddress_invalid_exception, message) + + # invalid symbol for deposit etp + ec, message = mvs_rpc.create_rawtx(1, senders, single_receiver, symbol="ABC"); + self.assertEqual(ec, code.argument_legality_exception, message) + + # invalid multi receivers for deposit etp + ec, message = mvs_rpc.create_rawtx(1, senders, multi_receivers); + self.assertEqual(ec, code.argument_legality_exception, message) + + # invalid symbol for transfer asset + ec, message = mvs_rpc.create_rawtx(3, senders, single_receiver, symbol=""); + self.assertEqual(ec, code.asset_symbol_length_exception, message) + + ec, message = mvs_rpc.create_rawtx(3, senders, single_receiver, symbol="x"*65); + self.assertEqual(ec, code.asset_symbol_length_exception, message) + + # no asset exist + ec, message = mvs_rpc.create_rawtx(3, senders, single_receiver, symbol="x"*64); + self.assertEqual(ec, code.asset_lack_exception, message) + + # invalid fee + ec, message = mvs_rpc.create_rawtx(0, senders, multi_receivers, fee=0); + self.assertEqual(ec, code.asset_exchange_poundage_exception, message) + + ec, message = mvs_rpc.create_rawtx(0, senders, multi_receivers, fee=10**4 -1); + self.assertEqual(ec, code.asset_exchange_poundage_exception, message) + + ec, message = mvs_rpc.create_rawtx(0, senders, multi_receivers, fee=10000000001); + self.assertEqual(ec, code.asset_exchange_poundage_exception, message) + + + def test_1_create_rawtx_transfer_etp(self): + senders = {Alice.mainaddress()} + single_receiver = {Bob.mainaddress() : 10} + multi_receivers = {Bob.mainaddress() : 10, Cindy.mainaddress() : 20} + + ec, message = mvs_rpc.create_rawtx(0, senders, single_receiver, mychange=Alice.mainaddress()); + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message), 0) + Alice.mining() + + ec, message = mvs_rpc.create_rawtx(0, senders, multi_receivers, mychange=Alice.mainaddress()); + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message), 0) + Alice.mining() + + + def test_2_create_rawtx_deposit_etp(self): + senders = {Alice.mainaddress()} + single_receiver = {Bob.mainaddress() : 10} + multi_receivers = {Bob.mainaddress() : 10, Cindy.mainaddress() : 20} + + ec, message = mvs_rpc.create_rawtx(1, senders, single_receiver, mychange=Alice.mainaddress()); + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message), 0) + Alice.mining() + + + def test_3_create_rawtx_transfer_asset(self): + senders = {Alice.mainaddress()} + single_receiver = {Bob.mainaddress() : 10} + multi_receivers = {Bob.mainaddress() : 10, Cindy.mainaddress() : 20} + + # create asset + domain_symbol, asset_symbol = Alice.create_random_asset(secondary=-1) + Alice.mining() + + ec, message = mvs_rpc.create_rawtx(3, senders, single_receiver, symbol=asset_symbol); + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message), 0) + Alice.mining() + + ec, message = mvs_rpc.create_rawtx(3, senders, multi_receivers, symbol=asset_symbol); + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message), 0) + Alice.mining() + + def test_4_rawtx(self): + senders = {Alice.mainaddress()} + single_receiver = {Bob.mainaddress() : 10} + multi_receivers = {Bob.mainaddress() : 10, Cindy.mainaddress() : 20} + + # create rawtx + ec, message = mvs_rpc.create_rawtx(0, senders, single_receiver, mychange=Alice.mainaddress()); + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message), 0) + Alice.mining() + + rawtx = message + + # invalid password + ec, message = mvs_rpc.sign_rawtx(Alice.name, Alice.password + '1', rawtx) + self.assertEqual(ec, 1000, message) + + # invalid account + ec, message = mvs_rpc.sign_rawtx(Bob.name, Bob.password, rawtx) + self.assertEqual(ec, code.argument_legality_exception, message) + + # invalid transaction + ec, message = mvs_rpc.sign_rawtx(Bob.name, Bob.password, rawtx + '1') + self.assertEqual(ec, code.command_params_exception, message) + + # sign + ec, message = mvs_rpc.sign_rawtx(Alice.name, Alice.password, rawtx) + self.assertEqual(ec, code.success, message) + self.assertGreater(len(message["hash"]), 0) + self.assertGreater(len(message["rawtx"]), 0) + Alice.mining() + + hash2 = message["hash"] + rawtx2 = message["rawtx"] + + # + # decode rawtx + # + + # invalid rawtx + ec, message = mvs_rpc.decode_rawtx(rawtx + '1') + self.assertEqual(ec, code.command_params_exception, message) + + # decode rawtx + ec, message = mvs_rpc.decode_rawtx(rawtx) + self.assertEqual(ec, code.success, message) + + # decode rawtx2 + ec, message = mvs_rpc.decode_rawtx(rawtx2) + self.assertEqual(ec, code.success, message) + + self.assertEqual(message["hash"], hash2, message) + + # + # sendrawtx + # + + # invalid rawtx + ec, message = mvs_rpc.send_rawtx(rawtx + '1') + self.assertEqual(ec, code.command_params_exception, message) + + ec, message = mvs_rpc.send_rawtx(rawtx2) + self.assertEqual(ec, code.success, message) + Alice.mining() + + self.assertEqual(message, hash2, message) diff --git a/test/test-rpc-v3/TestCase/Transaction/__init__.py b/test/test-rpc-v3/TestCase/Transaction/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/Transaction/test_transaction.py b/test/test-rpc-v3/TestCase/Transaction/test_transaction.py new file mode 100644 index 000000000..91f0367b7 --- /dev/null +++ b/test/test-rpc-v3/TestCase/Transaction/test_transaction.py @@ -0,0 +1,78 @@ +import random +from TestCase.MVSTestCase import * + +class TestTransaction(MVSTestCaseBase): + + def test_0_gettx(self): + # the argument ('11111111111111111111') for option '--HASH' is invalid + ec, message = mvs_rpc.gettx('1'*20) + self.assertEqual(ec, 1021, message) + + # transaction does not exist! + ec, message = mvs_rpc.gettx('18908d2035f40a9d4f1e07c1a3f16ace0882afbd31a7dfa3176843dd620abe9d') + self.assertEqual(ec, 5306, message) + + # send etp to get a tx hash + tx_hash = Alice.send_etp(Zac.mainaddress(), 10**8) + ec, tx_json = mvs_rpc.gettx(tx_hash) + self.assertEqual(ec, 0, tx_json) + self.assertEqual(tx_json['height'], 0) + + Alice.mining() + ec, tx_json2 = mvs_rpc.gettx(tx_hash) + self.assertEqual(ec, 0, tx_json2) + self.assertNotEqual(tx_json2['height'], 0) + + tx_json2['height'] = 0 + self.assertEqual(tx_json, tx_json2) + + + def test_1_listtxs(self): + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password) + self.assertEqual(ec, 0, message) + + def test_2_listtxs(self): + # account not found or incorrect password + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password+'1') + self.assertEqual(ec, 1000, message) + + # invalid address parameter! + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, Alice.mainaddress()+'1') + self.assertEqual(ec, 4010, message) + + ec, message = mvs_rpc.get_blockheader() + self.assertEqual(ec, 0, message) + + height = message["number"] + + # height: firt < second + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, Alice.mainaddress(), [height-1, height]) + self.assertEqual(ec, 0, message) + + # height: firt == second + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, Alice.mainaddress(), [height, height]) + self.assertEqual(ec, 5103, message) + + # height: firt > second + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, Alice.mainaddress(), [height, height-1]) + self.assertEqual(ec, 5103, message) + + # index: page index parameter must not be zero + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, index=0) + self.assertEqual(ec, 2003, message) + + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, index=1) + self.assertEqual(ec, 0, message) + + # limit: page record limit parameter must not be zero + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, limit=0) + self.assertEqual(ec, 2003, message) + + # limit: page record limit must not be bigger than 100. + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, limit=101) + self.assertEqual(ec, 2003, message) + + ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, limit=100) + self.assertEqual(ec, 0, message) + + diff --git a/test/test-rpc-v3/TestCase/__init__.py b/test/test-rpc-v3/TestCase/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/main.py b/test/test-rpc-v3/main.py new file mode 100644 index 000000000..66e42741e --- /dev/null +++ b/test/test-rpc-v3/main.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +import os, sys, time, re +import unittest +import TestCase +from Roles import * +from utils import mvs_rpc +from HTMLTestRunner import HTMLTestRunner + +def clear_account(): + for role in [Alice, Bob, Cindy, Dale, Eric, Frank, Zac]: + try: + # check if the role exists by get_balance + role.get_balance() + role.delete() + except: + pass + +def ensure_Alice_balance(): + Alice.create() + try: + while Alice.get_balance() < 1000 * (10**8): + Alice.mining(100) + finally: + Alice.delete() + +def run_testcase(): + with open('mvs_test_report.html', 'w') as f: + # runner = unittest.TextTestRunner(stream=f, verbosity=2) + runner = HTMLTestRunner(stream=f, + title='MVS API Test Report', + description='', + verbosity=2) + + #category_lst = ['Account', "Blockchain", "Block", "Identity", "ETP", "Asset", "MultiSignature", "RawTx", "Transaction"] + #for i in category_lst: + test_dir = "./TestCase/" + discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py', top_level_dir="./TestCase/") + result = runner.run(discover) + + #add api call time statics + benchmark_head = ''' + ++++++++ + + + + + + +''' + benchmark_fmt = ''' + + + + + + +''' + benchmark_tail = ''' +
MVS API/MethodMax(ms)Min(ms)Average(ms)Called(times)
%s%s%s%s%s
''' + body = ''.join( [benchmark_fmt % i for i in mvs_rpc.RPC.export_method_time()] ) + runner.ENDING_TMPL += benchmark_head + body + benchmark_tail + f.seek(0) + runner.generateReport(None, result) + +def data_base_script(tag): + workdir = "./tools/%s" % tag + if os.path.isdir(workdir): + os.system("rm -rf " + workdir) + os.makedirs(workdir) + ret = os.system("cd %s && python ../data_base.py > result.txt" % workdir) + assert (ret == 0) + +def fork_test( ): + origin = "origin" + popback = "popback" + # backup check point + _, (height, _) = mvs_rpc.get_info() + data_base_script(origin) + + # run testcase + run_testcase() + + # make this node partion with the others + peers = mvs_rpc.getpeerinfo()[1] + for peer in peers: + mvs_rpc.ban_node(peer) + try: + # popblock + ec, _ = mvs_rpc.pop_block(height + 1) + assert (ec == 0) + assert( mvs_rpc.get_info()[1][0] == height) + + # check database + data_base_script(popback) + + finally: + for peer in peers: + mvs_rpc.add_node(peer) + + # check origin and popback + f_origin = open('./tools/%s/result.txt' % origin) + f_popback= open('./tools/%s/result.txt' % popback) + while True: + line1 = f_origin.readline() + line2 = f_popback.readline() + assert (line1 == line2) + + if not (line1 or line2): + break + + f_origin.close() + f_popback.close() + print "fork test passed!" + + + + + +if __name__ == '__main__': + clear_account() + ensure_Alice_balance() + if len(sys.argv) >= 2 and sys.argv[1] == "fork": + print 'backup check point -> run testcase -> popblock -> check database ' + fork_test() + else: + print 'run testcase' + run_testcase() + diff --git a/test/test-rpc-v3/resource/keystore/Alice.json b/test/test-rpc-v3/resource/keystore/Alice.json new file mode 100644 index 000000000..e44bd14e7 --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/Alice.json @@ -0,0 +1,7 @@ +{ + "algo" : "aes", + "index" : 10, + "mnemonic" : "U2FsdGVkX1/Nfr6qc7ye8yeFxoql0cuTb/HOQ4ViT3ID1bV4susHm3EjREyWnNa5PYKABEcL128aOROmmOBVFVA0CwO5/0bryuxvwfpoPbDRgU/NbZE9wxu/3LG6oCQvXe/ooL1+KS2snOkT24FBgWNzEQcVpG0CkGW2eVSbgdpQIUOCfS/3Reo45vl5W5+mIBjg5xcYUi8gpqlsmklZZLf+eB78Id6ia+KMUfwU4F4=", + "multisigs" : null, + "version" : "0.2.1" +} diff --git a/test/test-rpc-v3/resource/keystore/Bob.json b/test/test-rpc-v3/resource/keystore/Bob.json new file mode 100644 index 000000000..6b0121af5 --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/Bob.json @@ -0,0 +1,7 @@ +{ + "algo" : "aes", + "index" : 10, + "mnemonic" : "U2FsdGVkX19cs5VKQoLagSTqObJBYhWL4y2taTIqdSvdw7B2YGzU+h6jhG926OuW4yz/Yglj2mMFgs9H6Vdk2mTXpEhrOj9WajIQ3Ah6MWAGM55EWZL5INKS0tjoGYNyfxEn7lMIeiEP3lKOH0GgnUFRZrqf1Q+hEUi9lllcgah5F3rT9hxkwYij+gOrziD0CGTxLAHGS85FU4UuBhGZ9Yst5Bffab8aO2I2/0AMiEo=", + "multisigs" : null, + "version" : "0.2.1" +} diff --git a/test/test-rpc-v3/resource/keystore/Cindy.json b/test/test-rpc-v3/resource/keystore/Cindy.json new file mode 100644 index 000000000..cddcbb1fa --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/Cindy.json @@ -0,0 +1,7 @@ +{ + "algo" : "aes", + "index" : 10, + "mnemonic" : "U2FsdGVkX19tSTVPRNmoIaByBERIJVoPnjb+se94jJvRbiASWDfovkTdn2tijMO+9Ln4drndvST48mbBYYpFyuQ+5n6rezOJfZCiFh/VxK/7Fa5ofMi03I0y9W08kghMfViaXEzHBBpWE+rA1KgSVz9Jm/Y4H1xPgdRjvdYkmLtYpQrQ26YK22Yu7K5e0GiPlq3/aQSFxhZ26S6iNIbOLiLTHdkg39/kJ2auyKnyg/c=", + "multisigs" : null, + "version" : "0.2.1" +} diff --git a/test/test-rpc-v3/resource/keystore/Dale.json b/test/test-rpc-v3/resource/keystore/Dale.json new file mode 100644 index 000000000..35bfaf98b --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/Dale.json @@ -0,0 +1,7 @@ +{ + "algo" : "aes", + "index" : 10, + "mnemonic" : "U2FsdGVkX1812Dv0kNhQXBbCCAlPMHX0J1d9q/Ly3MvIhIYwoESPuSeNPaL/ymlPcrZYKXBBfu99AZ10EnjKrOWLzdhz+nFfmtXf7ziKdHtspkvdCcq3bDeyOLdbm15bxukD5c3E0vEaisZ1PVe1craMLlBWci9JRmuui6OW73MANa1gs4RtXknxyt32JlRJKLqo0076ol98+TPjNYfMMOSSSJf6Vg5aNESMQ+H5EVQ=", + "multisigs" : null, + "version" : "0.2.1" +} diff --git a/test/test-rpc-v3/resource/keystore/Eric.json b/test/test-rpc-v3/resource/keystore/Eric.json new file mode 100644 index 000000000..cdb7fc260 --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/Eric.json @@ -0,0 +1,7 @@ +{ + "algo" : "aes", + "index" : 10, + "mnemonic" : "U2FsdGVkX19T8DRbGakWDlA6MYVnGDXYJOoRkCAUu1Bg2tIw9xVqscPEe8OCndVMorR/KmXICBKGIhtK8G9qSsxmjnnJQX71vFs2l2xEA/lGZYgruT8Ocmh9ONLYYJapVMqZxsb+VX/zH/rC/2e+PwkVVYkuRaI4h3IV9f5lXJl0pIycXpu+jZOmeXkATfcbVSxP3u03KtWaxFgHDMzCsYZcFF+PDp83Y044Yr24EWo=", + "multisigs" : null, + "version" : "0.2.1" +} diff --git a/test/test-rpc-v3/resource/keystore/Frank.json b/test/test-rpc-v3/resource/keystore/Frank.json new file mode 100644 index 000000000..ccbb230d1 --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/Frank.json @@ -0,0 +1,7 @@ +{ + "algo" : "aes", + "index" : 10, + "mnemonic" : "U2FsdGVkX1/r5ld5qfryydVBwyqebiyCfVfxLSZdCd4lraCkx1rcfrhSfw4o2HXsnByGByq0eJqUJPaCZrYiBliKX0wMxX3cyv8dYFc5dzmJ/pjESbmXiY1pOALGPeVnvFqrb0Arg5vsyuGRL2CJtwUCINH8WQx7SmkqshsMJncqdPiiqSRzxUUwJnfSNOCq+GOVCbhUZGxYho/JW3dWoTqZeFzdqDPuR8CZZ1HBMy1/zP5GVF/K90TyzchZOPUS", + "multisigs" : null, + "version" : "0.2.1" +} diff --git a/test/test-rpc-v3/resource/keystore/mvs_tkggfngu.json b/test/test-rpc-v3/resource/keystore/mvs_tkggfngu.json new file mode 100644 index 000000000..91e7ace3e --- /dev/null +++ b/test/test-rpc-v3/resource/keystore/mvs_tkggfngu.json @@ -0,0 +1 @@ +{"version":"0.2.1","algo":"aes","index":10,"mnemonic":"U2FsdGVkX19SDYfqsVHB8xL3uWTLDS2FIK4VjrXuwaXSFMGy+uyN+OwIlo0ceG8nVvpMpWihv1fmMSfwe0e3yKUem3k9MPz+2kba7u92KJCfd1yNB+UmQvcjMNCCHvQStaQJCQ0VRkFwyGO8DNVKFmIH9iNNRU5M1hX26RUbaw7R4tSVh9DNThazx4uAykZ6/EvRkNyI30sPnNrLsBR1xdubRTja/sjF9Afo5+tb9kE="} \ No newline at end of file diff --git a/test/test-rpc-v3/tools/data_base.py b/test/test-rpc-v3/tools/data_base.py new file mode 100644 index 000000000..763a56916 --- /dev/null +++ b/test/test-rpc-v3/tools/data_base.py @@ -0,0 +1,809 @@ +import struct +import zlib +import os, sys +str2int_map = { + 1: ' 0xFFFFFFFF: + assert (next < self.max_record_count) + + + self.fr.seek( 4 + self.record_size * next ) + next_ = str2int( self.fr.read(4) ) + value = self.fr.read(self.record_size-4) + + if next == next_: # self recursive + next = 0xFFFFFFFF + else: + next = next_ + + self.rows += 1 + if next == 0xFFFFFFFF: + self.fw.write(int2str(0xFFFFFFFF, 4)) + else: + self.fw.write(int2str(self.rows, 4)) + self.fw.write(value) + +class account_table: + def __init__(self): + self.filename = 'account_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + def re_arrange(self, table_dir): + fw_head = open("./%s_head" % self.filename, 'wb') + fw_body = open("./%s_body" % self.filename, 'wb') + fr_table= open(table_dir + '/' + self.filename, 'rb') + + hm = HeadManager(fw_head, self.header.size_of_offset) + + account_info = self.header.parse_header(fr_table) + + fw_head.write(int2str(self.header.bucket_size, 4)) + + payload_size = self.header.size_of_offset + for index, offset in account_info: + hm.append_slot(index, payload_size) + + next = offset + + while next != hm.INT_OFFSET_NULL: + fr_table.seek(self.header.offset_begin + next) + key = fr_table.read(self.slab.size_of_key) + next = str2int( fr_table.read(self.slab.size_of_next) ) + begin, end = self.slab.parse_value_func(fr_table) + + payload_size += self.slab.size_of_key + self.slab.size_of_next + (end - begin) + + fw_body.write(key) + if next <> hm.INT_OFFSET_NULL: + fw_body.write(int2str(payload_size, self.header.size_of_offset)) + else: + fw_body.write(hm.STR_OFFSET_NULL) + + + + fr_table.seek( begin ) + fw_body.write( fr_table.read( end - begin ) ) + hm.append_slot(self.header.bucket_size - 1, hm.INT_OFFSET_NULL) + fw_head.write('\x00' * self.header.extra_bytes) + fw_head.write( int2str(payload_size, self.header.size_of_offset) ) + + fr_table.close() + fw_head.close() + fw_body.close() + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + + extra_padding = [] + account_name_len = get_var_len(ff, extra_padding.append) + assert (0 < account_name_len <= 64) + account_name = ff.read(account_name_len) + mnemonic_len = get_var_len(ff, extra_padding.append) + if mnemonic_len <> 0: + assert (mnemonic_len % 16 == 1) + # print "mnemonic_len:", mnemonic_len + mnemonic = ff.read(mnemonic_len) + # print "mnemonic", to_string(mnemonic) + passwd_hash = ff.read(32) + hd_index = str2int(ff.read(4)) + # print 'hd_index:', hd_index + priority = ff.read(1) + type = ff.read(1) + status = ff.read(1) + + if type == 1: # multisig account + vec_size = str2int(ff.read(4)) + #print '\tvec_size', vec_size + for i in range(vec_size): + hd = str2int(ff.read(4)) + id = str2int(ff.read(4)) + m = ord(ff.read(1)) + n = ord(ff.read(1)) + l = get_var_len(ff, extra_padding.append) + pubkey = ff.read(l) + size_ = ord(ff.read(1)) + for j in range(size_): + l = get_var_len(ff, extra_padding.append) + cosigner_pubkey = ff.read(l) + desc = ff.read(get_var_len(ff, extra_padding.append)) + address = ff.read(get_var_len(ff, extra_padding.append)) + + end = ff.tell() + sum(extra_padding) + + return begin, end + + def query(self, key, table_dir): + def std_hash(any): + seed = 0 + for i in any: + i = ord(i) + seed ^= i + 0x9e3779b9 + (seed << 6) + (seed >> 2) + seed &= 0xFFFFFFFFFFFFFFFF + return seed + #print std_hash(hash_) + key = to_bin(key) + + with open(table_dir + '/' + self.filename, 'rb') as fr_table: + self.header.parse_header(fr_table, False) + + index = std_hash(key) % self.header.bucket_size + + fr_table.seek( 4 + index * self.header.size_of_offset ) + offset = fr_table.read( self.header.size_of_offset ) + while offset <> '\xFF' * self.header.size_of_offset: + offset = str2int(offset) + assert (offset < self.header.payload_size) + + fr_table.seek(self.header.offset_begin + offset) + key_, next_ = fr_table.read(self.slab.size_of_key), fr_table.read(self.slab.size_of_next) + if key_ == key: + begin, end = self.parse_value_func(fr_table) + fr_table.seek(begin) + return fr_table.read(end - begin) + offset = next_ + + +class asset_table(account_table): + def __init__(self): + self.filename = 'asset_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + version_ = str2int( ff.read(4) ) + hash_ = ff.read(32) + index_ = str2int( ff.read(4) ) + height_ = str2int( ff.read(8) ) + + extra_padding = [] + symbol_len = get_var_len(ff, extra_padding.append) + assert (0 < symbol_len <= 64) + + symbol_name = ff.read(symbol_len) + maximum_supply = str2int(ff.read(8)) + decimal_number = str2int(ff.read(1)) + secondaryissue_threshold = str2int(ff.read(1)) + unused2 = str2int(ff.read(1)) + unused3 = str2int(ff.read(1)) + + assert (unused2 == unused3 == 0) + + issuer_len = get_var_len(ff, extra_padding.append) + assert (0 < issuer_len <= 64) + issuer_name = ff.read(issuer_len) + + address_len = get_var_len(ff, extra_padding.append) + assert (0 < address_len <= 64) + address = ff.read(address_len) + + description_len = get_var_len(ff, extra_padding.append) + assert (0 <= description_len <= 100) # optional + description = ff.read(description_len) + + end = ff.tell() + return begin, end + +class mit_table(account_table): + def __init__(self): + self.filename = 'mit_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + extra_padding = [] + + output_height = ff.read(4) + timestamp = ff.read(4) + + length = get_var_len(ff, extra_padding.append) + assert (0 < length <= 64) + to_did = ff.read(length) + + status_ = str2int( ff.read(1) ) + assert (0<= status_ < 4) + + length = get_var_len(ff, extra_padding.append) + assert (0 < length <= 64) + symbol_ = ff.read(length) + + length = get_var_len(ff, extra_padding.append) + assert (0 < length <= 64) + address_ = ff.read(length) + + if status_ == 1: + length = get_var_len(ff, extra_padding.append) + assert (0 <= length <= 256) + content_ = ff.read(length) + + end = ff.tell() + return begin, end + +class cert_table(account_table): + def __init__(self): + self.filename = 'cert_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + + extra_padding = [] + + symbol_len = get_var_len(ff, extra_padding.append) + assert (0 < symbol_len <= 64) + symbol_ = ff.read(symbol_len) + + owner_len = get_var_len(ff, extra_padding.append) + assert (0 < owner_len <= 64) + owner_ = ff.read(owner_len) + + address_len = get_var_len(ff, extra_padding.append) + assert (0 < address_len <= 64) + address_ = ff.read(address_len) + + cert_type_ = str2int( ff.read(4) ) + assert (0 <= cert_type_ <= 3) + status_ = str2int( ff.read(1) ) + assert (0 <= status_ <= 3) + + end = ff.tell() + return begin, end + +class did_table(account_table): + def __init__(self): + self.filename = 'did_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + + version_ = str2int(ff.read(4)) + hash_ = ff.read(32) + index_ = str2int(ff.read(4)) + height_ = str2int(ff.read(8)) + status_ = str2int(ff.read(4)) + assert (0 < status_ <= 2) + + symbol_len = str2int(ff.read(1)) + assert (0 < symbol_len <= 64) + symbol = ff.read(symbol_len) + address_len = str2int(ff.read(1)) + assert (0 < address_len <= 64) + address = ff.read(address_len) + + end = ff.tell() + return begin, end + +class transaction_table(account_table): + def __init__(self): + self.filename = 'transaction_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + @classmethod + def parse_etp(cls, ff): + pass + + @classmethod + def parse_etp_award(cls, ff): + height = ff.read(8) + + @classmethod + def parse_asset(cls, ff): + extra_padding = [] + def asset_detail(ff): + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 64) + symbol = ff.read(var_len) + maximum_supply = ff.read(8) + decimal_number = ff.read(1) + secondaryissue_threshold = ff.read(1) + unused2 = str2int( ff.read(1) ) + unused3 = str2int( ff.read(1) ) + + assert (0 == unused2 == unused3) + + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 64) + issuer = ff.read(var_len) + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 64) + address = ff.read(var_len) + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 100) + description = ff.read(var_len) + + def asset_transfer(ff): + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 64) + symbol = ff.read(var_len) + + quantity = ff.read(8) + status = str2int( ff.read(4) ) + if status == 1: + asset_detail(ff) + elif status == 2: + asset_transfer(ff) + else: + assert (False) + + @classmethod + def parse_message(cls, ff): + extra_padding = [] + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 300) + message = ff.read(var_len) + + @classmethod + def parse_did(cls, ff): + status = str2int( ff.read(4) ) + assert (0 < status <= 2) + extra_padding = [] + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + symbol = ff.read(var_len) + + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + address = ff.read(var_len) + + + + @classmethod + def parse_asset_cert(cls, ff): + extra_padding = [] + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + symbol = ff.read(var_len) + + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + owner = ff.read(var_len) + + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + address = ff.read(var_len) + + cert_type = str2int( ff.read(4) ) + assert (0 <= cert_type <= 3) + + status = str2int( ff.read(1) ) + assert (0 <= status <= 3) + + @classmethod + def parse_asset_mit(cls, ff): + status = str2int( ff.read(1) ) + + extra_padding = [] + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + symbol = ff.read(var_len) + + var_len = get_var_len(ff, extra_padding.append) + assert (0 < var_len <= 64) + address = ff.read(var_len) + + assert (0 < (status % 128) <= 2 ) + + if status == 1: + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 256) + content = ff.read(var_len) + + + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + + attachment_parser = { + 0: cls.parse_etp, + 1: cls.parse_etp_award, + 2: cls.parse_asset, + 3: cls.parse_message, + 4: cls.parse_did, + 5: cls.parse_asset_cert, + 6: cls.parse_asset_mit, + } + + hight = str2int( ff.read(4) ) + index = str2int( ff.read(4) ) + assert (index < 1000) + + # transaction + extra_padding = [] + version = str2int( ff.read(4) ) + assert (version < 5) + input_size = get_var_len(ff, extra_padding.append) + assert (input_size < 700) # max input ~ 667 ? + for i in xrange(input_size): + #previous_output + utxo_hash_ = ff.read(32) + utxo_index = str2int( ff.read(4) ) + + #scprit + script_len = get_var_len(ff, extra_padding.append) + assert (script_len < 1000) + script = ff.read(script_len) + #seq + sequence = str2int( ff.read(4) ) + + output_size = get_var_len(ff, extra_padding.append) + assert (output_size < 70) + for i in xrange(output_size): + amount = str2int( ff.read(8) ) + script_len = get_var_len(ff, extra_padding.append) + assert (script_len < 256) + script = ff.read(script_len) + + #attach data + attach_version = str2int( ff.read(4) ) + attach_type = str2int( ff.read(4) ) + if attach_version == 207: + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 64) + todid = ff.read(var_len) + + var_len = get_var_len(ff, extra_padding.append) + assert (0 <= var_len <= 64) + fromdid = ff.read(var_len) + # assert attach_type in attachment_parser + attachment_parser[attach_type](ff) + + locktime = str2int( ff.read(4) ) + end = ff.tell() + return begin, end + +class block_table(account_table): + def __init__(self): + self.filename = 'block_table' + self.header = Header(8, 4) + self.slab = Slab(32, 8, self.parse_value_func) + + @classmethod + def parse_value_func(cls, ff): + begin = ff.tell() + + extra_padding = [] + # header + #### 4 byte version + version = ff.read(4) + previous_block_hash = ff.read(32) + merkle = ff.read(32) + timestamp = ff.read(4) + bits = ff.read(32) + nonce = ff.read(8) + mixhash = ff.read(32) + + number = ff.read(4) + # header end + + height32 = ff.read(4) + tx_count32 = str2int( ff.read(4) ) + + # transactions + for i in xrange(tx_count32): + tx_hash = ff.read(32) + + end = ff.tell() + return begin, end + +class address_did_table: + def __init__(self): + self.table_filename = 'address_did_table' + self.row_filename = 'address_did_row' + + self.size_of_record = 219 # record row size + + self.header = Header(4, 0) + self.record = Record(20, 4, 4) + + def re_arrange(self, table_dir): + fw_head = open("./%s_head" % self.table_filename, 'wb') + fw_body = open("./%s_body" % self.table_filename, 'wb') + fw_rows = open("./%s_rows" % self.table_filename, 'wb') + fr_table= open(table_dir + '/' + self.table_filename, 'rb') + fr_rows = open(table_dir + '/' + self.row_filename, 'rb') + + rm = RowsManager(self.size_of_record, fr_rows, fw_rows) + hm = HeadManager(fw_head, self.header.size_of_offset) + + address_did_info = self.header.parse_header(fr_table) + # record's offset begin pos is different from slab + self.header.offset_begin += self.header.size_of_offset + + fw_head.write(int2str(self.header.bucket_size, 4)) + + #print 'bucket_size:', self.header.bucket_size + #print 'payload_size:', self.header.payload_size + #print 'offset_begin:', self.header.offset_begin + + payload_size = 0 + for index, offset in address_did_info: + hm.append_slot(index, payload_size) + + next = offset + + while next != hm.INT_OFFSET_NULL: + assert (next < self.header.payload_size) + + fr_table.seek(self.header.offset_begin + next * self.record.record_size) + key = fr_table.read(self.record.size_of_key) + next = str2int( fr_table.read(self.record.size_of_next) ) + + value = fr_table.read(self.record.size_of_value) + + payload_size += 1 + + fw_body.write(key) + if next <> hm.INT_OFFSET_NULL: + fw_body.write(int2str(payload_size, self.header.size_of_offset)) + else: + fw_body.write(hm.STR_OFFSET_NULL) + + fw_body.write( int2str(rm.rows, self.record.size_of_value) ) + rm.append_row(str2int(value)) + print 'total rows:', rm.rows + assert (rm.rows <= rm.max_record_count) + + hm.append_slot(self.header.bucket_size - 1, hm.INT_OFFSET_NULL) + fw_head.write('\x00' * self.header.extra_bytes) + fw_head.write( int2str(payload_size, self.header.size_of_offset) ) + + fr_table.close() + fr_rows.close() + fw_head.close() + fw_body.close() + fw_rows.close() + +class account_address_table(address_did_table): + def __init__(self): + self.table_filename = 'account_address_table' + self.row_filename = 'account_address_rows' + + self.size_of_record = 365 # record row size + + self.header = Header(4, 0) + self.record = Record(20, 4, 4) + +class address_asset_table(address_did_table): + def __init__(self): + self.table_filename = 'address_asset_table' + self.row_filename = 'address_asset_row' + + self.size_of_record = 359 # record row size + + self.header = Header(4, 0) + self.record = Record(20, 4, 4) + +class address_mit_table(address_did_table): + def __init__(self): + self.table_filename = 'address_mit_table' + self.row_filename = 'address_mit_row' + + self.size_of_record = 220 # record row size + + self.header = Header(4, 0) + self.record = Record(20, 4, 4) + +class history_table(address_did_table): + def __init__(self): + self.table_filename = 'history_table' + self.row_filename = 'history_rows' + + self.size_of_record = 85 # record row size + + self.header = Header(4, 0) + self.record = Record(20, 4, 4) + +class mit_history_table(address_did_table): + def __init__(self): + self.table_filename = 'mit_history_table' + self.row_filename = 'mit_history_row' + + self.size_of_record = 237 # record row size + + self.header = Header(4, 0) + self.record = Record(20, 4, 4) + +class spend_table(address_did_table): + def __init__(self): + self.table_filename = 'spend_table' + self.row_filename = '' + + self.size_of_record = 0 # record row size + + self.header = Header(4, 0) + self.record = Record(36, 4, 36) + +all_tables = [account_table, asset_table, cert_table, did_table, transaction_table, block_table, mit_table, + address_did_table, account_address_table, address_asset_table, address_mit_table, history_table, mit_history_table] + +def GetFileMd5(filename): + import hashlib + if not os.path.isfile(filename): + return + myhash = hashlib.md5() + f = file(filename,'rb') + while True: + b = f.read(8096) + if not b : + break + myhash.update(b) + f.close() + return myhash.hexdigest() + +def crc32(filepath): + block_size = 1024 * 1024 + crc = 0 + + try: + fd = open(filepath, 'rb') + while True: + buffer = fd.read(block_size) + if len(buffer) == 0: # EOF or file empty. return hashes + fd.close() + if sys.version_info[0] < 3 and crc < 0: + crc += 2 ** 32 + return crc + crc = zlib.crc32(buffer, crc) + except Exception as e: + if sys.version_info[0] < 3: + error = unicode(e) + else: + error = str(e) + return 0, error + +if __name__ == "__main__": + import getpass + user = getpass.getuser() + mainnet_dir = '/home/%s/.metaverse/mainnet/' % user + #at = transaction_table() + #at.re_arrange('/home/czp/.metaverse/mainnet/') + #import pdb;pdb.set_trace() + #data = at.query("422e2ea8ac1d333b61672044ad7e83b384c7ee621632fbe83dca0510278f7616", mainnet_dir) + #print to_string(data) + + for table in all_tables: #[address_asset_table]: + t = table() + print "begin to re_arrage: ", t.__class__ + t.re_arrange(mainnet_dir) + + ret = {} + for i in os.listdir('.'): + if i[-5:] in ("_head", "_body", "_rows"): + ret[i] = GetFileMd5(i) + print ret \ No newline at end of file diff --git a/test/test-rpc-v3/tools/parse_cmd.py b/test/test-rpc-v3/tools/parse_cmd.py new file mode 100644 index 000000000..c7bb89fee --- /dev/null +++ b/test/test-rpc-v3/tools/parse_cmd.py @@ -0,0 +1,371 @@ +import os +import re + +cmd_dir = '/home/czp/GitHub/nova/metaverse/include/metaverse/explorer/extensions/commands' + +def get_paraname(line): + pattern = '"([/\w]+)(,\w+){0,1}"' + m = re.search(pattern, line) + if m: + ret = m.groups()[0] + else: + ret = line.strip() + return ret + +def get_paradef(line): + pattern = "value\\<([\s\S]+)\\>" + ret = {} + paradef = line.split('->') + m = re.search(pattern, paradef[0]) + ret["para_type"] = m.groups()[0] + for i in paradef[1:]: + if 'required' in i: + ret["must_give"] = True + elif 'default_value' in i: + pattern = 'default_value\\(([\s\S]+)\\)' + + m = re.search(pattern, i) + ret["default_value"] = m.groups()[0].strip() + elif 'zero_tokens' in i: + ret["just_token"] = True + else: + print i + assert False + return ret + +def macro_expand(input): + convert = { + 'BX_ACCOUNT_NAME' : "Account name required.", + 'BX_ACCOUNT_AUTH' : "Account password(authorization) required.", + 'BX_MST_OFFERING_CURVE' : '''The token offering model by block height. + TYPE=1 - fixed quantity model; TYPE=2 - specify parameters; + LQ - Locked Quantity each period; + LP - Locked Period, numeber of how many blocks; + UN - Unlock Number, number of how many LPs; + eg: + TYPE=1;LQ=9000;LP=60000;UN=3 + TYPE=2;LQ=9000;LP=60000;UN=3;UC=20000,20000,20000;UQ=3000,3000,3000 + defaults to disable.''', + 'BX_ADMIN_NAME' : "Administrator required.(when administrator_required in mvs.conf is set true)", + 'BX_ADMIN_AUTH' : "Administrator password required.", + } + + return convert.get(input, input) + +def parse_cmd(filename): + starts = 'BX_HELP_VARIABLE ",h",' + + positional = [] + buff = [] + with open(filename) as fr: + for line in fr: + if 'return get_argument_metadata()' in line: + positional.append(line) + if not line.strip().endswith(';'): + for line in fr: + positional.append(line) + if line.strip().endswith(';'): + break + + + if 'symbol' in line: + pattern = '{[ ]+return[ ]+"(\w+)"[ ]*;[ ]*}' + m = re.search(pattern, line) + if m: + cmdname = m.groups()[0] + + if line.strip() == starts: + for line in fr: + buff.append(line) + if line.strip().endswith(';'): + break + break + + positional = ''.join(positional) + pattern = '\\.add\\("([^,]+)",[ ]*([-]?\d+)\\)' + + pos_params = [] + for m in re.finditer(pattern, positional): + params = m.groups()[0] + pos_params.append(params) + + + buff = ''.join(buff) + pattern = '\s+\\(\n([\s\S]+?)\s+\\)[;\n]{1}' + + param_details = [] + for m in re.finditer(pattern, buff): + #print buff[m.start(): m.end()] + params = m.groups()[0].split(',\n') + assert len(params) == 3 + param_name = get_paraname(params[0]) + param_def = get_paradef(params[1]) + param_desc = params[2].strip().replace('\\\n', '') + + param_desc = macro_expand(param_desc) + param_details.append( (param_name, param_def, param_desc) ) + + # generate SDK + #generate_py_SDK(cmdname, pos_params, param_details) + #generate_go_SDK(cmdname, pos_params, param_details) + generate_DotNet_SDK(cmdname, pos_params, param_details) + +def generate_py_SDK(cmdname, pos_params, params): + template = ''' +@mvs_api_v2 +def %(cmdname)s(%(argument_defines)s): + \'\'\' +%(comments)s + \'\'\' + positional=[%(positional_arguments)s] + optional={ + %(optional_arguments)s + } + return '%(cmdname)s', positional, optional +''' + def cpp2py_type(cpp_type): + convert = { + 'uint8_t' : 'int', + 'uint16_t': 'int', + 'uint32_t': 'int', + 'std::uint32_t' : 'int', + 'uint64_t': 'int', + + 'int8_t': 'int', + 'int16_t': 'int', + 'int32_t': 'int', + 'std::int32_t': 'int', + 'int64_t': 'int', + + 'non_negative_uint64' : 'int', + + 'std::string' : 'str', + 'bool': 'bool', + 'explorer::config::transaction' : 'str', + 'std::vector' : '[str1, str2, ...]', + 'explorer::config::language' : "string of hexcode", + + 'libbitcoin::explorer::commands::colon_delimited2_item' : "(int_low, int_high)", + 'bc::config::hash256' : "string of hash256", + 'boost::filesystem::path': 'string of file path', + 'bc::wallet::payment_address': 'string of Base58-encoded public key address', + } + return convert[cpp_type] + + comments = [] + argument_defines_1 = [] # with no default valie + argument_defines_2 = [] # with default value + optional_arguments = [] + positional_arguments = [] + for param_name, param_def, param_desc in params: + comments.append(" :param: %s(%s): %s" % (param_name, cpp2py_type(param_def["para_type"]), param_desc)) + if param_def.get("must_give", False) == False: + argument_defines_2.append('%s=None' % param_name) + else: + argument_defines_1.append('%s' % param_name) + + if param_name in pos_params: + if param_def["para_type"] == 'std::vector': + positional_arguments.append("' '.join(%s)" % param_name) + else: + positional_arguments.append(param_name) + continue + + if "colon_delimited2_item" in param_def["para_type"]: + optional_arguments.append( + '"%s" : "%%s:%%s" %% (%s[0], %s[1]),' % (param_name, param_name, param_name) + ) + else: + optional_arguments.append('"%s" : %s,' % (param_name, param_name)) + + paras = { + 'comments': '\n'.join(comments), + 'cmdname': cmdname, + 'argument_defines': ', '.join(argument_defines_1 + argument_defines_2), + 'optional_arguments': '\n '.join(optional_arguments), + 'positional_arguments': ', '.join(positional_arguments), + } + + print template % paras + +def generate_go_SDK(cmdname, pos_params, params): + template = ''' +/* +%(comments)s +*/ +func (r *RPCClient) %(func_name)s(%(argument_defines)s) (*JSONRpcResp, error) { + cmd := "%(cmdname)s" + + optional := map[string]interface{}{ + %(optional_arguments)s + } + args := []interface{}{%(positional_arguments)s optional} + return r.doPost(r.Url, cmd, args) +} +''' + def cpp2go_type(cpp_type): + convert = { + 'uint8_t' : 'uint8', + 'uint16_t': 'uint16', + 'uint32_t': 'uint32', + 'std::uint32_t' : 'uint32', + 'uint64_t': 'uint64', + + 'int8_t': 'int8', + 'int16_t': 'int16', + 'int32_t': 'int32', + 'std::int32_t': 'int32', + 'int64_t': 'int64', + + 'non_negative_uint64' : 'uint64', + + 'std::string' : 'string', + 'bool': 'bool', + 'explorer::config::transaction' : 'string', + 'std::vector' : '[]string', + 'explorer::config::language' : 'string', + + 'libbitcoin::explorer::commands::colon_delimited2_item' : "[2]uint64", + 'bc::config::hash256' : "string", + 'boost::filesystem::path': 'string', + 'bc::wallet::payment_address': 'string', + } + return convert[cpp_type] + + def special_type_desc(cpp_type): + convert = { + 'explorer::config::transaction': "string of hexcode", + 'std::vector': 'list of string', + 'libbitcoin::explorer::commands::colon_delimited2_item' : 'a range expressed by 2 integers', + 'bc::config::hash256' : 'string of hash256', + 'boost::filesystem::path' : 'string of file path', + 'bc::wallet::payment_address' : 'string of Base58-encoded public key address', + } + + return convert.get(cpp_type, cpp_type) + + + comments = [] + argument_defines = [] + optional_arguments = [] + positional_arguments = [] + for param_name, param_def, param_desc in params: + comments.append(" :param: %s(%s): %s" % (param_name, special_type_desc( param_def["para_type"] ), param_desc) ) + argument_defines.append( '%s %s' % (param_name, cpp2go_type( param_def["para_type"] ) ) ) + + if param_name in pos_params: + if param_def["para_type"] == 'std::vector': + positional_arguments.append('strings.Join(%s, " ")' % param_name) + else: + positional_arguments.append(param_name) + continue + if "colon_delimited2_item" in param_def["para_type"]: + optional_arguments.append('"%s" : strings.Join([]string{strconv.FormatUint(uint64(%s[0]), 10), strconv.FormatUint(uint64(%s[1]), 10)}, ":"),' % (param_name, param_name, param_name)) + else: + optional_arguments.append('"%s" : %s,' % (param_name, param_name) ) + + paras = { + 'comments' : '\n'.join(comments), + 'func_name' : cmdname.title(), + 'cmdname' : cmdname, + 'argument_defines' : ', '.join(argument_defines), + 'optional_arguments' : '\n '.join(optional_arguments), + 'positional_arguments' : ' '.join([i+',' for i in positional_arguments]), + } + + print template % paras + + +def generate_DotNet_SDK(cmdname, pos_params, params): + template = ''' +/* +%(comments)s +*/ +public String %(func_name)s(%(argument_defines)s) +{ + List parameters = new List() { %(positional_arguments)s }; + %(add_optional_parameters)s + return getResult("%(func_name)s", parameters); +} +''' + def cpp2dotnet_type(cpp_type): + convert = { + 'uint8_t' : 'UInt8', + 'uint16_t': 'UInt16', + 'uint32_t': 'UInt32', + 'std::uint32_t' : 'UInt32', + 'uint64_t': 'UInt64', + + 'int8_t': 'Int8', + 'int16_t': 'Int16', + 'int32_t': 'Int32', + 'std::int32_t': 'Int32', + 'int64_t': 'Int64', + + 'non_negative_uint64' : 'UInt64', + + 'std::string' : 'String', + 'bool': 'Boolean', + 'explorer::config::transaction' : 'String', + 'std::vector' : 'List', + 'explorer::config::language' : 'String', + + 'libbitcoin::explorer::commands::colon_delimited2_item' : "Tuple", + 'bc::config::hash256' : "String", + 'boost::filesystem::path': 'String', + 'bc::wallet::payment_address': 'String', + } + return convert[cpp_type] + + def special_type_desc(cpp_type): + convert = { + 'explorer::config::transaction': "string of hexcode", + 'std::vector': 'list of string', + 'libbitcoin::explorer::commands::colon_delimited2_item' : 'a range expressed by 2 integers', + 'bc::config::hash256' : 'string of hash256', + 'boost::filesystem::path' : 'string of file path', + 'bc::wallet::payment_address' : 'string of Base58-encoded public key address', + } + + return convert.get(cpp_type, cpp_type) + comments = [] + argument_defines = [] + optional_arguments = [] + positional_arguments = [] + for param_name, param_def, param_desc in params: + comments.append(" :param: %s(%s): %s" % (param_name, special_type_desc(param_def["para_type"]), param_desc)) + argument_defines.append('%s %s' % (cpp2dotnet_type(param_def["para_type"]), param_name)) + + if param_name in pos_params: + if cpp2dotnet_type(param_def["para_type"]) == 'String': + positional_arguments.append(param_name) + elif cpp2dotnet_type(param_def["para_type"]) == 'List': + positional_arguments.append('String.Join(" ", %s.ToArray())' % param_name) + else: + positional_arguments.append('%s.ToString()' % param_name) + continue + if "vector<" in param_def["para_type"]: + optional_arguments.append('foreach (var i in %s) {' % param_name) + optional_arguments.append(' parameters.AddRange(new List{"--%s", i.ToString()});' % param_name) + optional_arguments.append('}') + else: + if "colon_delimited2_item" in param_def["para_type"]: + value = 'parameters.AddRange(new List{"--%s", String.Format("{0}:{1}", %s.Item1, %s.Item2)});' % (param_name, param_name, param_name) + else: + value = 'parameters.AddRange(new List{"--%s", %s.ToString()});' % (param_name, param_name) + optional_arguments.append(value) + + paras = { + 'comments': '\n'.join(comments), + 'func_name': cmdname, + 'argument_defines': ', '.join(argument_defines), + 'add_optional_parameters': '\n '.join(optional_arguments), + 'positional_arguments': ', '.join(positional_arguments), + } + + print template % paras + +if __name__ == "__main__": + for root, dirs, files in os.walk(cmd_dir): + for file in files: + parse_cmd(root + '/' + file) \ No newline at end of file diff --git a/test/test-rpc-v3/utils/__init__.py b/test/test-rpc-v3/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/utils/code.py b/test/test-rpc-v3/utils/code.py new file mode 100644 index 000000000..65951e5a5 --- /dev/null +++ b/test/test-rpc-v3/utils/code.py @@ -0,0 +1,146 @@ +# return code + +success = 0 + +unknown_error_exception = 500 +fatal_exception = 1001 +connection_exception = 1011 +session_expired_exception = 1012 + +invalid_command_exception = 1020 +command_params_exception = 1021 +command_platform_compat_exception = 1022 +ui_invoke_explorer_exception = 1023 +setting_required_exception = 1024 +block_sync_required_exception = 1025 + + + +argument_exceed_limit_exception = 2001 +argument_size_invalid_exception = 2002 +argument_legality_exception = 2003 +argument_dismatch_exception = 2004 + +account_existed_exception = 3001 +account_authority_exception = 3002 +account_notfound_exception = 3003 + + +account_name_exception = 3201 +account_length_exception = 3202 +account_address_get_exception = 3203 + +account_deposit_period_exception = 3301 +account_balance_lack_exception = 3302 + + +address_list_empty_exception = 4001 +address_list_nullptr_exception = 4002 +address_dismatch_account_exception = 4003 +address_amount_exception = 4004 +address_notfound_exception = 4005 +address_generate_exception = 4005 + + +address_invalid_exception = 4010 +toaddress_empty_exception = 4011 +toaddress_invalid_exception = 4012 +toaddress_unrecognized_exception = 4013 +fromaddress_empty_exception = 4014 +fromaddress_invalid_exception = 4015 +fromaddress_unrecognized_exception = 4016 + +asset_lack_exception = 5001 +asset_amount_exception = 5002 +asset_notfound_exception = 5003 +asset_type_exception = 5004 +asset_exchange_poundage_exception = 5005 +asset_issue_poundage_exception = 5006 +asset_description_length_exception = 5007 +asset_symbol_duplicate_exception = 5008 +asset_symbol_existed_exception = 5009 +asset_symbol_notfound_exception = 5010 +asset_symbol_length_exception = 5011 +asset_symbol_name_exception = 5012 +asset_issued_not_delete = 5013 +asset_delete_fail = 5014 +asset_secondaryissue_threshold_exception = 5015 +asset_attenuation_model_exception = 5016 +asset_cert_exception = 5017 +asset_cert_existed_exception = 5018 +asset_cert_notfound_exception = 5019 +asset_cert_notowned_exception = 5020 +asset_cert_domain_exception = 5021 + +etp_lack_exception = 5051 + +block_height_get_exception = 5101 +block_last_height_get_exception = 5102 +block_height_exception = 5103 +block_hash_get_exception = 5104 +block_header_get_exception = 5105 + +multisig_cosigne_exception = 5201 +multisig_exist_exception = 5202 +multisig_notfound_exception = 5203 +multisig_script_exception = 5204 +multisig_index_exception = 5205 +signature_amount_exception = 5220 +pubkey_amount_exception = 5230 +pubkey_dismatch_exception = 5231 +prikey_notfound_exception = 5232 +pubkey_notfound_exception = 5233 + +tx_io_exception = 5301 +tx_source_exception = 5302 +tx_sign_exception = 5303 +tx_validate_exception = 5304 +tx_broadcast_exception = 5305 +tx_notfound_exception = 5306 +tx_attachment_value_exception = 5307 +tx_fetch_exception = 5308 +tx_send_exception = 5309 +tx_encode_get_exception = 5310 +tx_decode_get_exception = 5311 +tx_timestamp_exception = 5312 +tx_locktime_exception = 5313 + +utxo_fetch_exception = 5401 + +redeem_script_empty_exception = 5501 +redeem_script_data_exception = 5502 +redeem_script_pattern_exception = 5503 + + +encode_exception = 6001 +ec_to_address_exception = 6002 +ec_to_public_exception = 6003 + +did_symbol_name_exception = 7001 +did_symbol_existed_exception = 7002 +did_symbol_length_exception = 7003 +did_description_length_exception = 7004 +did_register_poundage_exception = 7005 +did_symbol_notfound_exception = 7006 +did_symbol_duplicate_exception = 7007 +did_address_needed_exception = 7008 +did_symbol_notowned_exception = 7009 +did_multisig_address_exception = 7010 + +seed_exception = 9001 +seed_size_exception = 9001 +seed_length_exception = 9002 + +hd_length_exception = 9101 +hd_key_exception = 9102 +hd_new_exception = 9103 +hd_private_new_exception = 9104 +hd_to_ec_exception = 9105 + +mnemonicwords_amount_exception = 9201 +mnemonicwords_content_exception = 9202 +mnemonicwords_new_exception = 9203 +mnemonicwords_to_seed_exception = 9204 +mnemonicwords_dismatch_exception = 9205 +mnemonicwords_empty_exception = 9206 +mnemonicwords_existed_exception = 9207 \ No newline at end of file diff --git a/test/test-rpc-v3/utils/common.py b/test/test-rpc-v3/utils/common.py new file mode 100644 index 000000000..99d5372b9 --- /dev/null +++ b/test/test-rpc-v3/utils/common.py @@ -0,0 +1,85 @@ +import os +import pwd +import time +import random + +from datetime import datetime +import struct + +str2int_map = { + 1: '!B', + 2: '!H', + 4: '!L', + 8: '!Q', +} + +def str2int(s): + global str2int_map + return struct.unpack(str2int_map[len(s)], s)[0] + +def int2str(i, size): + global str2int_map + return struct.pack(str2int_map[size], i) + +hex_string='0123456789abcdef' +char2i = {} +for i, char in enumerate(hex_string): + char2i[char] = i + +def to_string(s): + def to_hex(i): + global hex_string + h,l =divmod(i, 16) + return hex_string[h] + hex_string[l] + t = [ to_hex(ord(i)) for i in s ] + return ''.join(t) + + +def to_bin(ss): + global char2i + bin_lst = [chr(char2i[ss[i]] * 16 + char2i[ss[i + 1]]) for i in range(0, len(ss), 2)] + bin_lst.reverse() + return ''.join( bin_lst ) + +def remove_file(file_path): + if os.path.exists(file_path): + os.remove(file_path) + +def toHex(s): + if s[:2] == '0x': + s = s[2:] + return ''.join( [chr(int(s[i:i + 2], 16)) for i in xrange(0, len(s), 2)] ) + +def toString(h): + return ''.join(['%02x' % ord(i) for i in h]) + +def get_timestamp(): + now = datetime.now() + return now.strftime("%Y%m%dT%H%M%ST%f") + +def get_random_str(): + return get_timestamp() + str(random.randint(0, 100)) + +def create_multisig_address(roles, required_key_num): + assert( required_key_num <= len(roles) ) + desc = ' & '.join([i.name for i in roles]) + '\'s Multisig Address' + for i, role in enumerate(roles): + addr = role.new_multisigaddress(desc, roles[:i] + roles[i+1:], required_key_num) + return addr + +def gen_invalid_address(address): + ''' + generate an invalid Base58 addr, according to the input address + ''' + if address[-1] == '1': + return address[:-1] + '0' + return address[:-1] + '1' + +def get_username(): + return pwd.getpwuid(os.getuid())[0] + +def duration_call(func, *args, **kwargs): + before = time.clock() + ret = func(*args, **kwargs) + after = time.clock() + return after - before, ret \ No newline at end of file diff --git a/test/test-rpc-v3/utils/cryptojs.py b/test/test-rpc-v3/utils/cryptojs.py new file mode 100644 index 000000000..7592438cd --- /dev/null +++ b/test/test-rpc-v3/utils/cryptojs.py @@ -0,0 +1,55 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +from Crypto.Hash import MD5 + +from Crypto.Cipher import AES +from Crypto import Random +import base64 + +def toString(s): + return ''.join(['%02x' % ord(i) for i in s]) + +def toBase64(s): + return base64.b64encode(s) + +def derive_key(passphrase, salt): + key_size = 32 + iv_size = 16 + iterations = 1 + + derivedKeyWords = '' + block = '' + + while len(derivedKeyWords) < (key_size + iv_size): + hash = MD5.new() + hash.update(block + passphrase + salt) + + block = hash.digest() + derivedKeyWords += block + key = derivedKeyWords[:key_size] + iv = derivedKeyWords[key_size:] + return key, iv + + +def Pkcs7(data, block_size=16): + nPaddingBytes = block_size - (len(data) % block_size) + return chr(nPaddingBytes) * nPaddingBytes + + +def AES_CBC_encrypt(message, key, iv): + cipher = AES.new(key, AES.MODE_CBC, iv) + msg = cipher.encrypt(message + Pkcs7(message)) + return msg + + +def AES_CBC_decrypt(cipher_txt, passphrase): + s = base64.b64decode(cipher_txt) + salt = s[8:16] + raw_cipher_txt = s[16:] + key, iv = derive_key(passphrase, salt) + cipher = AES.new(key, AES.MODE_CBC, iv) + msg = cipher.decrypt(raw_cipher_txt) + padding = ord(msg[-1]) + return msg[:-padding] + + diff --git a/test/test-rpc-v3/utils/database.py b/test/test-rpc-v3/utils/database.py new file mode 100644 index 000000000..91e8093df --- /dev/null +++ b/test/test-rpc-v3/utils/database.py @@ -0,0 +1,37 @@ +import struct + +str2int_map = { + 1: ' {addr1:amount1, addr2:amount2, ...} + -s [--senders] Send from addresses + [addr1, addr2, ...] + -t [--type] Transaction type. 0 -- transfer etp, 1 -- deposit + etp, 3 -- transfer asset + ''' + return "createrawtx", [], { + '-r': ["%s:%s" % (i, receivers[i]) for i in receivers], + '-s': [i for i in senders], + '-t': type, + '-d': deposit, + '-f': fee, + '-i': message, + '-m': mychange, + '-n': symbol + }, None + +@mvs_api +def sign_rawtx(account, password, transaction): + ''' + TRANSACTION The input Base16 transaction to sign. + ''' + return "signrawtx", [account, password, transaction], {}, None + +@mvs_api +def decode_rawtx(transaction): + ''' + TRANSACTION The input Base16 transaction to decode. + ''' + return "decoderawtx", [transaction], {}, None + +@mvs_api +def send_rawtx(transaction, fee=None): + ''' + TRANSACTION The input Base16 transaction to broadcast. + ''' + return "sendrawtx", [transaction], {'-f':fee}, None + +@mvs_api +def shutdown(): + return "shutdown", [], {}, None + +@mvs_api +def add_node(peer): + ''' + :param peer: format in: "10.10.10.35:5251" + ''' + return 'addnode', [peer], {}, None + +@mvs_api +def ban_node(peer): + ''' + :param peer: format in: "10.10.10.35:5251" + ''' + return 'addnode', [peer], {'-o':'ban'}, None + +@mvs_api +def register_mit(account, password, to_did, symbol=None, content=None, mits=None, fee=None): + if None == content: + content = "" + ''' + account Account name required. + password Account password(authorization) required. + to_did Target did + symbol MIT symbol + content MIT content + ''' + return "registermit", [account, password, to_did, symbol], {'--content':content, '--fee':fee, '--mits':mits}, None + +@mvs_api +def transfer_mit(account, password, to_did, symbol, fee=None): + return "transfermit", [account, password, to_did, symbol], {"-f":fee}, None + +@mvs_api +def list_mits(account=None, password=None): + positional = filter(None, [account, password]) + return "listmits", positional, {}, None + +@mvs_api +def get_mit(symbol=None, tracing=False, page_index=1, page_limit=100): + positional = filter(None, [symbol]) + if symbol != None and tracing: + positional.append("--trace") + return "getmit", positional, {'--index':page_index, '--limit':page_limit}, None + else: + return "getmit", positional, {}, None + +@mvs_api +def get_memorypool(): + return "getmemorypool", [], {}, None + +@mvs_api +def pop_block(height): + ''' + pop blocks with height >= [height]. + ''' + return "popblock", [height], {}, None + +if __name__ == "__main__": + rc = RemoteCtrl("10.10.10.35") + print rc.list_balances('lxf', '123') + print list_balances('lxf', '123') diff --git a/test/test-rpc-v3/utils/validate.py b/test/test-rpc-v3/utils/validate.py new file mode 100644 index 000000000..669da517e --- /dev/null +++ b/test/test-rpc-v3/utils/validate.py @@ -0,0 +1,61 @@ +import MOCs +import mvs_rpc + +def validate_tx(checker, tx_hash, from_who, to_who, amount, fee, desc=None): + ''' + + :param checker: unittest instance + :param tx_hash: + :param from_who: role instance + :param to_who: role instance + :param amount: + :param fee: + :param desc: + :return: + ''' + tx_type = 'etp' + tx = MOCs.Transaction.from_hash(tx_hash) + + checker.assertEqual(tx.hash, tx_hash) + checker.assertEqual(tx.version, '4') # 2 -> 4 since nova version + checker.assertNotEqual(len(tx.inputs), 0) + + sum_payment = 0 + for i in tx.inputs: + ec, message = mvs_rpc.gettx(i.previous_output.hash) + checker.assertEqual(ec, 0, message) + prev_tx = MOCs.Transaction.from_json(message) + o = prev_tx.outputs[i.previous_output.index] + checker.assertEqual(o.index, i.previous_output.index) + checker.assertEqual(o.attachment.type, tx_type) + checker.assertIn(o.address, from_who.addresslist) + sum_payment += o.value + + output_index = 0 + + checker.assertEqual(tx.outputs[output_index].index, output_index) + checker.assertEqual(tx.outputs[output_index].address, to_who.mainaddress()) + checker.assertEqual(tx.outputs[output_index].attachment.type, tx_type) + checker.assertEqual(tx.outputs[output_index].value, amount) + + output_index += 1 + + if desc: + checker.assertEqual(tx.outputs[output_index].index, output_index) + checker.assertEqual(tx.outputs[output_index].address, to_who.mainaddress()) + checker.assertEqual(tx.outputs[output_index].attachment.type, 'message') + checker.assertEqual(tx.outputs[output_index].attachment.content, desc) + checker.assertEqual(tx.outputs[output_index].value, 0) + + output_index += 1 + + if sum_payment > (amount + fee): + checker.assertEqual(tx.outputs[output_index].index, output_index) + checker.assertEqual(tx.outputs[output_index].value, sum_payment - (amount + fee)) + checker.assertIn(tx.outputs[output_index].address, from_who.addresslist) + checker.assertEqual(tx.outputs[output_index].attachment.type, tx_type) + + output_index += 1 + +def validate_asset_tx(): + pass \ No newline at end of file diff --git a/test/test-rpc/Roles.py b/test/test-rpc/Roles.py index 66d86b069..e70716d5c 100644 --- a/test/test-rpc/Roles.py +++ b/test/test-rpc/Roles.py @@ -134,9 +134,12 @@ def issue_cert(self, to_): assert (result == 0) return cert_symbol - def issue_naming_cert(self, domain_symbol): + def issue_naming_cert(self, domain_symbol,did_symbol=None): + if did_symbol==None: + did_symbol = self.did_symbol + cert_symbol = (domain_symbol + "." + common.get_random_str()).upper() - result, message = mvs_rpc.issue_cert(self.name, self.password, self.did_symbol, cert_symbol, "NAMING") + result, message = mvs_rpc.issue_cert(self.name, self.password, did_symbol, cert_symbol, "NAMING") if result != 0: print("failed to issue_cert: {}".format(message)) assert (result == 0) diff --git a/test/test-rpc/TestCase/Account/test_account.py b/test/test-rpc/TestCase/Account/test_account.py index 4b9c71f64..fd8377c33 100644 --- a/test/test-rpc/TestCase/Account/test_account.py +++ b/test/test-rpc/TestCase/Account/test_account.py @@ -23,7 +23,7 @@ def test_1_get_account(self): mnemonic, address_num = message self.assertEqual(mnemonic, ' '.join(Alice.mnemonic)) - self.assertEqual(address_num, 10) + self.assertEqual(address_num, 11) def test_2_delete_account(self): # password error diff --git a/test/test-rpc/TestCase/Asset/test_asset_after_did_modified.py b/test/test-rpc/TestCase/Asset/test_asset_after_did_modified.py index ba5d72dee..65d1c2cef 100644 --- a/test/test-rpc/TestCase/Asset/test_asset_after_did_modified.py +++ b/test/test-rpc/TestCase/Asset/test_asset_after_did_modified.py @@ -30,7 +30,7 @@ def test_0_scenario_did_modified(self): fst_did_address = addresslist[length - 1] fst_did_symbol = u"zacfirstdiid." + common.get_random_str() - result, message = mvs_rpc.send(Alice.name, Alice.password, fst_did_address, 22 * 10 ** 8) + result, message = mvs_rpc.send(Alice.name, Alice.password, fst_did_address, 30 * 10 ** 8) self.assertEqual(result, code.success, message) Zac.mining() @@ -42,7 +42,7 @@ def test_0_scenario_did_modified(self): snd_did_address = addresslist[length - 2] snd_did_symbol = u"zacmodifydiid." + common.get_random_str() - result, message = mvs_rpc.send(Alice.name, Alice.password, snd_did_address, 12 * 10 ** 8) + result, message = mvs_rpc.send(Alice.name, Alice.password, snd_did_address, 15 * 10 ** 8) self.assertEqual(result, code.success, message) Zac.mining() @@ -54,7 +54,7 @@ def test_0_scenario_did_modified(self): # trd_address = addresslist[length - 3] - result, message = mvs_rpc.send(Alice.name, Alice.password, trd_address, 12 * 10 ** 8) + result, message = mvs_rpc.send(Alice.name, Alice.password, trd_address, 15 * 10 ** 8) self.assertEqual(result, code.success, message) Zac.mining() diff --git a/test/test-rpc/TestCase/Asset/test_asset_boundary.py b/test/test-rpc/TestCase/Asset/test_asset_boundary.py index 74143e345..2991b4ce7 100644 --- a/test/test-rpc/TestCase/Asset/test_asset_boundary.py +++ b/test/test-rpc/TestCase/Asset/test_asset_boundary.py @@ -73,13 +73,13 @@ def test_1_create_asset(self): self.assertEqual(ec, 5009, message) def test_2_issue_asset(self): - ten_etp = 10 * (10 ** 8) + ten_etp = 12 * (10 ** 8) # account password match error ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password + '2', Zac.asset_symbol, 1) self.assertEqual(ec, 1000, message) #issue asset fee less than 10 etp - ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, Zac.asset_symbol, ten_etp - 1) + ec, message = mvs_rpc.issue_asset(Zac.name, Zac.password, Zac.asset_symbol, 9) self.assertEqual(ec, 5006, message) #asset symbol length must be less than 64 diff --git a/test/test-rpc/TestCase/Block/test_block.py b/test/test-rpc/TestCase/Block/test_block.py index 44ae2e9ba..ae057b9d3 100644 --- a/test/test-rpc/TestCase/Block/test_block.py +++ b/test/test-rpc/TestCase/Block/test_block.py @@ -16,6 +16,7 @@ def test_0_getblock(self): ec, message_by_height = mvs_rpc.get_block(height) self.assertEqual(ec, 0, message) + self.checkResponseKeys(message_by_height, ["header", "txs"]) self.assertEqual(message_by_hash, message_by_height) @@ -28,11 +29,7 @@ def test_1_getblockheader(self): self.assertEqual(ec, 0, message) # check keys expect_keys = ["bits", "hash", "merkle_tree_hash", "mixhash", "nonce", "number", "previous_block_hash", "time_stamp", "transaction_count", "version"] - expect_keys.sort() - - actual_keys = message.keys() - actual_keys.sort() - self.assertEqual(actual_keys, expect_keys) + self.checkResponseKeys(message, expect_keys) hash = message["hash"] height = message["number"] diff --git a/test/test-rpc/TestCase/Identity/test_did.py b/test/test-rpc/TestCase/Identity/test_did.py index 4c110bdd7..bdfa3dfbd 100644 --- a/test/test-rpc/TestCase/Identity/test_did.py +++ b/test/test-rpc/TestCase/Identity/test_did.py @@ -187,3 +187,95 @@ def test_2_didchangeaddress(self): self.assertEqual(ec, 0, message) Alice.mining() self.assertEqual(Zac.get_didaddress(did_symbol), Zac.mainaddress(), 'Failed when registerdid with:'+did_symbol) + +class TestdidUTXOcommon(MVSTestCaseBase): + def test_didsend_twice(self): + Alice.send_etp(Zac.mainaddress(), 10**10) + Alice.mining() + + ##registerdid + did_symbol = 'Zac@'+common.get_random_str() + ec, message = Zac.register_did(symbol=did_symbol) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #didsendfrom + ec, message = mvs_rpc.didsend_from(Zac.name,Zac.password, did_symbol, Alice.mainaddress(),10000) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend_from(Zac.name,Zac.password, did_symbol, Alice.mainaddress(),10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #didsend + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #didsendmore + receivers = { + Bob.mainaddress(): 100000, + Cindy.did_symbol: 100001, + Dale.mainaddress(): 100002, + Eric.did_symbol: 100003, + } + ec, message = mvs_rpc.didsendmore(Zac.name, Zac.password, receivers, did_symbol, 10000) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsendmore(Zac.name, Zac.password, receivers, did_symbol, 10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #create asset + domain_symbol, asset_symbol = Zac.create_random_asset(did_symbol=did_symbol) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #sendasset + ec, message = mvs_rpc.didsend_asset(Zac.name,Zac.password, Alice.did_symbol,asset_symbol, 100) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #sendassetfrom + ec, message = mvs_rpc.didsend_asset_from(Zac.name,Zac.password, did_symbol,Alice.did_symbol,asset_symbol, 100) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #register mit + mit_symbol = ("MIT." + common.get_random_str()).upper() + content = "MIT of Zac: " + mit_symbol + ec, message = mvs_rpc.register_mit(Zac.name, Zac.password, did_symbol, mit_symbol, content) + self.assertEqual(ec, code.success, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + # transfer mit + ec, message = Zac.transfer_mit(Bob.did_symbol, mit_symbol) + self.assertEqual(ec, code.success, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + + #issue cert + cert_symbol = Zac.issue_naming_cert(domain_symbol,did_symbol) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() + + #transfer cert + ec, message = mvs_rpc.transfer_cert(Zac.name, Zac.password, Alice.did_symbol, cert_symbol, + 'naming', + fee=None) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.didsend(Zac.name,Zac.password, Alice.did_symbol,10000) + self.assertEqual(ec, 0, message) + Alice.mining() diff --git a/test/test-rpc/TestCase/MVSTestCase.py b/test/test-rpc/TestCase/MVSTestCase.py index 33c5a9c90..8015a2269 100644 --- a/test/test-rpc/TestCase/MVSTestCase.py +++ b/test/test-rpc/TestCase/MVSTestCase.py @@ -36,9 +36,17 @@ def setUp(self): print "current block height=[%s], hash=[%s]" % (height, hash) def tearDown(self): - for role in self.roles: - result, message = role.delete() - self.assertEqual(result, 0, message) + pass + #for role in self.roles: + # result, message = role.delete() + # self.assertEqual(result, 0, message) + + def checkResponseKeys(self, result, expect_keys): + if result != None and isinstance(result, dict): + expect_keys.sort() + actual_keys = result.keys() + actual_keys.sort() + self.assertEqual(actual_keys, expect_keys) class MultiSigDIDTestCase(MVSTestCaseBase): def setUp(self): diff --git a/test/test-rpc/json_rpc_v2_keys.txt b/test/test-rpc/json_rpc_v2_keys.txt new file mode 100644 index 000000000..5d81e75a2 --- /dev/null +++ b/test/test-rpc/json_rpc_v2_keys.txt @@ -0,0 +1,234 @@ +{ + "burn": [ + "transaction" + ], + "changepasswd": [ + "name", + "status" + ], + "createasset": [ + "asset" + ], + "createrawtx": [ + "hex" + ], + "decoderawtx": [ + "transaction" + ], + "deleteaccount": [ + "name", + "status" + ], + "deletelocalasset": [ + "operate", + "result", + "symbol" + ], + "deletemultisig": [ + "address", + "description", + "index", + "m", + "multisig-script", + "n", + "public-keys", + "self-publickey" + ], + "deposit": [ + "transaction" + ], + "didchangeaddress": [ + "transaction" + ], + "didsend": [ + "transaction" + ], + "didsendasset": [ + "transaction" + ], + "didsendassetfrom": [ + "transaction" + ], + "didsendfrom": [ + "transaction" + ], + "didsendmore": [ + "transaction" + ], + "getaccount": [ + "address-count", + "mnemonic-key", + "name", + "user-status" + ], + "getaccountasset": [ + "assets" + ], + "getaddressasset": [ + "assets" + ], + "getasset": [ + "assets" + ], + "getbalance": [ + "total-available", + "total-confirmed", + "total-frozen", + "total-received", + "total-unspent" + ], + "getblock": [ + "header", + "txs" + ], + "getblockheader": [ + "bits", + "hash", + "merkle_tree_hash", + "mixhash", + "nonce", + "number", + "previous_block_hash", + "time_stamp", + "transaction_count", + "version" + ], + "getdid": [ + "addresses", + "did" + ], + "getinfo": [ + "database-version", + "difficulty", + "hash-rate", + "height", + "is-mining", + "network-assets-count", + "peers", + "protocol-version", + "testnet", + "wallet-account-count", + "wallet-version" + ], + "getmit": [ + "address", + "content", + "height", + "status", + "symbol", + "time_stamp", + "to_did" + ], + "getnewaccount": [ + "default-address", + "mnemonic" + ], + "getnewaddress": [ + "addresses" + ], + "getnewmultisig": [ + "address", + "description", + "index", + "m", + "multisig-script", + "n", + "public-keys", + "self-publickey" + ], + "getpublickey": [ + "address", + "public-key" + ], + "gettx": [ + "hash", + "height", + "inputs", + "lock_time", + "outputs", + "version" + ], + "importaccount": [ + "addresses", + "hd_index", + "mnemonic", + "name" + ], + "importkeyfile": [ + "addresses", + "name" + ], + "issue": [ + "transaction" + ], + "issuecert": [ + "transaction" + ], + "listaddresses": [ + "addresses" + ], + "listassets": [ + "assets" + ], + "listbalances": [ + "balances" + ], + "listdids": [ + "dids" + ], + "listmits": [ + "mits" + ], + "listmultisig": [ + "multisig" + ], + "listtxs": [ + "current_page", + "total_page", + "transaction_count", + "transactions" + ], + "registerdid": [ + "transaction" + ], + "registermit": [ + "transaction" + ], + "secondaryissue": [ + "transaction" + ], + "send": [ + "transaction" + ], + "sendasset": [ + "transaction" + ], + "sendassetfrom": [ + "transaction" + ], + "sendfrom": [ + "transaction" + ], + "sendmore": [ + "transaction" + ], + "sendrawtx": [ + "hash" + ], + "signrawtx": [ + "hash", + "hex" + ], + "transfercert": [ + "transaction" + ], + "transfermit": [ + "transaction" + ], + "validateaddress": [ + "address-type", + "is-valid", + "message", + "test-net" + ] +} \ No newline at end of file diff --git a/test/test-rpc/tools/parse_cmd.py b/test/test-rpc/tools/parse_cmd.py new file mode 100644 index 000000000..02fe385c9 --- /dev/null +++ b/test/test-rpc/tools/parse_cmd.py @@ -0,0 +1,532 @@ +import os, sys +import re + +cmd_dir = '/home/czp/GitHub/nova/metaverse/include/metaverse/explorer/extensions/commands' + +rettypes = ''' +getblockheader, blockheader +getblock, block +getnewaccount,account +getnewaddress,List +listaddresses,List +validateaddress,address +importaccount,accountEx +importkeyfile,String +dumpkeyfile,String +changepasswd,accountstatus +deleteaccount,accountstatus +getaccount,accountresult +shutdown,String +getinfo,info +getheight,UInt64 +getpeerinfo,List +getmininginfo,mining_info +startmining,String +stopmining,String +getwork,List +addnode,String +submitwork,Boolean +getmemorypool,List +getbalance,balances +listbalances,List +deposit,transaction +send,transaction +sendfrom,transaction +sendmore,transaction +gettx,transaction +listtxs,transactions +createasset,asset +deletelocalasset,delasset +issue,transaction +getaccountasset,List +getaddressasset,List +getasset,List +listassets,List +sendasset,transaction +sendassetfrom,transaction +secondaryissue,transaction +issuecert,transaction +transfercert,transaction +burn,transaction +getnewmultisig,mulsignature +listmultisig,List +deletemultisig,mulsignature +createmultisigtx,String +signmultisigtx,String +createrawtx,String +signrawtx,rawtx +decoderawtx,transaction +sendrawtx,String +registerdid,transaction +listdids,List +didsend,transaction +didsendmore,transaction +didsendfrom,transaction +didsendasset,transaction +didsendassetfrom,transaction +didchangeaddress,transaction +getdid,List +registermit,transaction +transfermit,transaction +listmits,List +getmit,List +getpublickey,String +popblock,String +fetchheaderext,String +getaddressetp,String +setminingaccount,String +''' + +func2type = {} +for i in rettypes.strip().split('\n'): + k,v = i.split(',') + func2type[k.strip()] = v.strip() + +def get_paraname(line): + pattern = '"([/\w]+)(,\w+){0,1}"' + m = re.search(pattern, line) + if m: + ret = m.groups()[0] + else: + ret = line.strip() + return ret + +def get_paradef(line): + pattern = "value\\<([\s\S]+)\\>" + ret = {} + paradef = line.split('->') + m = re.search(pattern, paradef[0]) + ret["para_type"] = m.groups()[0] + for i in paradef[1:]: + if 'required' in i: + ret["must_give"] = True + elif 'default_value' in i: + pattern = 'default_value\\(([\s\S]+)\\)' + + m = re.search(pattern, i) + ret["default_value"] = m.groups()[0].strip() + elif 'zero_tokens' in i: + ret["just_token"] = True + else: + print i + assert False + return ret + +def macro_expand(input): + convert = { + 'BX_ACCOUNT_NAME' : "Account name required.", + 'BX_ACCOUNT_AUTH' : "Account password(authorization) required.", + 'BX_MST_OFFERING_CURVE' : '''The token offering model by block height. + TYPE=1 - fixed quantity model; TYPE=2 - specify parameters; + LQ - Locked Quantity each period; + LP - Locked Period, numeber of how many blocks; + UN - Unlock Number, number of how many LPs; + eg: + TYPE=1;LQ=9000;LP=60000;UN=3 + TYPE=2;LQ=9000;LP=60000;UN=3;UC=20000,20000,20000;UQ=3000,3000,3000 + defaults to disable.''', + 'BX_ADMIN_NAME' : "Administrator required.(when administrator_required in mvs.conf is set true)", + 'BX_ADMIN_AUTH' : "Administrator password required.", + } + + return convert.get(input, input) + +special_cmds = [] + +def parse_cmd(filename): + starts = 'BX_HELP_VARIABLE ",h",' + + positional = [] + buff = [] + with open(filename) as fr: + for line in fr: + if 'return get_argument_metadata()' in line: + positional.append(line) + if not line.strip().endswith(';'): + for line in fr: + positional.append(line) + if line.strip().endswith(';'): + break + + + if 'symbol' in line: + pattern = '{[ ]+return[ ]+"(\w+)"[ ]*;[ ]*}' + m = re.search(pattern, line) + if m: + cmdname = m.groups()[0] + + if line.strip() == starts: + for line in fr: + buff.append(line) + if line.strip().endswith(';'): + break + break + + positional = ''.join(positional) + pattern = '\\.add\\("([^,]+)",[ ]*([-]?\d+)\\)' + + pos_params = [] + for m in re.finditer(pattern, positional): + params = m.groups()[0] + pos_params.append(params) + + + buff = ''.join(buff) + pattern = '\s+\\(\n([\s\S]+?)\s+\\)[;\n]{1}' + + param_details = [] + for m in re.finditer(pattern, buff): + #print buff[m.start(): m.end()] + params = m.groups()[0].split(',\n') + assert len(params) == 3 + param_name = get_paraname(params[0]) + param_def = get_paradef(params[1]) + param_desc = params[2].strip().replace('\\\n', '') + + param_desc = macro_expand(param_desc) + param_details.append( (param_name, param_def, param_desc) ) + + # generate SDK + #generate_py_SDK(cmdname, pos_params, param_details) + generate_go_SDK(cmdname, pos_params, param_details) + #generate_DotNet_SDK(cmdname, pos_params, param_details) + +def generate_py_SDK(cmdname, pos_params, params): + global special_cmds + template = ''' +@mvs_api_v2 +def %(cmdname)s(%(argument_defines)s): + \'\'\' +%(comments)s + \'\'\' + positional=[%(positional_arguments)s] + %(token_arguments)s + optional={ + %(optional_arguments)s + } + return '%(cmdname)s', positional, optional +''' + def cpp2py_type(cpp_type): + convert = { + 'uint8_t' : 'int', + 'uint16_t': 'int', + 'uint32_t': 'int', + 'std::uint32_t' : 'int', + 'uint64_t': 'int', + + 'int8_t': 'int', + 'int16_t': 'int', + 'int32_t': 'int', + 'std::int32_t': 'int', + 'int64_t': 'int', + + 'non_negative_uint64' : 'int', + + 'std::string' : 'str', + 'bool': 'bool', + 'explorer::config::transaction' : 'str', + 'std::vector' : '[str1, str2, ...]', + 'explorer::config::language' : "string of hexcode", + + 'libbitcoin::explorer::commands::colon_delimited2_item' : "(int_low, int_high)", + 'bc::config::hash256' : "string of hash256", + 'boost::filesystem::path': 'string of file path', + 'bc::wallet::payment_address': 'string of Base58-encoded public key address', + } + return convert[cpp_type] + + comments = [] + argument_defines_1 = [] # with no default valie + argument_defines_2 = [] # with default value + optional_arguments = [] + positional_arguments = [] + token_arguments = [] + for param_name, param_def, param_desc in params: + comments.append(" :param: %s(%s): %s" % (param_name, cpp2py_type(param_def["para_type"]), param_desc)) + if param_def.get("must_give", False) == False: + if "colon_delimited2_item" in param_def["para_type"]: + argument_defines_2.append('%s=(0,0)' % param_name) + special_cmds.append(cmdname) + else: + argument_defines_2.append('%s=None' % param_name) + else: + argument_defines_1.append('%s' % param_name) + + if param_name in pos_params: + if param_def["para_type"] == 'std::vector': + positional_arguments.append("' '.join(%s)" % param_name) + special_cmds.append(cmdname) + else: + positional_arguments.append(param_name) + continue + + if param_def.get("just_token", False) == True: + assert param_def["para_type"] == "bool" + token_arguments.append(param_name) + special_cmds.append(cmdname) + continue + + if "colon_delimited2_item" in param_def["para_type"]: + optional_arguments.append( + '"%s" : "%%s:%%s" %% (%s[0], %s[1]),' % (param_name, param_name, param_name) + ) + else: + optional_arguments.append('"%s" : %s,' % (param_name, param_name)) + + paras = { + 'comments': '\n'.join(comments), + 'cmdname': cmdname, + 'argument_defines': ', '.join(argument_defines_1 + argument_defines_2), + 'optional_arguments': '\n '.join(optional_arguments), + 'positional_arguments': ', '.join(positional_arguments), + 'token_arguments': '\n '.join('if %s == True: positional.append("--%s")' % (i, i) for i in token_arguments), + } + + print template % paras + +def generate_go_SDK(cmdname, pos_params, params): + template = ''' +/* +%(comments)s +*/ +func (r *RPCClient) %(func_name)s(%(argument_defines)s) (*JSONRpcResp, error) { + cmd := "%(cmdname)s" + positional := []interface{}{%(positional_arguments)s} + %(positional_defaults)s + optional := map[string]interface{}{ + %(optional_mustgive)s + } + %(token_arguments)s + %(optional_arguments)s + args := append(positional, optional) + return r.doPost(r.Url, cmd, args) +} +''' + def cpp2go_type(cpp_type): + convert = { + 'uint8_t' : 'uint8', + 'uint16_t': 'uint16', + 'uint32_t': 'uint32', + 'std::uint32_t' : 'uint32', + 'uint64_t': 'uint64', + + 'int8_t': 'int8', + 'int16_t': 'int16', + 'int32_t': 'int32', + 'std::int32_t': 'int32', + 'int64_t': 'int64', + + 'non_negative_uint64' : 'uint64', + + 'std::string' : 'string', + 'bool': 'bool', + 'explorer::config::transaction' : 'string', + 'std::vector' : '[]string', + 'explorer::config::language' : 'string', + + 'libbitcoin::explorer::commands::colon_delimited2_item' : "[2]uint64", + 'bc::config::hash256' : "string", + 'boost::filesystem::path': 'string', + 'bc::wallet::payment_address': 'string', + } + return convert[cpp_type] + + def special_type_desc(cpp_type): + convert = { + 'explorer::config::transaction': "string of hexcode", + 'std::vector': 'list of string', + 'libbitcoin::explorer::commands::colon_delimited2_item' : 'a range expressed by 2 integers', + 'bc::config::hash256' : 'string of hash256', + 'boost::filesystem::path' : 'string of file path', + 'bc::wallet::payment_address' : 'string of Base58-encoded public key address', + } + + return convert.get(cpp_type, cpp_type) + + def get_zero(go_type): + if '[2]uint64' == go_type: + return '[2]uint64{0, 0}' + if 'int' in go_type: + return '0' + if 'string' == go_type: + return '""' + if 'bool' == go_type: + return 'false' + if '[]string'== go_type: + return 'nil' + print go_type + assert (False) + comments = [] + argument_defines = [] + token_arguments = [] + optional_arguments = [] + optional_mustgive = [] + positional_arguments = [] + positional_defaults = [] + for param_name, param_def, param_desc in params: + #print cmdname, param_name + comments.append(" :param: %s(%s): %s" % (param_name, special_type_desc( param_def["para_type"] ), param_desc) ) + argument_defines.append( '%s %s' % (param_name, cpp2go_type( param_def["para_type"] ) ) ) + + if param_name in pos_params: + if param_def.get("must_give", False) == False: + zero = get_zero(cpp2go_type(param_def["para_type"])) + if param_def.has_key("default_value"): + if param_def["default_value"] == zero: + positional_defaults.append('if %s != %s {\n positional = append(positional, %s)\n }' % ( + param_name, zero, param_name)) + continue + + if param_def["para_type"] == 'std::vector': + positional_arguments.append('strings.Join(%s, " ")' % param_name) + else: + positional_arguments.append(param_name) + continue + if param_def.get("just_token", False) == True: + assert param_def["para_type"] == "bool" + token_arguments.append(param_name) + continue + + if param_def.get("must_give", False) == False: + if "colon_delimited2_item" in param_def["para_type"]: + optional_arguments.append( (param_name, get_zero(cpp2go_type(param_def["para_type"])), 'strings.Join([]string{strconv.FormatUint(uint64(%s[0]), 10), strconv.FormatUint(uint64(%s[1]), 10)}, ":")' % ( param_name, param_name))) + else: + optional_arguments.append( (param_name, get_zero(cpp2go_type(param_def["para_type"])), param_name) ) + else: + optional_mustgive.append('"%s":%s,' % (param_name, param_name)) + paras = { + 'comments' : '\n'.join(comments), + 'func_name' : cmdname.title(), + 'cmdname' : cmdname, + 'argument_defines' : ', '.join(argument_defines), + 'token_arguments' : '\n '.join('if %s == true{\n positional=append(positional, "--%s")\n }' % (i, i) for i in token_arguments), + 'optional_arguments' : '\n '.join(['if %s != %s {\n optional["%s"] = %s\n }' % (key, zero, key, value) for key, zero, value in optional_arguments]), + 'optional_mustgive' : '\n '.join(optional_mustgive), + 'positional_arguments' : ', '.join(positional_arguments), + 'positional_defaults' : '\n '.join(positional_defaults), + } + + print template % paras + + +def generate_DotNet_SDK(cmdname, pos_params, params): + template = ''' +/* +%(comments)s +*/ +public %(rettype)s %(func_name)s(%(argument_defines)s) +{ + List parameters = new List() { %(positional_arguments)s }; + %(positional_arguments_with_default_value)s + %(token_arguments)s + %(add_optional_parameters)s + return getResult<%(rettype)s>("%(func_name)s", parameters); +} +''' + def cpp2dotnet_type(cpp_type): + convert = { + 'uint8_t' : 'UInt8?', + 'uint16_t': 'UInt16?', + 'uint32_t': 'UInt32?', + 'std::uint32_t' : 'UInt32?', + 'uint64_t': 'UInt64?', + + 'int8_t': 'Int8?', + 'int16_t': 'Int16?', + 'int32_t': 'Int32?', + 'std::int32_t': 'Int32?', + 'int64_t': 'Int64?', + + 'non_negative_uint64' : 'UInt64?', + + 'std::string' : 'String', + 'bool': 'Boolean?', + 'explorer::config::transaction' : 'String', + 'std::vector' : 'List', + 'explorer::config::language' : 'String', + + 'libbitcoin::explorer::commands::colon_delimited2_item' : "Tuple", + 'bc::config::hash256' : "String", + 'boost::filesystem::path': 'String', + 'bc::wallet::payment_address': 'String', + } + return convert[cpp_type] + + def special_type_desc(cpp_type): + convert = { + 'explorer::config::transaction': "string of hexcode", + 'std::vector': 'list of string', + 'libbitcoin::explorer::commands::colon_delimited2_item' : 'a range expressed by 2 integers', + 'bc::config::hash256' : 'string of hash256', + 'boost::filesystem::path' : 'string of file path', + 'bc::wallet::payment_address' : 'string of Base58-encoded public key address', + } + + return convert.get(cpp_type, cpp_type) + comments = [] + argument_defines_1 = [] # with no default valie + argument_defines_2 = [] # with default value + optional_arguments = [] + positional_arguments = [] + positional_arguments_with_default_value = [] + token_arguments = [] + for param_name, param_def, param_desc in params: + comments.append(" :param: %s(%s): %s" % (param_name, special_type_desc(param_def["para_type"]), param_desc)) + #argument_defines.append('%s %s' % (cpp2dotnet_type(param_def["para_type"]), param_name)) + if param_def.get("must_give", False) == False: + argument_defines_2.append('%s %s=null' % (cpp2dotnet_type(param_def["para_type"]), param_name)) + else: + argument_defines_1.append('%s %s' % (cpp2dotnet_type(param_def["para_type"]).replace('?', ''), param_name) ) + + if param_name in pos_params: + if param_def.get("must_give", False) == False: + target = positional_arguments_with_default_value + else: + target = positional_arguments + + if cpp2dotnet_type(param_def["para_type"]) == 'String': + target.append((param_name, param_name)) + elif cpp2dotnet_type(param_def["para_type"]) == 'List': + target.append((param_name, 'String.Join(" ", %s.ToArray())' % param_name)) + else: + target.append((param_name, '%s.ToString()' % param_name)) + continue + + if param_def.get("just_token", False) == True: + assert param_def["para_type"] == "bool" + token_arguments.append(param_name) + continue + + if "vector<" in param_def["para_type"]: + optional_arguments.append('foreach (var i in %s) {' % param_name) + optional_arguments.append(' parameters.AddRange(new List{"--%s", i.ToString()});' % param_name) + optional_arguments.append('}') + else: + prefix = "" + if param_def.get("must_give", False) == False: + prefix = "if (%s != null) " % param_name + + if "colon_delimited2_item" in param_def["para_type"]: + value = 'parameters.AddRange(new List{"--%s", String.Format("{0}:{1}", %s.Item1, %s.Item2)});' % (param_name, param_name, param_name) + else: + value = 'parameters.AddRange(new List{"--%s", %s.ToString()});' % (param_name, param_name) + optional_arguments.append(prefix + value) + + paras = { + 'comments': '\n'.join(comments), + 'func_name': cmdname, + 'argument_defines': ', '.join(argument_defines_1 + argument_defines_2), + 'add_optional_parameters': '\n '.join(optional_arguments), + 'positional_arguments': ', '.join([i[1] for i in positional_arguments]), + 'positional_arguments_with_default_value' : '\n '.join([ 'if (%s != null) parameters.Add(%s);' % (i[0], i[1]) for i in positional_arguments_with_default_value ] ), + 'rettype' : func2type[cmdname], + 'token_arguments': '\n '.join([ 'if (%s == true) parameters.Add("--%s");' % (i, i) for i in token_arguments ]) + } + + print template % paras + +if __name__ == "__main__": + for root, dirs, files in os.walk(cmd_dir): + for file in files: + parse_cmd(root + '/' + file) + print >> sys.stderr, set(special_cmds) \ No newline at end of file diff --git a/test/test-rpc/utils/mvs_rpc.py b/test/test-rpc/utils/mvs_rpc.py index 268067ba9..0af08ddf6 100644 --- a/test/test-rpc/utils/mvs_rpc.py +++ b/test/test-rpc/utils/mvs_rpc.py @@ -1,3 +1,4 @@ +import os import requests import json from utils import common @@ -10,6 +11,7 @@ class RPC: url="http://127.0.0.1:8820/rpc/v2" method_time = {} + method_keys = {} def __init__(self, method): self.method = method @@ -49,8 +51,50 @@ def post(self, positional, optional): else: self.method_time[self.method] = [after - before] + result = rpc_rsp.json()['result'] + if result != None and isinstance(result, dict): + if self.method not in self.method_keys: + keys = result.keys() + keys.sort() + self.method_keys[self.method] = keys + return rpc_rsp + @classmethod + def check_method_response_keys(cls): + path = "./json_rpc_v2_keys.txt" + if (os.path.isfile(path)): + same = True + with open(path, 'r') as handle: + target_keys = json.load(handle) + if len(target_keys) != len(cls.method_keys): + same = False + else: + for k in target_keys: + if k in cls.method_keys: + key1 = target_keys[k] + key1.sort() + key2 = cls.method_keys[k] + key2.sort() + + if key1 != key2: + same = False + break + else: + same = False + break + + if not same: + path = "./json_rpc_v2_keys_error.txt" + if (os.path.isfile(path)): + os.remove(path) + with open(path, 'w') as f: + f.write(json.dumps(cls.method_keys, sort_keys=True, indent=4)) + assert(False and "Json RPC v2 keys do not match!") + else: + with open(path, 'w') as handle: + handle.write(json.dumps(cls.method_keys, sort_keys=True, indent=4)) + @classmethod def export_method_time(cls): ret = [] @@ -64,6 +108,9 @@ def export_method_time(cls): lambda x,y: cmp(x[3], y[3]), reverse=True ) + + cls.check_method_response_keys() + return ret def remote_call(remote_ip, func): @@ -94,9 +141,11 @@ def wrapper(*args, **kwargs): if "error" in rpc_rsp.json(): print(rpc_rsp.json()['error']['code'], rpc_rsp.json()['error']['message']) return rpc_rsp.json()['error']['code'], rpc_rsp.json()['error']['message'] + + result = rpc_rsp.json()['result'] if result_handler: - return 0, result_handler(rpc_rsp.json()['result']) - return 0, rpc_rsp.json()['result'] + return 0, result_handler(result) + return 0, result return wrapper def mvs_api_v3(func):