From 9768e96d7447f8afa443347b9532d0f4a1bab17c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 25 Oct 2019 16:52:30 +0000 Subject: [PATCH 001/469] simplewallet: remove remaining payment id dead code pointed out by coverity --- src/simplewallet/simplewallet.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6a54c24fb..1c38f7a5a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -885,16 +885,6 @@ bool simple_wallet::change_password(const std::vector &args) bool simple_wallet::payment_id(const std::vector &args/* = std::vector()*/) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - - crypto::hash payment_id; - if (args.size() > 0) - { - PRINT_USAGE(USAGE_PAYMENT_ID); - return true; - } - payment_id = crypto::rand(); - success_msg_writer() << tr("Random payment ID: ") << payment_id; - return true; } bool simple_wallet::print_fee_info(const std::vector &args/* = std::vector()*/) @@ -5953,13 +5943,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id)) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); } else { @@ -8985,7 +8960,6 @@ bool simple_wallet::address_book(const std::vector &args/* = std::v if (tools::wallet2::parse_long_payment_id(args[3], payment_id)) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - description_start += 2; } else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id)) { From f49a8cad2373ddfb2fbdb438cbc35f37b6f4eabf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 20:16:19 +0000 Subject: [PATCH 002/469] easylogging++: add screen.xterm-256color to the "allow colour" TERM list --- external/easylogging++/easylogging++.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 5c756bcdf..8439bec0b 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -1243,7 +1243,7 @@ bool OS::termSupportsColor(void) { std::string term = getEnvironmentVariable("TERM", ""); return term == "xterm" || term == "xterm-color" || term == "xterm-256color" || term == "screen" || term == "linux" || term == "cygwin" - || term == "screen-256color"; + || term == "screen-256color" || term == "screen.xterm-256color"; } // DateTime From 8ff9e6bc3219692f3f24ca3b0d84daa94872bbc3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Oct 2019 10:59:31 +0000 Subject: [PATCH 003/469] wallet: do not warn if the rpc cost was free --- src/wallet/wallet_rpc_helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_helpers.h b/src/wallet/wallet_rpc_helpers.h index 91803ff77..4291a112d 100644 --- a/src/wallet/wallet_rpc_helpers.h +++ b/src/wallet/wallet_rpc_helpers.h @@ -65,7 +65,7 @@ namespace tools rpc_payment_state.credits = post_call_credits; rpc_payment_state.expected_spent += expected_credits; - if (pre_call_credits < post_call_credits) + if (pre_call_credits <= post_call_credits) return; uint64_t cost = pre_call_credits - post_call_credits; From 2f8f3a9432a23264329d6532d4ef630272ac2f56 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Oct 2019 16:13:46 +0000 Subject: [PATCH 004/469] rpc: base flush_cache request/response on the new base structs --- src/rpc/core_rpc_server_commands_defs.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 855ea854c..d38f6b183 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -2556,22 +2556,21 @@ namespace cryptonote struct COMMAND_RPC_FLUSH_CACHE { - struct request_t + struct request_t: public rpc_request_base { bool bad_txs; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(bad_txs, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; - struct response_t + struct response_t: public rpc_response_base { - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) + KV_SERIALIZE_PARENT(rpc_response_base) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response; From 261abf79e1c0faed88e1c4f14fdd925586f776b3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Oct 2019 16:20:13 +0000 Subject: [PATCH 005/469] functional_tests: ensure mining stops on error in mining test this prevents messing up any subsequent test too --- tests/functional_tests/mining.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py index ad646417e..d067c25e1 100755 --- a/tests/functional_tests/mining.py +++ b/tests/functional_tests/mining.py @@ -170,5 +170,15 @@ class MiningTest(): assert res.hash == block_hash +class Guard: + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + daemon = Daemon() + try: daemon.stop_mining() + except: pass + if __name__ == '__main__': - MiningTest().run_test() + with Guard() as guard: + MiningTest().run_test() From 86ac20f64ef64e4cadf6ceb2914f7342747cbabc Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Oct 2019 19:14:14 +0000 Subject: [PATCH 006/469] blockchain: fix unwanted error when probing the pool for a tx --- src/cryptonote_core/blockchain.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b7e9f4ca2..b13ecbbf1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1765,9 +1765,18 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id { cryptonote::tx_memory_pool::tx_details td; cryptonote::blobdata blob; - if (m_tx_pool.get_transaction_info(txid, td)) + if (m_tx_pool.have_tx(txid)) { - bei.block_cumulative_weight += td.weight; + if (m_tx_pool.get_transaction_info(txid, td)) + { + bei.block_cumulative_weight += td.weight; + } + else + { + MERROR_VER("Transaction is in the txpool, but metadata not found"); + bvc.m_verifivation_failed = true; + return false; + } } else if (m_db->get_pruned_tx_blob(txid, blob)) { From 277003f14571d551cc3c9e788d0e3b64fc9b3dd6 Mon Sep 17 00:00:00 2001 From: Sarang Noether <32460187+SarangNoether@users.noreply.github.com> Date: Mon, 28 Oct 2019 09:34:49 -0400 Subject: [PATCH 007/469] Minor prover simplification --- src/ringct/bulletproofs.cc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index ff6fee95c..313a526c5 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -601,21 +601,16 @@ try_again: rct::keyV l0 = vector_subtract(aL, z); const rct::keyV &l1 = sL; - // This computes the ugly sum/concatenation from PAPER LINE 65 rct::keyV zero_twos(MN); const rct::keyV zpow = vector_powers(z, M+2); - for (size_t i = 0; i < MN; ++i) + for (size_t j = 0; j < M; ++j) { - zero_twos[i] = rct::zero(); - for (size_t j = 1; j <= M; ++j) - { - if (i >= (j-1)*N && i < j*N) + for (size_t i = 0; i < N; ++i) { - CHECK_AND_ASSERT_THROW_MES(1+j < zpow.size(), "invalid zpow index"); - CHECK_AND_ASSERT_THROW_MES(i-(j-1)*N < twoN.size(), "invalid twoN index"); - sc_muladd(zero_twos[i].bytes, zpow[1+j].bytes, twoN[i-(j-1)*N].bytes, zero_twos[i].bytes); + CHECK_AND_ASSERT_THROW_MES(j+2 < zpow.size(), "invalid zpow index"); + CHECK_AND_ASSERT_THROW_MES(i < twoN.size(), "invalid twoN index"); + sc_mul(zero_twos[j*N+i].bytes,zpow[j+2].bytes,twoN[i].bytes); } - } } rct::keyV r0 = vector_add(aR, z); From 65301c406e6d5e5a72c40dfdca62f018fcff0db9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 28 Oct 2019 16:04:43 +0000 Subject: [PATCH 008/469] core: point out when we hit the block rate visibility limit --- src/cryptonote_core/cryptonote_core.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index acb494a49..0ac45be18 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1881,9 +1881,10 @@ namespace cryptonote } static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days + static constexpr unsigned int max_blocks_checked = 150; const time_t now = time(NULL); - const std::vector timestamps = m_blockchain_storage.get_last_block_timestamps(60); + const std::vector timestamps = m_blockchain_storage.get_last_block_timestamps(max_blocks_checked); static const unsigned int seconds[] = { 5400, 3600, 1800, 1200, 600 }; for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) @@ -1895,7 +1896,7 @@ namespace cryptonote MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); if (p < threshold) { - MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck."); + MWARNING("There were " << b << (b == max_blocks_checked ? " or more" : "") << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck."); std::shared_ptr block_rate_notify = m_block_rate_notify; if (block_rate_notify) From 45fd72b0d7083646164bd4ec5238d930e19c38c9 Mon Sep 17 00:00:00 2001 From: Sarang Noether <32460187+SarangNoether@users.noreply.github.com> Date: Mon, 28 Oct 2019 12:34:44 -0400 Subject: [PATCH 009/469] Updated paper references --- src/ringct/bulletproofs.cc | 46 ++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 313a526c5..80ecc5593 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Adapted from Java code by Sarang Noether +// Paper references are to https://eprint.iacr.org/2017/1066 (revision 1 July 2018) #include #include @@ -521,6 +522,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } PERF_TIMER_STOP_BP(PROVE_v); + // PAPER LINES 41-42 PERF_TIMER_START_BP(PROVE_aLaR); for (size_t j = 0; j < M; ++j) { @@ -566,14 +568,14 @@ try_again: rct::key hash_cache = rct::hash_to_scalar(V); PERF_TIMER_START_BP(PROVE_step1); - // PAPER LINES 38-39 + // PAPER LINES 43-44 rct::key alpha = rct::skGen(); rct::key ve = vector_exponent(aL8, aR8); rct::key A; sc_mul(tmp.bytes, alpha.bytes, INV_EIGHT.bytes); rct::addKeys(A, ve, rct::scalarmultBase(tmp)); - // PAPER LINES 40-42 + // PAPER LINES 45-47 rct::keyV sL = rct::skvGen(MN), sR = rct::skvGen(MN); rct::key rho = rct::skGen(); ve = vector_exponent(sL, sR); @@ -581,7 +583,7 @@ try_again: rct::addKeys(S, ve, rct::scalarmultBase(rho)); S = rct::scalarmultKey(S, INV_EIGHT); - // PAPER LINES 43-45 + // PAPER LINES 48-50 rct::key y = hash_cache_mash(hash_cache, A, S); if (y == rct::zero()) { @@ -598,6 +600,7 @@ try_again: } // Polynomial construction by coefficients + // PAPER LINES 70-71 rct::keyV l0 = vector_subtract(aL, z); const rct::keyV &l1 = sL; @@ -619,7 +622,7 @@ try_again: r0 = vector_add(r0, zero_twos); rct::keyV r1 = hadamard(yMN, sR); - // Polynomial construction before PAPER LINE 46 + // Polynomial construction before PAPER LINE 51 rct::key t1_1 = inner_product(l0, r1); rct::key t1_2 = inner_product(l1, r0); rct::key t1; @@ -629,7 +632,7 @@ try_again: PERF_TIMER_STOP_BP(PROVE_step1); PERF_TIMER_START_BP(PROVE_step2); - // PAPER LINES 47-48 + // PAPER LINES 52-53 rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); rct::key T1, T2; @@ -643,7 +646,7 @@ try_again: ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes); ge_p3_tobytes(T2.bytes, &p3); - // PAPER LINES 49-51 + // PAPER LINES 54-56 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); if (x == rct::zero()) { @@ -652,7 +655,7 @@ try_again: goto try_again; } - // PAPER LINES 52-53 + // PAPER LINES 61-63 rct::key taux; sc_mul(taux.bytes, tau1.bytes, x.bytes); rct::key xsq; @@ -666,7 +669,7 @@ try_again: rct::key mu; sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes); - // PAPER LINES 54-57 + // PAPER LINES 58-60 rct::keyV l = l0; l = vector_add(l, vector_scalar(l1, x)); rct::keyV r = r0; @@ -685,7 +688,7 @@ try_again: CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed"); #endif - // PAPER LINES 32-33 + // PAPER LINE 6 rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); if (x_ip == rct::zero()) { @@ -720,20 +723,19 @@ try_again: PERF_TIMER_STOP_BP(PROVE_step3); PERF_TIMER_START_BP(PROVE_step4); - // PAPER LINE 13 const rct::keyV *scale = &yinvpow; while (nprime > 1) { - // PAPER LINE 15 + // PAPER LINE 20 nprime /= 2; - // PAPER LINES 16-17 + // PAPER LINES 21-22 PERF_TIMER_START_BP(PROVE_inner_product); rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); PERF_TIMER_STOP_BP(PROVE_inner_product); - // PAPER LINES 18-19 + // PAPER LINES 23-24 PERF_TIMER_START_BP(PROVE_LR); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, scale, &ge_p3_H, &tmp); @@ -741,7 +743,7 @@ try_again: R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, scale, &ge_p3_H, &tmp); PERF_TIMER_STOP_BP(PROVE_LR); - // PAPER LINES 21-22 + // PAPER LINES 25-27 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); if (w[round] == rct::zero()) { @@ -750,7 +752,7 @@ try_again: goto try_again; } - // PAPER LINES 24-25 + // PAPER LINES 29-30 const rct::key winv = invert(w[round]); if (nprime > 1) { @@ -760,7 +762,7 @@ try_again: PERF_TIMER_STOP_BP(PROVE_hadamard2); } - // PAPER LINES 28-29 + // PAPER LINES 33-34 PERF_TIMER_START_BP(PROVE_prime); aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); @@ -771,7 +773,6 @@ try_again: } PERF_TIMER_STOP_BP(PROVE_step4); - // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) return Bulletproof(std::move(V), A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t); } @@ -805,7 +806,10 @@ struct proof_data_t size_t logM, inv_offset; }; -/* Given a range proof, determine if it is valid */ +/* Given a range proof, determine if it is valid + * This uses the method in PAPER LINES 95-105, + * weighted across multiple proofs in a batch + */ bool bulletproof_VERIFY(const std::vector &proofs) { init_exponents(); @@ -866,7 +870,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); PERF_TIMER_START_BP(VERIFY_line_21_22); - // PAPER LINES 21-22 // The inner product challenges are computed per round pd.w.resize(rounds); for (size_t i = 0; i < rounds; ++i) @@ -924,7 +927,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) rct::key proof8_A = rct::scalarmult8(proof.A); PERF_TIMER_START_BP(VERIFY_line_61); - // PAPER LINE 61 sc_mulsub(m_y0.bytes, proof.taux.bytes, weight_y.bytes, m_y0.bytes); const rct::keyV zpow = vector_powers(pd.z, M+3); @@ -957,7 +959,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_STOP_BP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); - // PAPER LINE 62 multiexp_data.emplace_back(weight_z, proof8_A); sc_mul(tmp.bytes, pd.x.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_S); @@ -968,7 +969,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); PERF_TIMER_START_BP(VERIFY_line_24_25); - // Basically PAPER LINES 24-25 // Compute the curvepoints from G[i] and H[i] rct::key yinvpow = rct::identity(); rct::key ypow = rct::identity(); @@ -1005,7 +1005,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mul(g_scalar.bytes, g_scalar.bytes, w_cache[i].bytes); sc_mul(h_scalar.bytes, h_scalar.bytes, w_cache[(~i) & (MN-1)].bytes); - // Adjust the scalars using the exponents from PAPER LINE 62 sc_add(g_scalar.bytes, g_scalar.bytes, pd.z.bytes); CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index"); CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index"); @@ -1038,7 +1037,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_STOP_BP(VERIFY_line_24_25); - // PAPER LINE 26 PERF_TIMER_START_BP(VERIFY_line_26_new); sc_muladd(z1.bytes, proof.mu.bytes, weight_z.bytes, z1.bytes); for (size_t i = 0; i < rounds; ++i) From 2e2bf8a20739522f7d0a76172f4c2d705e79ce00 Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 28 Oct 2019 21:52:19 +0000 Subject: [PATCH 010/469] daemon: always use bootstrap daemon (if set) in '--no-sync' mode --- src/cryptonote_protocol/cryptonote_protocol_handler.h | 1 + src/rpc/core_rpc_server.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index b16b42564..3b456e324 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -112,6 +112,7 @@ namespace cryptonote void stop(); void on_connection_close(cryptonote_connection_context &context); void set_max_out_peers(unsigned int max) { m_max_out_peers = max; } + bool no_sync() const { return m_no_sync; } void set_no_sync(bool value) { m_no_sync = value; } std::string get_peers_overview() const; std::pair get_next_needed_pruning_stripe() const; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index df264dde6..8c08b81b0 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1903,7 +1903,8 @@ namespace cryptonote } auto current_time = std::chrono::system_clock::now(); - if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s + if (!m_p2p.get_payload_object().no_sync() && + current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s { { boost::upgrade_to_unique_lock lock(upgrade_lock); @@ -1927,9 +1928,10 @@ namespace cryptonote uint64_t top_height = m_core.get_current_blockchain_height(); m_should_use_bootstrap_daemon = top_height + 10 < *bootstrap_daemon_height; MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << *bootstrap_daemon_height << ")"); + + if (!m_should_use_bootstrap_daemon) + return false; } - if (!m_should_use_bootstrap_daemon) - return false; if (mode == invoke_http_mode::JON) { From c96b7ee61910ab64e0e0cbd978addfe2ea3e4b27 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Oct 2019 10:43:31 +0000 Subject: [PATCH 011/469] tx_pool: fix error message assuming incorrectly --- src/cryptonote_core/tx_pool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 392e611e9..25b100563 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -264,7 +264,7 @@ namespace cryptonote } catch (const std::exception &e) { - MERROR("transaction already exists at inserting in memory pool: " << e.what()); + MERROR("Error adding transaction to txpool: " << e.what()); return false; } tvc.m_verifivation_impossible = true; @@ -310,7 +310,7 @@ namespace cryptonote } catch (const std::exception &e) { - MERROR("internal error: transaction already exists at inserting in memory pool: " << e.what()); + MERROR("internal error: error adding transaction to txpool: " << e.what()); return false; } tvc.m_added_to_pool = true; From deb350b7836c6b62a097b4231143c630ac731075 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 18 Jun 2019 21:16:25 +0000 Subject: [PATCH 012/469] always print peer IDs in the same format --- .../cryptonote_protocol_handler.inl | 6 ++---- src/daemon/rpc_command_executor.cpp | 4 ++-- src/p2p/net_node.inl | 12 ++++++------ src/p2p/p2p_protocol_defs.h | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 74ceeb41d..f43375360 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -188,7 +188,7 @@ namespace cryptonote auto connection_time = time(NULL) - cntxt.m_started; ss << std::setw(30) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + cntxt.m_remote_address.str() - << std::setw(20) << std::hex << peer_id + << std::setw(20) << nodetool::peerid_to_string(peer_id) << std::setw(20) << std::hex << support_flags << std::setw(30) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" << std::setw(25) << get_protocol_state_string(cntxt.m_state) @@ -248,9 +248,7 @@ namespace cryptonote cnx.rpc_port = cntxt.m_rpc_port; cnx.rpc_credits_per_hash = cntxt.m_rpc_credits_per_hash; - std::stringstream peer_id_str; - peer_id_str << std::hex << std::setw(16) << peer_id; - peer_id_str >> cnx.peer_id; + cnx.peer_id = nodetool::peerid_to_string(peer_id); cnx.support_flags = support_flags; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..2cd42045b 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -664,7 +664,7 @@ bool t_rpc_command_executor::print_connections() { << std::setw(30) << std::left << address << std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type)) << std::setw(6) << (info.ssl ? "yes" : "no") - << std::setw(20) << epee::string_tools::pad_string(info.peer_id, 16, '0', true) + << std::setw(20) << info.peer_id << std::setw(20) << info.support_flags << std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(info.recv_idle_time) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(info.send_idle_time) + ")" << std::setw(25) << info.state @@ -2217,7 +2217,7 @@ bool t_rpc_command_executor::sync_info() for (const auto &s: res.spans) if (s.connection_id == p.info.connection_id) nblocks += s.nblocks, size += s.size; - tools::success_msg_writer() << address << " " << epee::string_tools::pad_string(p.info.peer_id, 16, '0', true) << " " << + tools::success_msg_writer() << address << " " << p.info.peer_id << " " << epee::string_tools::pad_string(p.info.state, 16) << " " << epee::string_tools::pad_string(epee::string_tools::to_string_hex(p.info.pruning_seed), 8) << " " << p.info.height << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued"; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f8094bfa8..2915d64db 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1368,7 +1368,7 @@ namespace nodetool bool node_server::make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist) { for (const auto& pe: anchor_peerlist) { - _note("Considering connecting (out) to anchor peer: " << peerid_type(pe.id) << " " << pe.adr.str()); + _note("Considering connecting (out) to anchor peer: " << peerid_to_string(pe.id) << " " << pe.adr.str()); if(is_peer_used(pe)) { _note("Peer is used"); @@ -1949,7 +1949,7 @@ namespace nodetool const network_zone& zone = m_network_zones.at(zone_type); if(zone.m_config.m_peer_id != tr.peer_id) { - MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << zone.m_config.m_peer_id<< ")"); + MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << peerid_to_string(zone.m_config.m_peer_id) << ")"); return false; } crypto::public_key pk = AUTO_VAL_INIT(pk); @@ -2220,7 +2220,7 @@ namespace nodetool network_zone& zone = m_network_zones.at(address.get_zone()); if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) { - LOG_WARNING_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << rsp.peer_id); + LOG_WARNING_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << peerid_to_string(rsp.peer_id)); zone.m_net_server.get_config_object().close(ping_context.m_connection_id); return; } @@ -2453,7 +2453,7 @@ namespace nodetool zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { ss << cntxt.m_remote_address.str() - << " \t\tpeer_id " << cntxt.peer_id + << " \t\tpeer_id " << peerid_to_string(cntxt.peer_id) << " \t\tconn_id " << cntxt.m_connection_id << (cntxt.m_is_income ? " INC":" OUT") << std::endl; return true; @@ -2711,12 +2711,12 @@ namespace nodetool if (!check_connection_and_handshake_with_peer(pe.adr, pe.last_seen)) { zone.second.m_peerlist.remove_from_peer_gray(pe); - LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_type(pe.id)); + LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST: address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id)); } else { zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed, pe.rpc_port, pe.rpc_credits_per_hash); - LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_type(pe.id)); + LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id)); } } return true; diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 44b278589..393bddd05 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -132,7 +132,7 @@ namespace nodetool ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; for(const peerlist_entry& pe: pl) { - ss << pe.id << "\t" << pe.adr.str() + ss << peerid_to_string(pe.id) << "\t" << pe.adr.str() << " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-") << " \trpc credits per hash " << (pe.rpc_credits_per_hash > 0 ? std::to_string(pe.rpc_credits_per_hash) : "-") << " \tpruning seed " << pe.pruning_seed From 017f8168976d99c3a15f9eb3e158bc6a6876c48d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 26 May 2019 08:58:49 +0000 Subject: [PATCH 013/469] daemon: handle printing higher hash rates --- src/daemon/rpc_command_executor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..fc9f8ef39 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -380,7 +380,7 @@ static void get_metric_prefix(cryptonote::difficulty_type hr, double& hr_d, char prefix = 0; return; } - static const char metric_prefixes[4] = { 'k', 'M', 'G', 'T' }; + static const char metric_prefixes[] = { 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; for (size_t i = 0; i < sizeof(metric_prefixes); ++i) { if (hr < 1000000) From 886ed250555a32c05757e9b86563890156d4f045 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Sep 2019 19:04:29 +0000 Subject: [PATCH 014/469] blockchain: fix comment wrongly refering to SHA-3 rather than Keccak --- src/cryptonote_core/blockchain.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6467031c2..59860e67e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1042,7 +1042,7 @@ namespace cryptonote std::unordered_map>> m_scan_table; std::unordered_map m_blocks_longhash_table; - // SHA-3 hashes for each block and for fast pow checking + // Keccak hashes for each block and for fast pow checking std::vector> m_blocks_hash_of_hashes; std::vector> m_blocks_hash_check; std::vector m_blocks_txs_check; From 5a44893a71696fb89f51e438b665a9a71db6f3b7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 10 May 2019 17:20:20 +0000 Subject: [PATCH 015/469] python-rpc: add missing strict_balances parameter for get_accounts --- utils/python-rpc/framework/wallet.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index 6a3fabdc9..141e6595c 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -838,11 +838,12 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(validate_address) - def get_accounts(self, tag): + def get_accounts(self, tag, strict_balances = False): get_accounts = { 'method': 'get_accounts', 'params': { 'tag': tag, + 'strict_balances': strict_balances, }, 'jsonrpc': '2.0', 'id': '0' From 3b8dcc290d87fd16698b6dca161ded22ee681fdc Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Oct 2019 13:53:07 +0000 Subject: [PATCH 016/469] wallet2: make keys unlocker reentrant protects against having your keys mangled --- src/wallet/wallet2.cpp | 24 +++++++++++++++++++++--- src/wallet/wallet2.h | 2 ++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9b3e7e8b4..18fc93fd9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1038,10 +1038,15 @@ uint64_t gamma_picker::pick() return first_rct + crypto::rand_idx(n_rct); }; +boost::mutex wallet_keys_unlocker::lockers_lock; +unsigned int wallet_keys_unlocker::lockers = 0; wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional &password): w(w), locked(password != boost::none) { + boost::lock_guard lock(lockers_lock); + if (lockers++ > 0) + locked = false; if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) { locked = false; @@ -1056,6 +1061,9 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee:: w(w), locked(locked) { + boost::lock_guard lock(lockers_lock); + if (lockers++ > 0) + locked = false; if (!locked) return; w.generate_chacha_key_from_password(password, key); @@ -1064,9 +1072,19 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee:: wallet_keys_unlocker::~wallet_keys_unlocker() { - if (!locked) - return; - try { w.encrypt_keys(key); } + try + { + boost::lock_guard lock(lockers_lock); + if (lockers == 0) + { + MERROR("There are no lockers in wallet_keys_unlocker dtor"); + return; + } + --lockers; + if (!locked) + return; + w.encrypt_keys(key); + } catch (...) { MERROR("Failed to re-encrypt wallet keys"); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 640565a4e..d0c01fd71 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -122,6 +122,8 @@ private: wallet2 &w; bool locked; crypto::chacha_key key; + static boost::mutex lockers_lock; + static unsigned int lockers; }; class i_wallet2_callback From 1554a7768bcb4e29e7f3cb2d6f3225ddecacb458 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 30 Oct 2019 18:39:48 +0000 Subject: [PATCH 017/469] unit_tests: fix use after free --- tests/unit_tests/node_server.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 2c89323c7..5f91fc6d4 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -262,16 +262,25 @@ TEST(ban, ignores_port) TEST(node_server, bind_same_p2p_port) { - const auto new_node = []() -> std::unique_ptr { + struct test_data_t + { test_core pr_core; - cryptonote::t_cryptonote_protocol_handler cprotocol(pr_core, NULL); - std::unique_ptr server(new Server(cprotocol)); - cprotocol.set_p2p_endpoint(server.get()); + cryptonote::t_cryptonote_protocol_handler cprotocol; + std::unique_ptr server; - return server; + test_data_t(): cprotocol(pr_core, NULL) + { + server.reset(new Server(cprotocol)); + cprotocol.set_p2p_endpoint(server.get()); + } }; - const auto init = [](const std::unique_ptr& server, const char* port) -> bool { + const auto new_node = []() -> std::unique_ptr { + test_data_t *d = new test_data_t; + return std::unique_ptr(d); + }; + + const auto init = [](const std::unique_ptr& server, const char* port) -> bool { boost::program_options::options_description desc_options("Command line options"); cryptonote::core::init_options(desc_options); Server::init_options(desc_options); @@ -284,7 +293,7 @@ TEST(node_server, bind_same_p2p_port) boost::program_options::notify(vm); - return server->init(vm); + return server->server->init(vm); }; constexpr char port[] = "48080"; From f1091c410fd9f6dfe7514fb523f230c16668ced9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 30 Oct 2019 18:37:42 +0000 Subject: [PATCH 018/469] core_tests: remove some useless verbose logs --- tests/core_tests/bulletproofs.cpp | 1 - tests/core_tests/multisig.cpp | 2 -- tests/core_tests/rct.cpp | 1 - tests/core_tests/v2_tests.cpp | 1 - 4 files changed, 5 deletions(-) diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index fd3f5114b..0a5b98605 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -64,7 +64,6 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve false, "Failed to generate block"); events.push_back(blocks[n]); prev_block = blocks + n; - LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx)); } // rewind diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index e0c90423d..ba6e2d270 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -180,8 +180,6 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector& eve tx_source_entry& src = sources.back(); src.amount = blocks[0].miner_tx.vout[out_idx[out_idx_idx]].amount; - std::cout << "using " << print_money(src.amount) << " output at index " << out_idx[out_idx_idx] << std::endl; for (int m = 0; m <= mixin; ++m) { int idx; if (is_valid_decomposed_amount(src.amount)) From 508dcfadacbfa180258b1928d0cf2578d70204a0 Mon Sep 17 00:00:00 2001 From: tevador Date: Wed, 30 Oct 2019 20:23:45 +0100 Subject: [PATCH 019/469] RandomX: Update to v1.1.6 --- external/randomx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/randomx b/external/randomx index 53af68c34..7567cef4c 160000 --- a/external/randomx +++ b/external/randomx @@ -1 +1 @@ -Subproject commit 53af68c34a43f5bdb659f90cc27b6b2da7fd77fc +Subproject commit 7567cef4c6192fb5356bbdd7db802be77be0439b From 2efc6acdb24f63a528366cd203ab9bde24b18b72 Mon Sep 17 00:00:00 2001 From: tevador Date: Wed, 30 Oct 2019 20:25:10 +0100 Subject: [PATCH 020/469] RandomX: Update to v1.1.6 --- external/randomx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/randomx b/external/randomx index 53af68c34..7567cef4c 160000 --- a/external/randomx +++ b/external/randomx @@ -1 +1 @@ -Subproject commit 53af68c34a43f5bdb659f90cc27b6b2da7fd77fc +Subproject commit 7567cef4c6192fb5356bbdd7db802be77be0439b From 0e3b823a15793f4ccb815d0ad183f39e14930955 Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 28 Oct 2019 21:52:19 +0000 Subject: [PATCH 021/469] daemon: always use bootstrap daemon (if set) in '--no-sync' mode --- src/cryptonote_protocol/cryptonote_protocol_handler.h | 1 + src/rpc/core_rpc_server.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index b16b42564..3b456e324 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -112,6 +112,7 @@ namespace cryptonote void stop(); void on_connection_close(cryptonote_connection_context &context); void set_max_out_peers(unsigned int max) { m_max_out_peers = max; } + bool no_sync() const { return m_no_sync; } void set_no_sync(bool value) { m_no_sync = value; } std::string get_peers_overview() const; std::pair get_next_needed_pruning_stripe() const; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dfc4d4cf3..eb71504f0 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1666,7 +1666,8 @@ namespace cryptonote } auto current_time = std::chrono::system_clock::now(); - if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s + if (!m_p2p.get_payload_object().no_sync() && + current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s { { boost::upgrade_to_unique_lock lock(upgrade_lock); @@ -1690,9 +1691,10 @@ namespace cryptonote uint64_t top_height = m_core.get_current_blockchain_height(); m_should_use_bootstrap_daemon = top_height + 10 < *bootstrap_daemon_height; MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << *bootstrap_daemon_height << ")"); + + if (!m_should_use_bootstrap_daemon) + return false; } - if (!m_should_use_bootstrap_daemon) - return false; if (mode == invoke_http_mode::JON) { From f498dfc15844e1d79f6de0e2d0c74d5bbf10de08 Mon Sep 17 00:00:00 2001 From: Kevin Crumb Date: Wed, 30 Oct 2019 21:38:03 +0000 Subject: [PATCH 022/469] README: add Void Linux package dependencies see also https://github.com/void-linux/void-packages/blob/master/srcpkgs/monero/template --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index e465d1ccc..85a04efd0 100644 --- a/README.md +++ b/README.md @@ -174,31 +174,31 @@ sources are also used for statically-linked builds because distribution packages often include only shared library binaries (`.so`) but not static library archives (`.a`). -| Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Fedora | Optional | Purpose | -| ------------ | ------------- | -------- | -------------------- | ------------ | ------------------- | -------- | --------------- | -| GCC | 4.7.3 | NO | `build-essential` | `base-devel` | `gcc` | NO | | -| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | NO | | -| pkg-config | any | NO | `pkg-config` | `base-devel` | `pkgconf` | NO | | -| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | NO | C++ libraries | -| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum | -| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | NO | ZeroMQ library | -| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | -| libnorm[2] | ? | NO | `libnorm-dev` | | | YES | For ZeroMQ | -| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | -| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography | -| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | -| liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind | -| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | -| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `ldns-devel` | YES | SSL toolkit | -| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | YES | XML parsing | -| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | YES | Test suite | -| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation | -| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | -| lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations | -| libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | YES | Hardware wallet | -| libusb | ? | NO | `libusb-dev` | `libusb` | `libusb-devel` | YES | Hardware wallet | -| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | YES | Hardware wallet | -| protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet | +| Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Void pkg | Fedora pkg | Optional | Purpose | +| ------------ | ------------- | -------- | -------------------- | ------------ | ------------------ | ------------------- | -------- | --------------- | +| GCC | 4.7.3 | NO | `build-essential` | `base-devel` | `base-devel` | `gcc` | NO | | +| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | `cmake` | NO | | +| pkg-config | any | NO | `pkg-config` | `base-devel` | `base-devel` | `pkgconf` | NO | | +| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | `boost-devel` | NO | C++ libraries | +| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `libressl-devel` | `openssl-devel` | NO | sha256 sum | +| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | `zeromq-devel` | NO | ZeroMQ library | +| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | | `openpgm-devel` | NO | For ZeroMQ | +| libnorm[2] | ? | NO | `libnorm-dev` | | | | YES | For ZeroMQ | +| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | `unbound-devel` | NO | DNS resolver | +| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | `libsodium-devel` | NO | cryptography | +| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | `libunwind-devel` | YES | Stack traces | +| liblzma | any | NO | `liblzma-dev` | `xz` | `liblzma-devel` | `xz-devel` | YES | For libunwind | +| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | `readline-devel` | YES | Input editing | +| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `libldns-devel` | `ldns-devel` | YES | SSL toolkit | +| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | `expat-devel` | YES | XML parsing | +| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | `gtest-devel` | YES | Test suite | +| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | `doxygen` | YES | Documentation | +| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | +| lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations | +| libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | `hidapi-devel` | YES | Hardware wallet | +| libusb | ? | NO | `libusb-dev` | `libusb` | `libusb-devel` | `libusb-devel` | YES | Hardware wallet | +| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | `protobuf-devel` | YES | Hardware wallet | +| protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet | [1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must From ebc6ce44f407fab07955fc230f4cab59742fd959 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 Oct 2019 01:05:58 +0000 Subject: [PATCH 023/469] cryptonote: untangle dependency from miner to blockchain It causes link errors at least on mac --- src/cryptonote_basic/miner.cpp | 10 +++++----- src/cryptonote_basic/miner.h | 8 ++++---- src/cryptonote_core/cryptonote_core.cpp | 4 +++- src/cryptonote_core/cryptonote_tx_utils.cpp | 4 +++- src/rpc/core_rpc_server.cpp | 4 +++- tests/core_tests/chaingen.cpp | 8 ++++++-- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index c4b5c8455..688aeaea3 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -102,13 +102,13 @@ namespace cryptonote } - miner::miner(i_miner_handler* phandler, Blockchain* pbc):m_stop(1), + miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):m_stop(1), m_template{}, m_template_no(0), m_diffic(0), m_thread_index(0), m_phandler(phandler), - m_pbc(pbc), + m_gbh(gbh), m_height(0), m_threads_active(0), m_pausers_count(0), @@ -471,12 +471,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(const Blockchain *pbc, block& bl, const difficulty_type& diffic, uint64_t height) + bool miner::find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height) { for(; bl.nonce != std::numeric_limits::max(); bl.nonce++) { crypto::hash h; - get_block_longhash(pbc, bl, h, height, tools::get_max_concurrency()); + gbh(bl, height, tools::get_max_concurrency(), h); if(check_hash(h, diffic)) { @@ -572,7 +572,7 @@ namespace cryptonote b.nonce = nonce; crypto::hash h; - get_block_longhash(m_pbc, b, h, height, tools::get_max_concurrency()); + m_gbh(b, height, tools::get_max_concurrency(), h); if(check_hash(h, local_diff)) { diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 4efbcbec3..ce50d674e 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -52,7 +52,7 @@ namespace cryptonote ~i_miner_handler(){}; }; - class Blockchain; + typedef std::function get_block_hash_t; /************************************************************************/ /* */ @@ -60,7 +60,7 @@ namespace cryptonote class miner { public: - miner(i_miner_handler* phandler, Blockchain* pbc); + miner(i_miner_handler* phandler, const get_block_hash_t& gbh); ~miner(); bool init(const boost::program_options::variables_map& vm, network_type nettype); static void init_options(boost::program_options::options_description& desc); @@ -76,7 +76,7 @@ namespace cryptonote bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(const Blockchain *pbc, block& bl, const difficulty_type& diffic, uint64_t height); + static bool find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -135,7 +135,7 @@ namespace cryptonote std::list m_threads; epee::critical_section m_threads_lock; i_miner_handler* m_phandler; - Blockchain* m_pbc; + get_block_hash_t m_gbh; account_public_address m_mine_address; epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index acb494a49..02620996e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -223,7 +223,9 @@ namespace cryptonote core::core(i_cryptonote_protocol* pprotocol): m_mempool(m_blockchain_storage), m_blockchain_storage(m_mempool), - m_miner(this, &m_blockchain_storage), + m_miner(this, [this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) { + return cryptonote::get_block_longhash(&m_blockchain_storage, b, hash, height, threads); + }), m_starter_message_showed(false), m_target_blockchain_height(0), m_checkpoints_path(""), diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 3e1b4e97f..f50fc61a5 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -663,7 +663,9 @@ namespace cryptonote bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; bl.timestamp = 0; bl.nonce = nonce; - miner::find_nonce_for_given_block(NULL, bl, 1, 0); + miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(NULL, b, hash, height, threads); + }, bl, 1, 0); bl.invalidate_hashes(); return true; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index df264dde6..7c0c6a8c4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1833,7 +1833,9 @@ namespace cryptonote return false; } b.nonce = req.starting_nonce; - miner::find_nonce_for_given_block(&(m_core.get_blockchain_storage()), b, template_res.difficulty, template_res.height); + miner::find_nonce_for_given_block([this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) { + return cryptonote::get_block_longhash(&(m_core.get_blockchain_storage()), b, hash, height, threads); + }, b, template_res.difficulty, template_res.height); submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b)); r = on_submitblock(submit_req, submit_res, error_resp, ctx); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 3f2984288..35e449e10 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -186,7 +186,9 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co // Nonce search... blk.nonce = 0; - while (!miner::find_nonce_for_given_block(NULL, blk, get_test_difficulty(hf_ver), height)) + while (!miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(NULL, b, hash, height, threads); + }, blk, get_test_difficulty(hf_ver), height)) blk.timestamp++; add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1); @@ -797,7 +799,9 @@ void fill_tx_sources_and_destinations(const std::vector& event void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) { blk.nonce = 0; - while (!miner::find_nonce_for_given_block(NULL, blk, diffic, height)) + while (!miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(NULL, b, hash, height, threads); + }, blk, diffic, height)) blk.timestamp++; } From d31024c2e1634d32cfbf750ce5713b52a1e67707 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 Oct 2019 01:05:58 +0000 Subject: [PATCH 024/469] cryptonote: untangle dependency from miner to blockchain It causes link errors at least on mac --- src/cryptonote_basic/miner.cpp | 10 +++++----- src/cryptonote_basic/miner.h | 8 ++++---- src/cryptonote_core/cryptonote_core.cpp | 4 +++- src/cryptonote_core/cryptonote_tx_utils.cpp | 4 +++- src/rpc/core_rpc_server.cpp | 4 +++- tests/core_tests/chaingen.cpp | 8 ++++++-- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index c4b5c8455..688aeaea3 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -102,13 +102,13 @@ namespace cryptonote } - miner::miner(i_miner_handler* phandler, Blockchain* pbc):m_stop(1), + miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):m_stop(1), m_template{}, m_template_no(0), m_diffic(0), m_thread_index(0), m_phandler(phandler), - m_pbc(pbc), + m_gbh(gbh), m_height(0), m_threads_active(0), m_pausers_count(0), @@ -471,12 +471,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(const Blockchain *pbc, block& bl, const difficulty_type& diffic, uint64_t height) + bool miner::find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height) { for(; bl.nonce != std::numeric_limits::max(); bl.nonce++) { crypto::hash h; - get_block_longhash(pbc, bl, h, height, tools::get_max_concurrency()); + gbh(bl, height, tools::get_max_concurrency(), h); if(check_hash(h, diffic)) { @@ -572,7 +572,7 @@ namespace cryptonote b.nonce = nonce; crypto::hash h; - get_block_longhash(m_pbc, b, h, height, tools::get_max_concurrency()); + m_gbh(b, height, tools::get_max_concurrency(), h); if(check_hash(h, local_diff)) { diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 4efbcbec3..ce50d674e 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -52,7 +52,7 @@ namespace cryptonote ~i_miner_handler(){}; }; - class Blockchain; + typedef std::function get_block_hash_t; /************************************************************************/ /* */ @@ -60,7 +60,7 @@ namespace cryptonote class miner { public: - miner(i_miner_handler* phandler, Blockchain* pbc); + miner(i_miner_handler* phandler, const get_block_hash_t& gbh); ~miner(); bool init(const boost::program_options::variables_map& vm, network_type nettype); static void init_options(boost::program_options::options_description& desc); @@ -76,7 +76,7 @@ namespace cryptonote bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(const Blockchain *pbc, block& bl, const difficulty_type& diffic, uint64_t height); + static bool find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -135,7 +135,7 @@ namespace cryptonote std::list m_threads; epee::critical_section m_threads_lock; i_miner_handler* m_phandler; - Blockchain* m_pbc; + get_block_hash_t m_gbh; account_public_address m_mine_address; epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index acb494a49..02620996e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -223,7 +223,9 @@ namespace cryptonote core::core(i_cryptonote_protocol* pprotocol): m_mempool(m_blockchain_storage), m_blockchain_storage(m_mempool), - m_miner(this, &m_blockchain_storage), + m_miner(this, [this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) { + return cryptonote::get_block_longhash(&m_blockchain_storage, b, hash, height, threads); + }), m_starter_message_showed(false), m_target_blockchain_height(0), m_checkpoints_path(""), diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 3e1b4e97f..f50fc61a5 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -663,7 +663,9 @@ namespace cryptonote bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; bl.timestamp = 0; bl.nonce = nonce; - miner::find_nonce_for_given_block(NULL, bl, 1, 0); + miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(NULL, b, hash, height, threads); + }, bl, 1, 0); bl.invalidate_hashes(); return true; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index df264dde6..7c0c6a8c4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1833,7 +1833,9 @@ namespace cryptonote return false; } b.nonce = req.starting_nonce; - miner::find_nonce_for_given_block(&(m_core.get_blockchain_storage()), b, template_res.difficulty, template_res.height); + miner::find_nonce_for_given_block([this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) { + return cryptonote::get_block_longhash(&(m_core.get_blockchain_storage()), b, hash, height, threads); + }, b, template_res.difficulty, template_res.height); submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b)); r = on_submitblock(submit_req, submit_res, error_resp, ctx); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 3f2984288..35e449e10 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -186,7 +186,9 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co // Nonce search... blk.nonce = 0; - while (!miner::find_nonce_for_given_block(NULL, blk, get_test_difficulty(hf_ver), height)) + while (!miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(NULL, b, hash, height, threads); + }, blk, get_test_difficulty(hf_ver), height)) blk.timestamp++; add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1); @@ -797,7 +799,9 @@ void fill_tx_sources_and_destinations(const std::vector& event void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) { blk.nonce = 0; - while (!miner::find_nonce_for_given_block(NULL, blk, diffic, height)) + while (!miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(NULL, b, hash, height, threads); + }, blk, diffic, height)) blk.timestamp++; } From 5ae02988184bc8e837830d1320b51c8577c12c91 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 30 Oct 2019 18:39:48 +0000 Subject: [PATCH 025/469] unit_tests: fix use after free --- tests/unit_tests/node_server.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 2c89323c7..5f91fc6d4 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -262,16 +262,25 @@ TEST(ban, ignores_port) TEST(node_server, bind_same_p2p_port) { - const auto new_node = []() -> std::unique_ptr { + struct test_data_t + { test_core pr_core; - cryptonote::t_cryptonote_protocol_handler cprotocol(pr_core, NULL); - std::unique_ptr server(new Server(cprotocol)); - cprotocol.set_p2p_endpoint(server.get()); + cryptonote::t_cryptonote_protocol_handler cprotocol; + std::unique_ptr server; - return server; + test_data_t(): cprotocol(pr_core, NULL) + { + server.reset(new Server(cprotocol)); + cprotocol.set_p2p_endpoint(server.get()); + } }; - const auto init = [](const std::unique_ptr& server, const char* port) -> bool { + const auto new_node = []() -> std::unique_ptr { + test_data_t *d = new test_data_t; + return std::unique_ptr(d); + }; + + const auto init = [](const std::unique_ptr& server, const char* port) -> bool { boost::program_options::options_description desc_options("Command line options"); cryptonote::core::init_options(desc_options); Server::init_options(desc_options); @@ -284,7 +293,7 @@ TEST(node_server, bind_same_p2p_port) boost::program_options::notify(vm); - return server->init(vm); + return server->server->init(vm); }; constexpr char port[] = "48080"; From fb9b741bf0c7144036129741664e6a56d00df8f9 Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Thu, 31 Oct 2019 15:44:55 -0400 Subject: [PATCH 026/469] README update upgrade table with details for nov 30th network upgrade --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e465d1ccc..598cb1158 100644 --- a/README.md +++ b/README.md @@ -153,9 +153,11 @@ Dates are provided in the format YYYY-MM-DD. | 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required | 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format | 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format -| XXXXXXX | 2019-10-XX | XX | XXXXXXXXX | XXXXXXXXX | X +| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming transactions +| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX | X's indicate that these details have not been determined as of commit date. +* indicates estimate as of commit date ## Release staging schedule and protocol From 97ae7bb5cbf2cb3ef14fa20c9af72b24b9fe3e6f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Oct 2019 09:12:24 +0000 Subject: [PATCH 027/469] wallet2: do not repeatedly ask for pool txes sent to us MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets a passive attacker with access to the network link between node and wallet perform traffic analysis to deduce when an idle wallet receives a transaction. Reported by Tramèr et al. --- src/wallet/wallet2.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9b3e7e8b4..4f0e2c26a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2968,11 +2968,6 @@ void wallet2::update_pool_state(bool refreshed) LOG_PRINT_L1("We sent that one"); } } - else - { - LOG_PRINT_L1("Already saw that one, it's for us"); - txids.push_back({txid, true}); - } } // get those txes From 9f57f0df924175dfb3d8b222850d6363d3c17a6a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 1 Nov 2019 14:22:40 +0000 Subject: [PATCH 028/469] simplewallet: do not mention inactivity if a lock was manual --- src/simplewallet/simplewallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 03693a57c..100344601 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6195,7 +6195,8 @@ void simple_wallet::check_for_inactivity_lock(bool user) } while (1) { - tools::msg_writer() << tr("Locked due to inactivity. The wallet password is required to unlock the console."); + const char *inactivity_msg = user ? "" : tr("Locked due to inactivity."); + tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console."); try { if (get_and_verify_password()) From 78e59f531eb73cf145f04fb5a92507bb80522d3c Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Fri, 1 Nov 2019 16:53:10 +0200 Subject: [PATCH 029/469] Merge pull request #6059 45b6b6038 Updating gitian yml files for v0.15 (Jonathan Cross) --- contrib/gitian/gitian-linux.yml | 2 +- contrib/gitian/gitian-osx.yml | 2 +- contrib/gitian/gitian-win.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 8cbde2acb..8a338ba04 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "monero-linux-0.14" +name: "monero-linux-0.15" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index d3141e2c7..e712e4195 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "monero-osx-0.14" +name: "monero-osx-0.15" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index 4c559acfe..7b2b4373f 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "monero-win-0.14" +name: "monero-win-0.15" enable_cache: true suites: - "bionic" From 22d30866ca9eb312e8463f8386e8f129c096a14b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 1 Nov 2019 15:37:55 +0000 Subject: [PATCH 030/469] simplewallet: add missing inactivity-lock-timeout to set help blurb --- src/simplewallet/simplewallet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 100344601..5e7159645 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3262,7 +3262,9 @@ simple_wallet::simple_wallet() "auto-mine-for-rpc-payment-threshold \n " " Whether to automatically start mining for RPC payment if the daemon requires it.\n" "credits-target \n" - " The RPC payment credits balance to target (0 for default).")); + " The RPC payment credits balance to target (0 for default).\n " + "inactivity-lock-timeout \n " + " How many seconds to wait before locking the wallet (0 to disable).")); m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::on_command, this, &simple_wallet::encrypted_seed, _1), tr("Display the encrypted Electrum-style mnemonic seed.")); From d5472bd87b8e93706295d8aa7ff99e5ad594277d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Oct 2019 12:25:26 +0000 Subject: [PATCH 031/469] wallet2: do not send an unnecessary last getblocks.bin call on refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "everything refreshed" state was detected when a refresh call did not return any new blocks. This can be detected without that extra "empty" call by comparing the claimed node height to the height of the last block retrieved. Doing this avoids that last call, saves some bandwidth, and makes the common refresh case use only one call rather than two. As a side effect, it prevents an information leak reported by Tramèr et al: if the wallet retrieves a set of blocks which includes an output sent to the refreshing wallet, the wallet will prompt the user for the password to decode the amount and calculate the key image for the new output, and this will delay subsequent calls to getblocks.bin, allowing a passive adversary to note the delay and deduce when the wallet receives at least one output. This can still happen if the wallet downloads more than 1000 blocks, since this will be split in several calls, but then the most the adversary can tell is which 1000 block section the user received some monero (the adversary can estimate the heights of the blocks by calculating how many "large" transfers are done, which will be sections of blocks, the last of which will usually be below 1000, but the size of the data should allow the actual number of blocks sent to be determined fairly accurately). This timing trick still be used via the subsequent scan for incoming txes in the txpool, which will be fixed later. --- src/wallet/wallet2.cpp | 20 +++++++++++++++----- src/wallet/wallet2.h | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4f0e2c26a..c7c49f445 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2519,12 +2519,14 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl error = !cryptonote::parse_and_validate_block_from_blob(blob, bl, bl_id); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices) +void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height) { cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); req.block_ids = short_chain_history; + MDEBUG("Pulling blocks: start_height " << start_height); + req.prune = true; req.start_height = start_height; req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; @@ -2544,6 +2546,10 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, blocks_start_height = res.start_height; blocks = std::move(res.blocks); o_indices = std::move(res.output_indices); + current_height = res.current_height; + + MDEBUG("Pulled blocks: blocks_start_height " << blocks_start_height << ", count " << blocks.size() + << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height); } //---------------------------------------------------------------------------------------------------- void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &hashes) @@ -2726,9 +2732,10 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo refresh(trusted_daemon, start_height, blocks_fetched, received_money); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error, std::exception_ptr &exception) +void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception) { error = false; + last = false; exception = NULL; try @@ -2746,7 +2753,8 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks // pull the new blocks std::vector o_indices; - pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices); + uint64_t current_height; + pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices, current_height); THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices"); tools::threadpool& tpool = tools::threadpool::getInstance(); @@ -2784,6 +2792,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks } } waiter.wait(&tpool); + last = !blocks.empty() && cryptonote::get_block_height(parsed_blocks.back().block) + 1 == current_height; } catch(...) { @@ -3250,7 +3259,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo }); auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);}); - bool first = true; + bool first = true, last = false; while(m_run.load(std::memory_order_relaxed)) { uint64_t next_blocks_start_height; @@ -3272,7 +3281,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo refreshed = true; break; } - tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, error, exception);}); + if (!last) + tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, last, error, exception);}); if (!first) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 640565a4e..d99e6e8c5 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1403,10 +1403,10 @@ private: void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; bool clear(); void clear_soft(bool keep_key_images=false); - void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); + void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); - void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error, std::exception_ptr &exception); + void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception); void process_parsed_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added, std::map, size_t> *output_tracker_cache = NULL); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers) const; bool prepare_file_names(const std::string& file_path); From fd35e2304ab5223edd9c6caf03a16c19330e972d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Oct 2019 18:10:29 +0000 Subject: [PATCH 032/469] wallet: fix another facet of "did I get some monero" information leak We get new pool txes before processing any tx, pool or not. This ensures that if we're asked for a password, this does not cause a measurable delay in the txpool query after the last block query. --- src/simplewallet/simplewallet.cpp | 12 ++++++++-- src/wallet/wallet2.cpp | 40 +++++++++++++++++++++++-------- src/wallet/wallet2.h | 3 ++- src/wallet/wallet_rpc_server.cpp | 10 ++++++-- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 03693a57c..7cf0b4913 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8356,7 +8356,11 @@ bool simple_wallet::get_transfers(std::vector& local_args, std::vec m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - m_wallet->update_pool_state(); + std::vector> process_txs; + m_wallet->update_pool_state(process_txs); + if (!process_txs.empty()) + m_wallet->process_pool_state(process_txs); + std::list> payments; m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices); for (std::list>::const_iterator i = payments.begin(); i != payments.end(); ++i) { @@ -10002,7 +10006,11 @@ bool simple_wallet::show_transfer(const std::vector &args) try { - m_wallet->update_pool_state(); + std::vector> process_txs; + m_wallet->update_pool_state(process_txs); + if (!process_txs.empty()) + m_wallet->process_pool_state(process_txs); + std::list> pool_payments; m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account); for (std::list>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c7c49f445..b23e8525b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2829,7 +2829,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector &tx_hashe } //---------------------------------------------------------------------------------------------------- -void wallet2::update_pool_state(bool refreshed) +void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed) { MTRACE("update_pool_state start"); @@ -3019,13 +3019,7 @@ void wallet2::update_pool_state(bool refreshed) [tx_hash](const std::pair &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_new_transaction(tx_hash, tx, std::vector(), 0, 0, time(NULL), false, true, tx_entry.double_spend_seen, {}); - m_scanned_pool_txs[0].insert(tx_hash); - if (m_scanned_pool_txs[0].size() > 5000) - { - std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]); - m_scanned_pool_txs[0].clear(); - } + process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen)); } else { @@ -3056,6 +3050,24 @@ void wallet2::update_pool_state(bool refreshed) MTRACE("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- +void wallet2::process_pool_state(const std::vector> &txs) +{ + const time_t now = time(NULL); + for (const auto &e: txs) + { + const cryptonote::transaction &tx = e.first; + const bool double_spend_seen = e.second; + const crypto::hash tx_hash = get_transaction_hash(tx); + process_new_transaction(tx_hash, tx, std::vector(), 0, 0, now, false, true, double_spend_seen, {}); + m_scanned_pool_txs[0].insert(tx_hash); + if (m_scanned_pool_txs[0].size() > 5000) + { + std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]); + m_scanned_pool_txs[0].clear(); + } + } +} +//---------------------------------------------------------------------------------------------------- void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force) { std::vector hashes; @@ -3259,6 +3271,14 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo }); auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);}); + + // get updated pool state first, but do not process those txes just yet, + // since that might cause a password prompt, which would introduce a data + // leak allowing a passive adversary with traffic analysis capability to + // infer when we get an incoming output + std::vector> process_pool_txs; + update_pool_state(process_pool_txs, refreshed); + bool first = true, last = false; while(m_run.load(std::memory_order_relaxed)) { @@ -3389,8 +3409,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo try { // If stop() is called we don't need to check pending transactions - if (check_pool && m_run.load(std::memory_order_relaxed)) - update_pool_state(refreshed); + if (check_pool && m_run.load(std::memory_order_relaxed) && !process_pool_txs.empty()) + process_pool_state(process_pool_txs); } catch (...) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d99e6e8c5..c86315f7c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1219,7 +1219,8 @@ private: bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; - void update_pool_state(bool refreshed = false); + void update_pool_state(std::vector> &process_txs, bool refreshed = false); + void process_pool_state(const std::vector> &txs); void remove_obsolete_pool_txs(const std::vector &tx_hashes); std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ec21b2897..de501f056 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2438,7 +2438,10 @@ namespace tools if (req.pool) { - m_wallet->update_pool_state(); + std::vector> process_txs; + m_wallet->update_pool_state(process_txs); + if (!process_txs.empty()) + m_wallet->process_pool_state(process_txs); std::list> payments; m_wallet->get_unconfirmed_payments(payments, account_index, subaddr_indices); @@ -2518,7 +2521,10 @@ namespace tools } } - m_wallet->update_pool_state(); + std::vector> process_txs; + m_wallet->update_pool_state(process_txs); + if (!process_txs.empty()) + m_wallet->process_pool_state(process_txs); std::list> pool_payments; m_wallet->get_unconfirmed_payments(pool_payments, req.account_index); From 5956beaa15161c3993f38c314a10633d679f0eb8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Oct 2019 18:35:11 +0000 Subject: [PATCH 033/469] wallet2: fix is_synced checking target height, not height Target height would be appropriate for the daemon, which syncs off other daemons, but the wallet syncs off the daemon it's connected to, and its target is the daemon's current height. --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b23e8525b..7f8b48b8d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -13212,7 +13212,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui bool wallet2::is_synced() { uint64_t height; - boost::optional result = m_node_rpc_proxy.get_target_height(height); + boost::optional result = m_node_rpc_proxy.get_height(height); if (result && *result != CORE_RPC_STATUS_OK) return false; return get_blockchain_current_height() >= height; From e1083302489810562e8c9f19a4d2528ea9dd501d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Oct 2019 19:16:13 +0000 Subject: [PATCH 034/469] wallet: reuse cached height when set after refresh Refreshing sets cached height, which is otherwise got by calling get_info. Since get_info is called upon needing to display a prompt after a command has finished, it can be used to determine how much time a given command took to run if the cache timeout lapses while the command runs. Refreshing caches the height as a side effect, so get_info will never be called as a result of displaying a prompt after refreshing (and potentially leaking how much time it took to process a set of transactions, therefore leaking whether we got some monero in them). --- src/wallet/node_rpc_proxy.cpp | 10 ++++++++++ src/wallet/node_rpc_proxy.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 731896715..15ea26044 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -77,6 +77,7 @@ void NodeRPCProxy::invalidate() m_rpc_payment_seed_height = 0; m_rpc_payment_seed_hash = crypto::null_hash; m_rpc_payment_next_seed_hash = crypto::null_hash; + m_height_time = 0; } boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) @@ -101,6 +102,7 @@ boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version void NodeRPCProxy::set_height(uint64_t h) { m_height = h; + m_height_time = time(NULL); } boost::optional NodeRPCProxy::get_info() @@ -126,12 +128,20 @@ boost::optional NodeRPCProxy::get_info() m_target_height = resp_t.target_height; m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit; m_get_info_time = now; + m_height_time = now; } return boost::optional(); } boost::optional NodeRPCProxy::get_height(uint64_t &height) { + const time_t now = time(NULL); + if (now < m_height_time + 30) // re-cache every 30 seconds + { + height = m_height; + return boost::optional(); + } + auto res = get_info(); if (res) return res; diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index a9d8167ac..65ca40640 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -97,6 +97,7 @@ private: crypto::hash m_rpc_payment_seed_hash; crypto::hash m_rpc_payment_next_seed_hash; uint32_t m_rpc_payment_cookie; + time_t m_height_time; }; } From dcff02e4c3b1214143b19685361c3c6cffa62be7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 17 Oct 2019 12:13:17 +0000 Subject: [PATCH 035/469] epee: allow a random component in once_a_time timeouts --- contrib/epee/include/math_helper.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h index 604a04680..29acffaea 100644 --- a/contrib/epee/include/math_helper.h +++ b/contrib/epee/include/math_helper.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -230,7 +231,7 @@ namespace math_helper } } - template + template class once_a_time { uint64_t get_time() const @@ -251,12 +252,18 @@ namespace math_helper #endif } + void set_next_interval() + { + m_interval = get_interval()(); + } + public: - once_a_time():m_interval(default_interval * scale) + once_a_time() { m_last_worked_time = 0; if(!start_immediate) m_last_worked_time = get_time(); + set_next_interval(); } void trigger() @@ -273,6 +280,7 @@ namespace math_helper { bool res = functr(); m_last_worked_time = get_time(); + set_next_interval(); return res; } return true; @@ -283,9 +291,13 @@ namespace math_helper uint64_t m_interval; }; + template struct get_constant_interval { public: uint64_t operator()() const { return N; } }; + template - class once_a_time_seconds: public once_a_time<1000000, default_interval, start_immediate> {}; + class once_a_time_seconds: public once_a_time, start_immediate> {}; template - class once_a_time_milliseconds: public once_a_time<1000, default_interval, start_immediate> {}; + class once_a_time_milliseconds: public once_a_time, start_immediate> {}; + template + class once_a_time_seconds_range: public once_a_time {}; } } From b9fc206660089fd73eb30acfecfccadd6717dd06 Mon Sep 17 00:00:00 2001 From: JamesWrigley Date: Fri, 1 Nov 2019 16:16:37 +0100 Subject: [PATCH 036/469] Add a --keep-fakechain option to keep fakechain databases This is handy when doing tests that generate a lot of transactions, since that takes time it's preferable to re-use the database for future runs. --- src/cryptonote_core/cryptonote_core.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index acb494a49..cfb3e36f7 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -83,6 +83,11 @@ namespace cryptonote , "Run in a regression testing mode." , false }; + const command_line::arg_descriptor arg_keep_fakechain = { + "keep-fakechain" + , "Don't delete any existing database when in fakechain mode." + , false + }; const command_line::arg_descriptor arg_fixed_difficulty = { "fixed-difficulty" , "Fixed difficulty used for testing." @@ -315,6 +320,7 @@ namespace cryptonote command_line::add_arg(desc, arg_testnet_on); command_line::add_arg(desc, arg_stagenet_on); command_line::add_arg(desc, arg_regtest_on); + command_line::add_arg(desc, arg_keep_fakechain); command_line::add_arg(desc, arg_fixed_difficulty); command_line::add_arg(desc, arg_dns_checkpoints); command_line::add_arg(desc, arg_prep_blocks_threads); @@ -468,6 +474,7 @@ namespace cryptonote size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight); bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain); bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks); + bool keep_fakechain = command_line::get_arg(vm, arg_keep_fakechain); boost::filesystem::path folder(m_config_folder); if (m_nettype == FAKECHAIN) @@ -509,7 +516,7 @@ namespace cryptonote bool sync_on_blocks = true; uint64_t sync_threshold = 1; - if (m_nettype == FAKECHAIN) + if (m_nettype == FAKECHAIN && !keep_fakechain) { // reset the db by removing the database file before opening it if (!db->remove_data_file(filename)) From 38f6910481f9bb61c4d6bf8409fddd6426384a58 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Oct 2019 10:30:50 +0000 Subject: [PATCH 037/469] simplewallet: plug a timing leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As reported by Tramèr et al, timing of refresh requests can be used to see whether a password was requested (and thus at least one output received) since this will induce a delay in subsequent calls. To avoid this, we schedule calls at a given time instead of sleeping for a set time (which would make delays additive). To further avoid a scheduled call being during the time in which a password is prompted, the actual scheduled time is now randomized. --- src/simplewallet/simplewallet.cpp | 35 ++++++++++++++++++++++++------- src/simplewallet/simplewallet.h | 8 ++++--- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 7cf0b4913..ea8f6f2f5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8807,22 +8807,41 @@ void simple_wallet::check_for_messages() //---------------------------------------------------------------------------------------------------- void simple_wallet::wallet_idle_thread() { + const boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::universal_time(); while (true) { boost::unique_lock lock(m_idle_mutex); if (!m_idle_run.load(std::memory_order_relaxed)) break; -#ifndef _WIN32 - m_inactivity_checker.do_call(boost::bind(&simple_wallet::check_inactivity, this)); + // if another thread was busy (ie, a foreground refresh thread), we'll end up here at + // some random time that's not what we slept for, so we should not call refresh now + // or we'll be leaking that fact through timing + const boost::posix_time::ptime now0 = boost::posix_time::microsec_clock::universal_time(); + const uint64_t dt_actual = (now0 - start_time).total_microseconds() % 1000000; +#ifdef _WIN32 + static const uint64_t threshold = 10000; +#else + static const uint64_t threshold = 2000; #endif - m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this)); - m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this)); - m_rpc_payment_checker.do_call(boost::bind(&simple_wallet::check_rpc_payment, this)); + if (dt_actual < threshold) // if less than a threshold... would a very slow machine always miss it ? + { +#ifndef _WIN32 + m_inactivity_checker.do_call(boost::bind(&simple_wallet::check_inactivity, this)); +#endif + m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this)); + m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this)); + m_rpc_payment_checker.do_call(boost::bind(&simple_wallet::check_rpc_payment, this)); - if (!m_idle_run.load(std::memory_order_relaxed)) - break; - m_idle_cond.wait_for(lock, boost::chrono::seconds(1)); + if (!m_idle_run.load(std::memory_order_relaxed)) + break; + } + + // aim for the next multiple of 1 second + const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); + const auto dt = (now - start_time).total_microseconds(); + const auto wait = 1000000 - dt % 1000000; + m_idle_cond.wait_for(lock, boost::chrono::microseconds(wait)); } } //---------------------------------------------------------------------------------------------------- diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index e8f96ad54..75bd893d5 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -448,10 +448,12 @@ namespace cryptonote std::atomic m_locked; std::atomic m_in_command; + template struct get_random_interval { public: uint64_t operator()() const { return crypto::rand_range(mini, maxi); } }; + epee::math_helper::once_a_time_seconds<1> m_inactivity_checker; - epee::math_helper::once_a_time_seconds<90> m_refresh_checker; - epee::math_helper::once_a_time_seconds<90> m_mms_checker; - epee::math_helper::once_a_time_seconds<90> m_rpc_payment_checker; + epee::math_helper::once_a_time_seconds_range> m_refresh_checker; + epee::math_helper::once_a_time_seconds_range> m_mms_checker; + epee::math_helper::once_a_time_seconds_range> m_rpc_payment_checker; std::atomic m_need_payment; boost::posix_time::ptime m_last_rpc_payment_mining_time; From 4ad0f003859f26851c35299c26a451f4f319410e Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 2 Nov 2019 17:31:22 +0200 Subject: [PATCH 038/469] Merge pull request #6079 e4d1674e8 0.15.0.0 release engineering (Riccardo Spagni) --- README.md | 8 ++++---- contrib/gitian/README.md | 6 +++--- src/blocks/checkpoints.dat | Bin 234948 -> 244676 bytes src/checkpoints/checkpoints.cpp | 1 + src/cryptonote_core/blockchain.cpp | 2 +- src/version.cpp.in | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 598cb1158..7b6b252e0 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ invokes cmake commands as needed. ```bash cd monero - git checkout release-v0.14 + git checkout release-v0.15 make ``` @@ -315,7 +315,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ```bash git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.14.1.2 + git checkout tags/v0.15.0.0 ``` * Build: @@ -432,10 +432,10 @@ application. cd monero ``` -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.14.1.2'. If you don't care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.15.0.0'. If you don't care about the version and just want binaries from master, skip this step: ```bash - git checkout v0.14.1.2 + git checkout v0.15.0.0 ``` * If you are on a 64-bit system, run: diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 32aee5f56..3c40d09e0 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -126,7 +126,7 @@ Setup for LXC: ```bash GH_USER=fluffypony -VERSION=v0.14.1.0 +VERSION=v0.15.0.0 ./gitian-build.py --setup $GH_USER $VERSION ``` @@ -167,7 +167,7 @@ If all went well, this produces a number of (uncommitted) `.assert` files in the Checking your work ------------------ -Take a look in the assert files and note the SHA256 checksums listed there. eg for `v0.14.1.0` you should get this checksum: +Take a look in the assert files and note the SHA256 checksums listed there. eg for `v0.15.0.0` you should get this checksum: ``` 2b95118f53d98d542a85f8732b84ba13b3cd20517ccb40332b0edd0ddf4f8c62 monero-x86_64-linux-gnu.tar.gz @@ -183,7 +183,7 @@ If you chose to do detached signing using `--detach-sign` above (recommended), y ```bash GH_USER=fluffypony -VERSION=v0.14.1.0 +VERSION=v0.15.0.0 gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index eb614d58d93a6652a9569cd16dd9cd78962a455e..b14f9e8d2b0fcc87fb4f0afe427222a8cfa151f0 100644 GIT binary patch literal 244676 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 z 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�u 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≪E%5oH#Ce;y+E*fb-{o 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 Date: Sat, 2 Nov 2019 20:36:03 +0000 Subject: [PATCH 039/469] Adding support for hidden (anonymity) txpool --- contrib/epee/include/span.h | 3 +- src/blockchain_db/blockchain_db.cpp | 84 +++ src/blockchain_db/blockchain_db.h | 49 +- src/blockchain_db/lmdb/db_lmdb.cpp | 48 +- src/blockchain_db/lmdb/db_lmdb.h | 10 +- src/blockchain_db/testdb.h | 10 +- .../blockchain_import.cpp | 2 +- src/cryptonote_core/blockchain.cpp | 31 +- src/cryptonote_core/blockchain.h | 9 +- src/cryptonote_core/cryptonote_core.cpp | 120 ++-- src/cryptonote_core/cryptonote_core.h | 73 ++- src/cryptonote_core/i_core_events.h | 44 ++ src/cryptonote_core/tx_pool.cpp | 209 ++++--- src/cryptonote_core/tx_pool.h | 53 +- .../cryptonote_protocol_handler.h | 2 +- .../cryptonote_protocol_handler.inl | 34 +- .../cryptonote_protocol_handler_common.h | 4 +- src/cryptonote_protocol/enums.h | 43 ++ src/cryptonote_protocol/fwd.h | 37 ++ src/cryptonote_protocol/levin_notify.h | 6 +- src/p2p/net_node.h | 3 +- src/p2p/net_node.inl | 11 +- src/p2p/net_node_common.h | 6 +- src/rpc/core_rpc_server.cpp | 16 +- src/rpc/daemon_handler.cpp | 6 +- tests/core_proxy/core_proxy.cpp | 8 +- tests/core_proxy/core_proxy.h | 11 +- tests/core_tests/CMakeLists.txt | 2 + tests/core_tests/chaingen.h | 44 +- tests/core_tests/chaingen_main.cpp | 7 + tests/core_tests/double_spend.cpp | 3 +- tests/core_tests/double_spend.inl | 10 +- tests/core_tests/tx_pool.cpp | 561 ++++++++++++++++++ tests/core_tests/tx_pool.h | 118 ++++ tests/unit_tests/node_server.cpp | 11 +- 35 files changed, 1372 insertions(+), 316 deletions(-) create mode 100644 src/cryptonote_core/i_core_events.h create mode 100644 src/cryptonote_protocol/enums.h create mode 100644 src/cryptonote_protocol/fwd.h create mode 100644 tests/core_tests/tx_pool.cpp create mode 100644 tests/core_tests/tx_pool.h diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index e100452ca..59895535f 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -110,7 +110,8 @@ namespace epee constexpr std::size_t size() const noexcept { return len; } constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); } - const T &operator[](size_t idx) const { return ptr[idx]; } + T &operator[](size_t idx) noexcept { return ptr[idx]; } + const T &operator[](size_t idx) const noexcept { return ptr[idx]; } private: T* ptr; diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 63ac38a88..5fec22d8d 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -44,6 +44,71 @@ using epee::string_tools::pod_to_hex; namespace cryptonote { +bool matches_category(relay_method method, relay_category category) noexcept +{ + switch (category) + { + default: + return false; + case relay_category::all: + return true; + case relay_category::relayable: + if (method == relay_method::none) + return false; + return true; + case relay_category::broadcasted: + case relay_category::legacy: + break; + } + // check for "broadcasted" or "legacy" methods: + switch (method) + { + default: + case relay_method::local: + return false; + case relay_method::block: + case relay_method::flood: + return true; + case relay_method::none: + break; + } + return category == relay_category::legacy; +} + +void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept +{ + kept_by_block = 0; + do_not_relay = 0; + is_local = 0; + + switch (method) + { + case relay_method::none: + do_not_relay = 1; + break; + case relay_method::local: + is_local = 1; + break; + default: + case relay_method::flood: + break; + case relay_method::block: + kept_by_block = 1; + break; + } +} + +relay_method txpool_tx_meta_t::get_relay_method() const noexcept +{ + if (kept_by_block) + return relay_method::block; + if (do_not_relay) + return relay_method::none; + if (is_local) + return relay_method::local; + return relay_method::flood; +} + const command_line::arg_descriptor arg_db_sync_mode = { "db-sync-mode" , "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[[blocks]|[bytes]]." @@ -924,4 +989,23 @@ void BlockchainDB::fixup() batch_stop(); } +bool BlockchainDB::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category) +{ + try + { + txpool_tx_meta_t meta{}; + if (!get_txpool_tx_meta(tx_hash, meta)) + { + MERROR("Failed to get tx meta from txpool"); + return false; + } + return meta.matches(category); + } + catch (const std::exception &e) + { + MERROR("Failed to get tx meta from txpool: " << e.what()); + } + return false; +} + } // namespace cryptonote diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index d1e4919be..b2c5d6cb4 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -39,6 +39,7 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/difficulty.h" #include "cryptonote_basic/hardfork.h" +#include "cryptonote_protocol/enums.h" /** \file * Cryptonote Blockchain Database Interface @@ -105,6 +106,16 @@ typedef std::pair tx_out_index; extern const command_line::arg_descriptor arg_db_sync_mode; extern const command_line::arg_descriptor arg_db_salvage; +enum class relay_category : uint8_t +{ + broadcasted = 0,//!< Public txes received via block/flooding/fluff + relayable, //!< Every tx not marked `relay_method::none` + legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons + all //!< Everything in the db +}; + +bool matches_category(relay_method method, relay_category category) noexcept; + #pragma pack(push, 1) /** @@ -156,11 +167,22 @@ struct txpool_tx_meta_t uint8_t do_not_relay; uint8_t double_spend_seen: 1; uint8_t pruned: 1; - uint8_t bf_padding: 6; + uint8_t is_local: 1; + uint8_t bf_padding: 5; uint8_t padding[76]; // till 192 bytes + + void set_relay_method(relay_method method) noexcept; + relay_method get_relay_method() const noexcept; + + //! See `relay_category` description + bool matches(const relay_category category) const noexcept + { + return matches_category(get_relay_method(), category); + } }; + #define DBF_SAFE 1 #define DBF_FAST 2 #define DBF_FASTEST 4 @@ -1465,12 +1487,12 @@ public: /** * @brief get the number of transactions in the txpool */ - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const = 0; + virtual uint64_t get_txpool_tx_count(relay_category tx_category = relay_category::broadcasted) const = 0; /** - * @brief check whether a txid is in the txpool + * @brief check whether a txid is in the txpool and meets tx_category requirements */ - virtual bool txpool_has_tx(const crypto::hash &txid) const = 0; + virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const = 0; /** * @brief remove a txpool transaction @@ -1494,10 +1516,11 @@ public: * * @param txid the transaction id of the transation to lookup * @param bd the blob to return + * @param tx_category for filtering out hidden/private txes * - * @return true if the txid was in the txpool, false otherwise + * @return True iff `txid` is in the pool and meets `tx_category` requirements */ - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const = 0; + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const = 0; /** * @brief get a txpool transaction's blob @@ -1506,7 +1529,17 @@ public: * * @return the blob for that transaction */ - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const = 0; + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const = 0; + + /** + * @brief Check if `tx_hash` relay status is in `category`. + * + * @param tx_hash hash of the transaction to lookup + * @param category relay status category to test against + * + * @return True if `tx_hash` latest relay status is in `category`. + */ + bool txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category); /** * @brief prune output data for the given amount @@ -1604,7 +1637,7 @@ public: * * @return false if the function returns false for any transaction, otherwise true */ - virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = true) const = 0; + virtual bool for_all_txpool_txes(std::function, bool include_blob = false, relay_category category = relay_category::broadcasted) const = 0; /** * @brief runs a function over all key images stored diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index f978ef307..e8667adcf 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1771,7 +1771,7 @@ void BlockchainLMDB::update_txpool_tx(const crypto::hash &txid, const txpool_tx_ } } -uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const +uint64_t BlockchainLMDB::get_txpool_tx_count(relay_category category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1781,7 +1781,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const TXN_PREFIX_RDONLY(); - if (include_unrelayed_txes) + if (category == relay_category::all) { // No filtering, we can get the number of tx the "fast" way MDB_stat db_stats; @@ -1807,7 +1807,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const if (result) throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str())); const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; - if (!meta.do_not_relay) + if (meta.matches(category)) ++num_entries; } } @@ -1816,7 +1816,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const return num_entries; } -bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const +bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid, relay_category tx_category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1825,11 +1825,21 @@ bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const RCURSOR(txpool_meta) MDB_val k = {sizeof(txid), (void *)&txid}; - auto result = mdb_cursor_get(m_cur_txpool_meta, &k, NULL, MDB_SET); + MDB_val v; + auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET); if (result != 0 && result != MDB_NOTFOUND) throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str())); + if (result == MDB_NOTFOUND) + return false; + + bool found = true; + if (tx_category != relay_category::all) + { + const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; + found = meta.matches(tx_category); + } TXN_POSTFIX_RDONLY(); - return result != MDB_NOTFOUND; + return found; } void BlockchainLMDB::remove_txpool_tx(const crypto::hash& txid) @@ -1883,7 +1893,7 @@ bool BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta return true; } -bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const +bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1893,6 +1903,21 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl MDB_val k = {sizeof(txid), (void *)&txid}; MDB_val v; + + // if filtering, make sure those requirements are met before copying blob + if (tx_category != relay_category::all) + { + auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return false; + if (result != 0) + throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str())); + + const txpool_tx_meta_t& meta = *(const txpool_tx_meta_t*)v.mv_data; + if (!meta.matches(tx_category)) + return false; + } + auto result = mdb_cursor_get(m_cur_txpool_blob, &k, &v, MDB_SET); if (result == MDB_NOTFOUND) return false; @@ -1904,10 +1929,10 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl return true; } -cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid) const +cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const { cryptonote::blobdata bd; - if (!get_txpool_tx_blob(txid, bd)) + if (!get_txpool_tx_blob(txid, bd, tx_category)) throw1(DB_ERROR("Tx not found in txpool: ")); return bd; } @@ -2245,7 +2270,7 @@ bool BlockchainLMDB::check_pruning() return prune_worker(prune_mode_check, 0); } -bool BlockchainLMDB::for_all_txpool_txes(std::function f, bool include_blob, bool include_unrelayed_txes) const +bool BlockchainLMDB::for_all_txpool_txes(std::function f, bool include_blob, relay_category category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -2269,8 +2294,7 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function f, bool include_blob = false, bool include_unrelayed_txes = true) const; + virtual bool for_all_txpool_txes(std::function f, bool include_blob = false, relay_category category = relay_category::broadcasted) const; virtual bool for_all_key_images(std::function) const; virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index ac19fae25..a5847dec6 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -126,14 +126,14 @@ public: virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) override {} virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) override {} - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override { return 0; } - virtual bool txpool_has_tx(const crypto::hash &txid) const override { return false; } + virtual uint64_t get_txpool_tx_count(relay_category tx_relay = relay_category::broadcasted) const override { return 0; } + virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const override { return false; } virtual void remove_txpool_tx(const crypto::hash& txid) override {} virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const override { return false; } - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const override { return false; } + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const override { return false; } virtual uint64_t get_database_size() const override { return 0; } - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const override { return ""; } - virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const override { return false; } + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; } + virtual bool for_all_txpool_txes(std::function, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; } virtual void add_block( const cryptonote::block& blk , size_t block_weight diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 5d039d7f4..852e9cf4f 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -174,7 +174,7 @@ int check_flush(cryptonote::core &core, std::vector &block for(auto& tx_blob: block_entry.txs) { tx_verification_context tvc = AUTO_VAL_INIT(tvc); - core.handle_incoming_tx(tx_blob, tvc, true, true, false); + core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true); if(tvc.m_verifivation_failed) { MERROR("transaction verification failed, tx_id = " diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d22158dfc..7357e44d0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -616,7 +616,7 @@ block Blockchain::pop_block_from_blockchain() // that might not be always true. Unlikely though, and always relaying // these again might cause a spike of traffic as many nodes re-relay // all the transactions in a popped block when a reorg happens. - bool r = m_tx_pool.add_tx(tx, tvc, true, true, false, version); + bool r = m_tx_pool.add_tx(tx, tvc, relay_method::block, true, version); if (!r) { LOG_ERROR("Error returning transaction to tx_pool"); @@ -1765,7 +1765,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id { cryptonote::tx_memory_pool::tx_details td; cryptonote::blobdata blob; - if (m_tx_pool.have_tx(txid)) + if (m_tx_pool.have_tx(txid, relay_category::legacy)) { if (m_tx_pool.get_transaction_info(txid, td)) { @@ -3641,7 +3641,7 @@ void Blockchain::return_tx_to_pool(std::vector> // all the transactions in a popped block when a reorg happens. const size_t weight = get_transaction_weight(tx.first, tx.second.size()); const crypto::hash tx_hash = get_transaction_hash(tx.first); - if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, true, true, false, version)) + if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, relay_method::block, true, version)) { MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx.first) << " to tx_pool"); } @@ -3661,7 +3661,7 @@ bool Blockchain::flush_txes_from_pool(const std::vector &txids) uint64_t fee; bool relayed, do_not_relay, double_spend_seen, pruned; MINFO("Removing txid " << txid << " from the pool"); - if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned)) + if(!m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; @@ -4895,9 +4895,9 @@ void Blockchain::remove_txpool_tx(const crypto::hash &txid) m_db->remove_txpool_tx(txid); } -uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const +uint64_t Blockchain::get_txpool_tx_count(bool include_sensitive) const { - return m_db->get_txpool_tx_count(include_unrelayed_txes); + return m_db->get_txpool_tx_count(include_sensitive ? relay_category::all : relay_category::broadcasted); } bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const @@ -4905,19 +4905,24 @@ bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t & return m_db->get_txpool_tx_meta(txid, meta); } -bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const +bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const { - return m_db->get_txpool_tx_blob(txid, bd); + return m_db->get_txpool_tx_blob(txid, bd, tx_category); } -cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid) const +cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const { - return m_db->get_txpool_tx_blob(txid); + return m_db->get_txpool_tx_blob(txid, tx_category); } -bool Blockchain::for_all_txpool_txes(std::function f, bool include_blob, bool include_unrelayed_txes) const +bool Blockchain::for_all_txpool_txes(std::function f, bool include_blob, relay_category tx_category) const { - return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes); + return m_db->for_all_txpool_txes(f, include_blob, tx_category); +} + +bool Blockchain::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category) +{ + return m_db->txpool_tx_matches_category(tx_hash, category); } void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync) @@ -5098,7 +5103,7 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get CRITICAL_REGION_LOCAL(m_tx_pool); std::vector txs; - m_tx_pool.get_transactions(txs); + m_tx_pool.get_transactions(txs, true); size_t tx_weight; uint64_t fee; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6467031c2..c68fa22e3 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -964,11 +964,12 @@ namespace cryptonote void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta); void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta); void remove_txpool_tx(const crypto::hash &txid); - uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; + uint64_t get_txpool_tx_count(bool include_sensitive = false) const; bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const; - bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const; - cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; - bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = true) const; + bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const; + cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const; + bool for_all_txpool_txes(std::function, bool include_blob = false, relay_category tx_category = relay_category::broadcasted) const; + bool txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category); bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes, const std::vector &weights); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 02620996e..cf23a652c 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include +#include #include "string_tools.h" using namespace epee; @@ -753,7 +754,7 @@ namespace cryptonote return false; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash) { tvc = {}; @@ -817,7 +818,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash) { if(!check_tx_syntax(tx)) { @@ -946,23 +947,29 @@ namespace cryptonote return ret; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_txs(const epee::span tx_blobs, epee::span tvc, relay_method tx_relay, bool relayed) { TRY_ENTRY(); - CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + + if (tx_blobs.size() != tvc.size()) + { + MERROR("tx_blobs and tx_verification_context spans must have equal size"); + return false; + } struct result { bool res; cryptonote::transaction tx; crypto::hash hash; }; std::vector results(tx_blobs.size()); - tvc.resize(tx_blobs.size()); + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::vector::const_iterator it = tx_blobs.begin(); + epee::span::const_iterator it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { tpool.submit(&waiter, [&, i, it] { try { - results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay); + results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash); } catch (const std::exception &e) { @@ -978,7 +985,7 @@ namespace cryptonote for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { if (!results[i].res) continue; - if(m_mempool.have_tx(results[i].hash)) + if(m_mempool.have_tx(results[i].hash, relay_category::legacy)) { LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool"); already_have[i] = true; @@ -993,7 +1000,7 @@ namespace cryptonote tpool.submit(&waiter, [&, i, it] { try { - results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay); + results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash); } catch (const std::exception &e) { @@ -1014,7 +1021,7 @@ namespace cryptonote tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res}); } if (!tx_info.empty()) - handle_incoming_tx_accumulated_batch(tx_info, keeped_by_block); + handle_incoming_tx_accumulated_batch(tx_info, tx_relay == relay_method::block); bool ok = true; it = tx_blobs.begin(); @@ -1024,13 +1031,14 @@ namespace cryptonote ok = false; continue; } - if (keeped_by_block) + if (tx_relay == relay_method::block) get_blockchain_storage().on_new_tx_from_block(results[i].tx); if (already_have[i]) continue; const uint64_t weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, it->blob.size()); - ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], keeped_by_block, relayed, do_not_relay); + ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], tx_relay, relayed); + if(tvc[i].m_verifivation_failed) {MERROR_VER("Transaction verification failed: " << results[i].hash);} else if(tvc[i].m_verifivation_impossible) @@ -1044,19 +1052,9 @@ namespace cryptonote CATCH_ENTRY_L0("core::handle_incoming_txs()", false); } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, relay_method tx_relay, bool relayed) { - std::vector tx_blobs; - tx_blobs.push_back(tx_blob); - std::vector tvcv(1); - bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay); - tvc = tvcv[0]; - return r; - } - //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) - { - return handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, keeped_by_block, relayed, do_not_relay); + return handle_incoming_txs({std::addressof(tx_blob), 1}, {std::addressof(tvc), 1}, tx_relay, relayed); } //----------------------------------------------------------------------------------------------- bool core::get_stat_info(core_stat_info& st_inf) const @@ -1246,13 +1244,13 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed) { crypto::hash tx_hash = get_transaction_hash(tx); blobdata bl; t_serializable_object_to_blob(tx, bl); size_t tx_weight = get_transaction_weight(tx, bl.size()); - return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, keeped_by_block, relayed, do_not_relay); + return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, tx_relay, relayed); } //----------------------------------------------------------------------------------------------- size_t core::get_blockchain_total_transactions() const @@ -1260,9 +1258,9 @@ namespace cryptonote return m_blockchain_storage.get_total_transactions(); } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed) { - if(m_mempool.have_tx(tx_hash)) + if(m_mempool.have_tx(tx_hash, relay_category::legacy)) { LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); return true; @@ -1275,40 +1273,62 @@ namespace cryptonote } uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); - return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version); + return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, tx_relay, relayed, version); } //----------------------------------------------------------------------------------------------- bool core::relay_txpool_transactions() { // we attempt to relay txes that should be relayed, but were not - std::vector> txs; + std::vector> txs; if (m_mempool.get_relayable_transactions(txs) && !txs.empty()) { - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); - tx_verification_context tvc = AUTO_VAL_INIT(tvc); - NOTIFY_NEW_TRANSACTIONS::request r; - for (auto it = txs.begin(); it != txs.end(); ++it) + NOTIFY_NEW_TRANSACTIONS::request public_req{}; + NOTIFY_NEW_TRANSACTIONS::request private_req{}; + for (auto& tx : txs) { - r.txs.push_back(it->second); + switch (std::get<2>(tx)) + { + default: + case relay_method::none: + break; + case relay_method::local: + private_req.txs.push_back(std::move(std::get<1>(tx))); + break; + case relay_method::block: + case relay_method::flood: + public_req.txs.push_back(std::move(std::get<1>(tx))); + break; + } } - get_protocol()->relay_transactions(r, fake_context); - m_mempool.set_relayed(txs); + + /* All txes are sent on randomized timers per connection in + `src/cryptonote_protocol/levin_notify.cpp.` They are either sent with + "white noise" delays or via diffusion (Dandelion++ fluff). So + re-relaying public and private _should_ be acceptable here. */ + const boost::uuids::uuid source = boost::uuids::nil_uuid(); + if (!public_req.txs.empty()) + get_protocol()->relay_transactions(public_req, source, epee::net_utils::zone::public_); + if (!private_req.txs.empty()) + get_protocol()->relay_transactions(private_req, source, epee::net_utils::zone::invalid); } return true; } //----------------------------------------------------------------------------------------------- - void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob) + void core::on_transactions_relayed(const epee::span tx_blobs, const relay_method tx_relay) { - std::vector> txs; - cryptonote::transaction tx; - crypto::hash tx_hash; - if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash)) + std::vector tx_hashes{}; + tx_hashes.resize(tx_blobs.size()); + + cryptonote::transaction tx{}; + for (std::size_t i = 0; i < tx_blobs.size(); ++i) { - LOG_ERROR("Failed to parse relayed transaction"); - return; + if (!parse_and_validate_tx_from_blob(tx_blobs[i], tx, tx_hashes[i])) + { + LOG_ERROR("Failed to parse relayed transaction"); + return; + } } - txs.push_back(std::make_pair(tx_hash, std::move(tx_blob))); - m_mempool.set_relayed(txs); + m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay); } //----------------------------------------------------------------------------------------------- bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) @@ -1369,7 +1389,7 @@ namespace cryptonote for (const auto &tx_hash: b.tx_hashes) { cryptonote::blobdata txblob; - CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob), "Transaction not found in pool"); + CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob, relay_category::all), "Transaction not found in pool"); bce.txs.push_back({txblob, crypto::null_hash}); } return bce; @@ -1573,14 +1593,14 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transaction(const crypto::hash &id, cryptonote::blobdata& tx) const + bool core::get_pool_transaction(const crypto::hash &id, cryptonote::blobdata& tx, relay_category tx_category) const { - return m_mempool.get_transaction(id, tx); + return m_mempool.get_transaction(id, tx, tx_category); } //----------------------------------------------------------------------------------------------- bool core::pool_has_tx(const crypto::hash &id) const { - return m_mempool.have_tx(id); + return m_mempool.have_tx(id, relay_category::legacy); } //----------------------------------------------------------------------------------------------- bool core::get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, bool include_sensitive_data) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index f69ac3509..4b67984ab 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -35,7 +35,9 @@ #include #include +#include "cryptonote_core/i_core_events.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "cryptonote_protocol/enums.h" #include "storages/portable_storage_template_helper.h" #include "common/download.h" #include "common/command_line.h" @@ -46,6 +48,7 @@ #include "cryptonote_basic/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" +#include "span.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -77,7 +80,7 @@ namespace cryptonote * limited to, communication among the Blockchain, the transaction pool, * any miners, and the network. */ - class core: public i_miner_handler + class core final: public i_miner_handler, public i_core_events { public: @@ -115,14 +118,29 @@ namespace cryptonote * * @param tx_blob the tx to handle * @param tvc metadata about the transaction's validity - * @param keeped_by_block if the transaction has been in a block + * @param tx_relay how the transaction was received * @param relayed whether or not the transaction was relayed to us - * @param do_not_relay whether to prevent the transaction from being relayed * * @return true if the transaction was accepted, false otherwise */ - bool handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, relay_method tx_relay, bool relayed); + + /** + * @brief handles a list of incoming transactions + * + * Parses incoming transactions and, if nothing is obviously wrong, + * passes them along to the transaction pool + * + * @pre `tx_blobs.size() == tvc.size()` + * + * @param tx_blobs the txs to handle + * @param tvc metadata about the transactions' validity + * @param tx_relay how the transaction was received. + * @param relayed whether or not the transactions were relayed to us + * + * @return true if the transactions were accepted, false otherwise + */ + bool handle_incoming_txs(epee::span tx_blobs, epee::span tvc, relay_method tx_relay, bool relayed); /** * @brief handles a list of incoming transactions @@ -132,13 +150,16 @@ namespace cryptonote * * @param tx_blobs the txs to handle * @param tvc metadata about the transactions' validity - * @param keeped_by_block if the transactions have been in a block + * @param tx_relay how the transaction was received. * @param relayed whether or not the transactions were relayed to us - * @param do_not_relay whether to prevent the transactions from being relayed * * @return true if the transactions were accepted, false otherwise */ - bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, relay_method tx_relay, bool relayed) + { + tvc.resize(tx_blobs.size()); + return handle_incoming_txs(epee::to_span(tx_blobs), epee::to_mut_span(tvc), tx_relay, relayed); + } /** * @brief handles an incoming block @@ -212,9 +233,10 @@ namespace cryptonote virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** - * @brief called when a transaction is relayed + * @brief called when a transaction is relayed. + * @note Should only be invoked from `levin_notify`. */ - virtual void on_transaction_relayed(const cryptonote::blobdata& tx); + virtual void on_transactions_relayed(epee::span tx_blobs, relay_method tx_relay) final; /** @@ -440,11 +462,11 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transactions(std::vector& txs, bool include_unrelayed_txes = true) const; + bool get_pool_transactions(std::vector& txs, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_txpool_backlog @@ -455,34 +477,34 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const; + bool get_pool_transaction_hashes(std::vector& txs, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_transactions - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; + bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_transaction * * @note see tx_memory_pool::get_transaction */ - bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx) const; + bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx, relay_category tx_category) const; /** * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info */ - bool get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, bool include_unrelayed_txes = true) const; + bool get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_pool_for_rpc @@ -852,11 +874,11 @@ namespace cryptonote * @param tx_hash the transaction's hash * @param blob the transaction as a blob * @param tx_weight the weight of the transaction + * @param tx_relay how the transaction was received * @param relayed whether or not the transaction was relayed to us - * @param do_not_relay whether to prevent the transaction from being relayed * */ - bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed); /** * @brief add a new transaction to the transaction pool @@ -865,15 +887,14 @@ namespace cryptonote * * @param tx the transaction to add * @param tvc return-by-reference metadata about the transaction's verification process - * @param keeped_by_block whether or not the transaction has been in a block + * @param tx_relay how the transaction was received * @param relayed whether or not the transaction was relayed to us - * @param do_not_relay whether to prevent the transaction from being relayed * * @return true if the transaction is already in the transaction pool, * is already in a block on the Blockchain, or is successfully added * to the transaction pool */ - bool add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool add_new_tx(transaction& tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed); /** * @copydoc Blockchain::add_new_block @@ -929,8 +950,8 @@ namespace cryptonote bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; void set_semantics_failed(const crypto::hash &tx_hash); - bool handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_tx_post(const tx_blob_entry &tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash); + bool handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash); struct tx_verification_batch_info { const cryptonote::transaction *tx; crypto::hash tx_hash; tx_verification_context &tvc; bool &result; }; bool handle_incoming_tx_accumulated_batch(std::vector &tx_info, bool keeped_by_block); diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h new file mode 100644 index 000000000..f49fbdf07 --- /dev/null +++ b/src/cryptonote_core/i_core_events.h @@ -0,0 +1,44 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#pragma once + +#include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_protocol/enums.h" +#include "span.h" + +namespace cryptonote +{ + struct i_core_events + { + virtual ~i_core_events() noexcept + {} + + virtual void on_transactions_relayed(epee::span tx_blobs, relay_method tx_relay) = 0; + }; +} diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 392e611e9..815b03335 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -115,8 +115,10 @@ namespace cryptonote } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) { + const bool kept_by_block = (tx_relay == relay_method::block); + // this should already be called with that lock, but let's make it explicit for clarity CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -227,7 +229,7 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; - cryptonote::txpool_tx_meta_t meta; + cryptonote::txpool_tx_meta_t meta{}; bool ch_inp_res = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, kept_by_block); if(!ch_inp_res) { @@ -241,11 +243,10 @@ namespace cryptonote meta.max_used_block_height = 0; meta.last_failed_height = 0; meta.last_failed_id = null_hash; - meta.kept_by_block = kept_by_block; meta.receive_time = receive_time; meta.last_relayed_time = time(NULL); meta.relayed = relayed; - meta.do_not_relay = do_not_relay; + meta.set_relay_method(tx_relay); meta.double_spend_seen = have_tx_keyimges_as_spent(tx); meta.pruned = tx.pruned; meta.bf_padding = 0; @@ -256,9 +257,10 @@ namespace cryptonote m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); - m_blockchain.add_txpool_tx(id, blob, meta); - if (!insert_key_images(tx, id, kept_by_block)) + if (!insert_key_images(tx, id, tx_relay)) return false; + + m_blockchain.add_txpool_tx(id, blob, meta); m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id); lock.commit(); } @@ -280,7 +282,6 @@ namespace cryptonote { //update transactions container meta.weight = tx_weight; - meta.kept_by_block = kept_by_block; meta.fee = fee; meta.max_used_block_id = max_used_block_id; meta.max_used_block_height = max_used_block_height; @@ -289,7 +290,7 @@ namespace cryptonote meta.receive_time = receive_time; meta.last_relayed_time = time(NULL); meta.relayed = relayed; - meta.do_not_relay = do_not_relay; + meta.set_relay_method(tx_relay); meta.double_spend_seen = false; meta.pruned = tx.pruned; meta.bf_padding = 0; @@ -302,9 +303,10 @@ namespace cryptonote CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); m_blockchain.remove_txpool_tx(id); - m_blockchain.add_txpool_tx(id, blob, meta); - if (!insert_key_images(tx, id, kept_by_block)) + if (!insert_key_images(tx, id, tx_relay)) return false; + + m_blockchain.add_txpool_tx(id, blob, meta); m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id); lock.commit(); } @@ -315,7 +317,7 @@ namespace cryptonote } tvc.m_added_to_pool = true; - if(meta.fee > 0 && !do_not_relay) + if(meta.fee > 0 && tx_relay != relay_method::none) tvc.m_should_be_relayed = true; } @@ -331,7 +333,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) { crypto::hash h = null_hash; size_t blob_size = 0; @@ -339,7 +341,7 @@ namespace cryptonote t_serializable_object_to_blob(tx, bl); if (bl.size() == 0 || !get_transaction_hash(tx, h)) return false; - return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, keeped_by_block, relayed, do_not_relay, version); + return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, tx_relay, relayed, version); } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_txpool_weight() const @@ -375,7 +377,7 @@ namespace cryptonote txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(txid, meta)) { - MERROR("Failed to find tx in txpool"); + MERROR("Failed to find tx_meta in txpool"); return; } // don't prune the kept_by_block ones, they're likely added because we're adding a block with those @@ -384,7 +386,7 @@ namespace cryptonote --it; continue; } - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); cryptonote::transaction_prefix tx; if (!parse_and_validate_tx_prefix_from_blob(txblob, tx)) { @@ -413,17 +415,38 @@ namespace cryptonote MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, bool kept_by_block) + bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, relay_method tx_relay) { for(const auto& in: tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); std::unordered_set& kei_image_set = m_spent_key_images[txin.k_image]; - CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block - << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL - << "tx_id=" << id ); - auto ins_res = kei_image_set.insert(id); - CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); + + /* If any existing key-image in the set is publicly visible AND this is + not forcibly "kept_by_block", then fail (duplicate key image). If all + existing key images are supposed to be hidden, we silently allow so + that the node doesn't leak knowledge of a local/stem tx. */ + bool visible = false; + if (tx_relay != relay_method::block) + { + for (const crypto::hash& other_id : kei_image_set) + visible |= m_blockchain.txpool_tx_matches_category(other_id, relay_category::legacy); + } + + CHECK_AND_ASSERT_MES(!visible, false, "internal error: tx_relay=" << unsigned(tx_relay) + << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL + << "tx_id=" << id); + + /* If adding a tx (hash) that already exists, fail only if the tx has + been publicly "broadcast" previously. This way, when a private tx is + received for the first time from a remote node, "this" node will + respond as-if it were seen for the first time. LMDB does the + "hard-check" on key-images, so the effect is overwriting the existing + tx_pool metadata and "first seen" time. */ + const bool new_or_previously_private = + kei_image_set.insert(id).second || + !m_blockchain.txpool_tx_matches_category(id, relay_category::legacy); + CHECK_AND_ASSERT_MES(new_or_previously_private, false, "internal error: try to insert duplicate iterator in key_image set"); } ++m_cookie; return true; @@ -475,10 +498,10 @@ namespace cryptonote txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(id, meta)) { - MERROR("Failed to find tx in txpool"); + MERROR("Failed to find tx_meta in txpool"); return false; } - txblob = m_blockchain.get_txpool_tx_blob(id); + txblob = m_blockchain.get_txpool_tx_blob(id, relay_category::all); auto ci = m_parsed_tx_cache.find(id); if (ci != m_parsed_tx_cache.end()) { @@ -533,7 +556,7 @@ namespace cryptonote MERROR("Failed to find tx in txpool"); return false; } - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); auto ci = m_parsed_tx_cache.find(txid); if (ci != m_parsed_tx_cache.end()) { @@ -611,7 +634,7 @@ namespace cryptonote remove.push_back(std::make_pair(txid, meta.weight)); } return true; - }, false); + }, false, relay_category::all); if (!remove.empty()) { @@ -621,7 +644,7 @@ namespace cryptonote const crypto::hash &txid = entry.first; try { - cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); cryptonote::transaction_prefix tx; if (!parse_and_validate_tx_prefix_from_blob(bd, tx)) { @@ -649,7 +672,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_relayable_transactions(std::vector> &txs) const + bool tx_memory_pool::get_relayable_transactions(std::vector> &txs) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -667,8 +690,7 @@ namespace cryptonote { try { - cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid); - txs.push_back(std::make_pair(txid, bd)); + txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), meta.get_relay_method()); } catch (const std::exception &e) { @@ -678,26 +700,27 @@ namespace cryptonote } } return true; - }, false); + }, false, relay_category::relayable); return true; } //--------------------------------------------------------------------------------- - void tx_memory_pool::set_relayed(const std::vector> &txs) + void tx_memory_pool::set_relayed(const epee::span hashes, const relay_method method) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const time_t now = time(NULL); LockedTXN lock(m_blockchain); - for (auto it = txs.begin(); it != txs.end(); ++it) + for (const auto& hash : hashes) { try { txpool_tx_meta_t meta; - if (m_blockchain.get_txpool_tx_meta(it->first, meta)) + if (m_blockchain.get_txpool_tx_meta(hash, meta)) { meta.relayed = true; meta.last_relayed_time = now; - m_blockchain.update_txpool_tx(it->first, meta); + meta.set_relay_method(method); + m_blockchain.update_txpool_tx(hash, meta); } } catch (const std::exception &e) @@ -709,18 +732,19 @@ namespace cryptonote lock.commit(); } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const + size_t tx_memory_pool::get_transactions_count(bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - return m_blockchain.get_txpool_tx_count(include_unrelayed_txes); + return m_blockchain.get_txpool_tx_count(include_sensitive); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::vector& txs, bool include_unrelayed_txes) const + void tx_memory_pool::get_transactions(std::vector& txs, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; + txs.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ transaction tx; if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx))) @@ -732,39 +756,42 @@ namespace cryptonote tx.set_hash(txid); txs.push_back(std::move(tx)); return true; - }, true, include_unrelayed_txes); + }, true, category); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_hashes(std::vector& txs, bool include_unrelayed_txes) const + void tx_memory_pool::get_transaction_hashes(std::vector& txs, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; + txs.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ txs.push_back(txid); return true; - }, false, include_unrelayed_txes); + }, false, category); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_backlog(std::vector& backlog, bool include_unrelayed_txes) const + void tx_memory_pool::get_transaction_backlog(std::vector& backlog, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); - backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; + backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ backlog.push_back({meta.weight, meta.fee, meta.receive_time - now}); return true; - }, false, include_unrelayed_txes); + }, false, category); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const + void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; std::map agebytes; - stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); + stats.txs_total = m_blockchain.get_txpool_tx_count(include_sensitive); std::vector weights; weights.reserve(stats.txs_total); m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ @@ -789,7 +816,8 @@ namespace cryptonote if (meta.double_spend_seen) ++stats.num_double_spends; return true; - }, false, include_unrelayed_txes); + }, false, category); + stats.bytes_med = epee::misc_utils::median(weights); if (stats.txs_total > 1) { @@ -847,8 +875,10 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - tx_infos.reserve(m_blockchain.get_txpool_tx_count()); - key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); + const relay_category category = include_sensitive_data ? relay_category::all : relay_category::broadcasted; + const size_t count = m_blockchain.get_txpool_tx_count(include_sensitive_data); + tx_infos.reserve(count); + key_image_infos.reserve(count); m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); @@ -879,7 +909,7 @@ namespace cryptonote txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(std::move(txi)); return true; - }, true, include_sensitive_data); + }, true, category); txpool_tx_meta_t meta; for (const key_images_container::value_type& kee : m_spent_key_images) { @@ -889,30 +919,13 @@ namespace cryptonote ki.id_hash = epee::string_tools::pod_to_hex(k_image); for (const crypto::hash& tx_id_hash : kei_image_set) { - if (!include_sensitive_data) - { - try - { - if (!m_blockchain.get_txpool_tx_meta(tx_id_hash, meta)) - { - MERROR("Failed to get tx meta from txpool"); - return false; - } - if (!meta.relayed) - // Do not include that transaction if in restricted mode and it's not relayed - continue; - } - catch (const std::exception &e) - { - MERROR("Failed to get tx meta from txpool: " << e.what()); - return false; - } - } - ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); + if (m_blockchain.txpool_tx_matches_category(tx_id_hash, category)) + ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); } + // Only return key images for which we have at least one tx that we can show for them if (!ki.txs_hashes.empty()) - key_image_infos.push_back(ki); + key_image_infos.push_back(std::move(ki)); } return true; } @@ -948,18 +961,19 @@ namespace cryptonote txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(txi); return true; - }, true, false); + }, true, relay_category::broadcasted); for (const key_images_container::value_type& kee : m_spent_key_images) { std::vector tx_hashes; const std::unordered_set& kei_image_set = kee.second; for (const crypto::hash& tx_id_hash : kei_image_set) { - tx_hashes.push_back(tx_id_hash); + if (m_blockchain.txpool_tx_matches_category(tx_id_hash, relay_category::broadcasted)) + tx_hashes.push_back(tx_id_hash); } - const crypto::key_image& k_image = kee.first; - key_image_infos[k_image] = std::move(tx_hashes); + if (!tx_hashes.empty()) + key_image_infos[kee.first] = std::move(tx_hashes); } return true; } @@ -973,19 +987,26 @@ namespace cryptonote for (const auto& image : key_images) { - spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true); + bool is_spent = false; + const auto found = m_spent_key_images.find(image); + if (found != m_spent_key_images.end()) + { + for (const crypto::hash& tx_hash : found->second) + is_spent |= m_blockchain.txpool_tx_matches_category(tx_hash, relay_category::broadcasted); + } + spent.push_back(is_spent); } return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const + bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob, relay_category tx_category) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); try { - return m_blockchain.get_txpool_tx_blob(id, txblob); + return m_blockchain.get_txpool_tx_blob(id, txblob, tx_category); } catch (const std::exception &e) { @@ -1009,11 +1030,11 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx(const crypto::hash &id) const + bool tx_memory_pool::have_tx(const crypto::hash &id, relay_category tx_category) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - return m_blockchain.get_db().txpool_has_tx(id); + return m_blockchain.get_db().txpool_has_tx(id, tx_category); } //--------------------------------------------------------------------------------- bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) const @@ -1032,7 +1053,14 @@ namespace cryptonote bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) const { CRITICAL_REGION_LOCAL(m_transactions_lock); - return m_spent_key_images.end() != m_spent_key_images.find(key_im); + bool spent = false; + const auto found = m_spent_key_images.find(key_im); + if (found != m_spent_key_images.end()) + { + for (const crypto::hash& tx_hash : found->second) + spent |= m_blockchain.txpool_tx_matches_category(tx_hash, relay_category::broadcasted); + } + return spent; } //--------------------------------------------------------------------------------- void tx_memory_pool::lock() const @@ -1217,13 +1245,14 @@ namespace cryptonote << "weight: " << meta.weight << std::endl << "fee: " << print_money(meta.fee) << std::endl << "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl + << "is_local" << (meta.is_local ? 'T' : 'F') << std::endl << "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl << "max_used_block_height: " << meta.max_used_block_height << std::endl << "max_used_block_id: " << meta.max_used_block_id << std::endl << "last_failed_height: " << meta.last_failed_height << std::endl << "last_failed_id: " << meta.last_failed_id << std::endl; return true; - }, !short_format); + }, !short_format, relay_category::all); return ss.str(); } @@ -1255,7 +1284,7 @@ namespace cryptonote for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) { txpool_tx_meta_t meta; - if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta)) + if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta) && !meta.matches(relay_category::legacy)) { MERROR(" failed to find tx meta"); continue; @@ -1304,7 +1333,9 @@ namespace cryptonote } } - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second); + // "local" and "stem" txes are filtered above + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second, relay_category::all); + cryptonote::transaction tx; // Skip transactions that are not ready to be @@ -1379,7 +1410,7 @@ namespace cryptonote remove.insert(txid); } return true; - }, false); + }, false, relay_category::all); size_t n_removed = 0; if (!remove.empty()) @@ -1389,7 +1420,7 @@ namespace cryptonote { try { - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); cryptonote::transaction tx; if (!parse_and_validate_tx_from_blob(txblob, tx)) // remove pruned ones on startup, they're meant to be temporary { @@ -1450,7 +1481,7 @@ namespace cryptonote remove.push_back(txid); return true; } - if (!insert_key_images(tx, txid, meta.kept_by_block)) + if (!insert_key_images(tx, txid, meta.get_relay_method())) { MFATAL("Failed to insert key images from txpool tx"); return false; @@ -1458,7 +1489,7 @@ namespace cryptonote m_txs_by_fee_and_receive_time.emplace(std::pair(meta.fee / (double)meta.weight, meta.receive_time), txid); m_txpool_weight += meta.weight; return true; - }, true); + }, true, relay_category::all); if (!r) return false; } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index dec7e3cd9..f716440ad 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -32,17 +32,20 @@ #include "include_base_utils.h" #include +#include #include #include #include #include #include +#include "span.h" #include "string_tools.h" #include "syncobj.h" #include "math_helper.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/verification_context.h" +#include "cryptonote_protocol/enums.h" #include "blockchain_db/blockchain_db.h" #include "crypto/hash.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -105,9 +108,10 @@ namespace cryptonote * @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t) * * @param id the transaction's hash + * @tx_relay how the transaction was received * @param tx_weight the transaction's weight */ - bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version); /** * @brief add a transaction to the transaction pool @@ -119,14 +123,13 @@ namespace cryptonote * * @param tx the transaction to be added * @param tvc return-by-reference status about the transaction verification - * @param kept_by_block has this transaction been in a block? + * @tx_relay how the transaction was received * @param relayed was this transaction from the network or a local client? - * @param do_not_relay to avoid relaying the transaction to the network * @param version the version used to create the transaction * * @return true if the transaction passes validations, otherwise false */ - bool add_tx(transaction &tx, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version); /** * @brief takes a transaction with the given hash from the pool @@ -149,10 +152,11 @@ namespace cryptonote * @brief checks if the pool has a transaction with the given hash * * @param id the hash to look for + * @param tx_category a filter for txes * - * @return true if the transaction is in the pool, otherwise false + * @return true if the transaction is in the pool and meets tx_category requirements */ - bool have_tx(const crypto::hash &id) const; + bool have_tx(const crypto::hash &id, relay_category tx_category) const; /** * @brief action to take when notified of a block added to the blockchain @@ -236,37 +240,37 @@ namespace cryptonote * @brief get a list of all transactions in the pool * * @param txs return-by-reference the list of transactions - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transactions(std::vector& txs, bool include_unrelayed_txes = true) const; + void get_transactions(std::vector& txs, bool include_sensitive = false) const; /** * @brief get a list of all transaction hashes in the pool * * @param txs return-by-reference the list of transactions - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const; + void get_transaction_hashes(std::vector& txs, bool include_sensitive = false) const; /** * @brief get (weight, fee, receive time) for all transaction in the pool * * @param txs return-by-reference that data - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transaction_backlog(std::vector& backlog, bool include_unrelayed_txes = true) const; + void get_transaction_backlog(std::vector& backlog, bool include_sensitive = false) const; /** * @brief get a summary statistics of all transaction hashes in the pool * * @param stats return-by-reference the pool statistics - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; + void get_transaction_stats(struct txpool_stats& stats, bool include_sensitive = false) const; /** * @brief get information about all transactions and key images in the pool @@ -275,11 +279,12 @@ namespace cryptonote * * @param tx_infos return-by-reference the transactions' information * @param key_image_infos return-by-reference the spent key images' information - * @param include_sensitive_data include unrelayed txes and fields that are sensitive to the node privacy + * @param include_sensitive_data return stempool, anonymity-pool, and unrelayed + * txes and fields that are sensitive to the node privacy * * @return true */ - bool get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, bool include_sensitive_data = true) const; + bool get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos, bool include_sensitive_data = false) const; /** * @brief get information about all transactions and key images in the pool @@ -308,10 +313,11 @@ namespace cryptonote * * @param h the hash of the transaction to get * @param tx return-by-reference the transaction blob requested + * @param tx_relay last relay method us * * @return true if the transaction is found, otherwise false */ - bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob) const; + bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob, relay_category tx_category) const; /** * @brief get a list of all relayable transactions and their hashes @@ -326,21 +332,22 @@ namespace cryptonote * * @return true */ - bool get_relayable_transactions(std::vector>& txs) const; + bool get_relayable_transactions(std::vector>& txs) const; /** * @brief tell the pool that certain transactions were just relayed * - * @param txs the list of transactions (and their hashes) + * @param hashes list of tx hashes that are about to be relayed + * @param tx_relay update how the tx left this node */ - void set_relayed(const std::vector>& txs); + void set_relayed(epee::span hashes, relay_method tx_relay); /** * @brief get the total number of transactions in the pool * * @return the number of transactions in the pool */ - size_t get_transactions_count(bool include_unrelayed_txes = true) const; + size_t get_transactions_count(bool include_sensitive = false) const; /** * @brief get a string containing human-readable pool information @@ -441,7 +448,7 @@ namespace cryptonote * * @return true on success, false on error */ - bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, bool kept_by_block); + bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, relay_method tx_relay); /** * @brief remove old transactions from the pool @@ -544,7 +551,7 @@ namespace cryptonote * transaction on the assumption that the original will not be in a * block again. */ - typedef std::unordered_map > key_images_container; + typedef std::unordered_map> key_images_container; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) public: diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 3b456e324..ddbd45a61 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -130,7 +130,7 @@ namespace cryptonote //----------------- i_bc_protocol_layout --------------------------------------- virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context); + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone); //---------------------------------------------------------------------------------- //bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context); bool should_drop_connection(cryptonote_connection_context& context, uint32_t next_stripe); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 74ceeb41d..e20934a25 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -455,7 +455,7 @@ namespace cryptonote for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) { cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(*tx_blob_it, tvc, true, true, false); + m_core.handle_incoming_tx(*tx_blob_it, tvc, relay_method::block, true); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); @@ -619,7 +619,7 @@ namespace cryptonote { MDEBUG("Incoming tx " << tx_hash << " not in pool, adding"); cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed) + if(!m_core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true) || tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); drop_connection(context, false, false); @@ -667,13 +667,13 @@ namespace cryptonote drop_connection(context, false, false); m_core.resume_mine(); return 1; - } - + } + size_t tx_idx = 0; for(auto& tx_hash: new_block.tx_hashes) { cryptonote::blobdata txblob; - if(m_core.get_pool_transaction(tx_hash, txblob)) + if(m_core.get_pool_transaction(tx_hash, txblob, relay_category::broadcasted)) { have_tx.push_back({txblob, crypto::null_hash}); } @@ -702,7 +702,7 @@ namespace cryptonote need_tx_indices.push_back(tx_idx); } } - + ++tx_idx; } @@ -909,8 +909,8 @@ namespace cryptonote newtxs.reserve(arg.txs.size()); for (size_t i = 0; i < arg.txs.size(); ++i) { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, false, true, false); + cryptonote::tx_verification_context tvc{}; + m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::flood, true); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); @@ -925,7 +925,7 @@ namespace cryptonote if(arg.txs.size()) { //TODO: add announce usage here - relay_transactions(arg, context); + relay_transactions(arg, context.m_connection_id, context.m_remote_address.get_zone()); } return 1; @@ -1316,7 +1316,7 @@ namespace cryptonote TIME_MEASURE_START(transactions_process_time); num_txs += block_entry.txs.size(); std::vector tvc; - m_core.handle_incoming_txs(block_entry.txs, tvc, true, true, false); + m_core.handle_incoming_txs(block_entry.txs, tvc, relay_method::block, true); if (tvc.size() != block_entry.txs.size()) { LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()"); @@ -2344,14 +2344,14 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template - bool t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + bool t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone) { - for(auto& tx_blob : arg.txs) - m_core.on_transaction_relayed(tx_blob); - - // no check for success, so tell core they're relayed unconditionally - m_p2p->send_txs(std::move(arg.txs), exclude_context.m_remote_address.get_zone(), exclude_context.m_connection_id, m_core.pad_transactions()); - return true; + /* Push all outgoing transactions to this function. The behavior needs to + identify how the transaction is going to be relayed, and then update the + local mempool before doing the relay. The code was already updating the + DB twice on received transactions - it is difficult to workaround this + due to the internal design. */ + return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core, m_core.pad_transactions()) != epee::net_utils::zone::invalid; } //------------------------------------------------------------------------------------------------------------------------ template diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index a67178c52..978a9ebf3 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -41,7 +41,7 @@ namespace cryptonote struct i_cryptonote_protocol { virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0; - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0; + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone)=0; //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; }; @@ -54,7 +54,7 @@ namespace cryptonote { return false; } - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone) { return false; } diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h new file mode 100644 index 000000000..ad4eedf4c --- /dev/null +++ b/src/cryptonote_protocol/enums.h @@ -0,0 +1,43 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#pragma once + +#include + +namespace cryptonote +{ + //! Methods tracking how a tx was received and relayed + enum class relay_method : std::uint8_t + { + none = 0, //!< Received via RPC with `do_not_relay` set + local, //!< Received via RPC; trying to send over i2p/tor, etc. + block, //!< Received in block, takes precedence over others + flood //!< Received/sent over public networks + }; +} diff --git a/src/cryptonote_protocol/fwd.h b/src/cryptonote_protocol/fwd.h new file mode 100644 index 000000000..616b48be3 --- /dev/null +++ b/src/cryptonote_protocol/fwd.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#pragma once + +namespace cryptonote +{ + class core; + struct cryptonote_connection_context; + struct i_core_events; +} + diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 484243af5..8bc9b72fa 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -35,6 +35,7 @@ #include "byte_slice.h" #include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_protocol/fwd.h" #include "net/enums.h" #include "span.h" @@ -51,11 +52,6 @@ namespace nodetool template struct p2p_connection_context_t; } -namespace cryptonote -{ - struct cryptonote_connection_context; -} - namespace cryptonote { namespace levin diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 8d861ce29..ee70c2806 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -43,6 +43,7 @@ #include #include "cryptonote_config.h" +#include "cryptonote_protocol/fwd.h" #include "cryptonote_protocol/levin_notify.h" #include "warnings.h" #include "net/abstract_tcp_server2.h" @@ -336,7 +337,7 @@ namespace nodetool virtual void callback(p2p_connection_context& context); //----------------- i_p2p_endpoint ------------------------------------------------------------- virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections); - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs); + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs); virtual bool invoke_command_to_peer(int command, const epee::span req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); virtual bool invoke_notify_to_peer(int command, const epee::span req_buff, const epee::net_utils::connection_context_base& context); virtual bool drop_connection(const epee::net_utils::connection_context_base& context); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f8094bfa8..45bb10593 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -2053,13 +2053,18 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - epee::net_utils::zone node_server::send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs) + epee::net_utils::zone node_server::send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs) { namespace enet = epee::net_utils; - const auto send = [&txs, &source, pad_txs] (std::pair& network) + const auto send = [&txs, &source, &core, pad_txs] (std::pair& network) { - if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || network.first != enet::zone::public_))) + const bool is_public = (network.first == enet::zone::public_); + const cryptonote::relay_method tx_relay = is_public ? + cryptonote::relay_method::flood : cryptonote::relay_method::local; + + core.on_transactions_relayed(epee::to_span(txs), tx_relay); + if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || !is_public))) return network.first; return enet::zone::invalid; }; diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 752873666..aa1b83b83 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -34,6 +34,8 @@ #include #include #include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_protocol/enums.h" +#include "cryptonote_protocol/fwd.h" #include "net/enums.h" #include "net/net_utils_base.h" #include "p2p_protocol_defs.h" @@ -48,7 +50,7 @@ namespace nodetool struct i_p2p_endpoint { virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections)=0; - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs)=0; + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs)=0; virtual bool invoke_command_to_peer(int command, const epee::span req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool invoke_notify_to_peer(int command, const epee::span req_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; @@ -73,7 +75,7 @@ namespace nodetool { return false; } - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs) + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs) { return epee::net_utils::zone::invalid; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..dc93e7023 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include +#include #include "include_base_utils.h" #include "string_tools.h" using namespace epee; @@ -1095,9 +1096,8 @@ namespace cryptonote } res.sanity_check_failed = false; - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); - tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed) + tx_verification_context tvc{}; + if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (req.do_not_relay ? relay_method::none : relay_method::local), false) || tvc.m_verifivation_failed) { res.status = "Failed"; std::string reason = ""; @@ -1142,7 +1142,7 @@ namespace cryptonote NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, fake_context); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = CORE_RPC_STATUS_OK; return true; @@ -2370,7 +2370,7 @@ namespace cryptonote if (req.txids.empty()) { std::vector pool_txs; - bool r = m_core.get_pool_transactions(pool_txs); + bool r = m_core.get_pool_transactions(pool_txs, true); if (!r) { res.status = "Failed to get txpool contents"; @@ -2747,13 +2747,11 @@ namespace cryptonote crypto::hash txid = *reinterpret_cast(txid_data.data()); cryptonote::blobdata txblob; - bool r = m_core.get_pool_transaction(txid, txblob); - if (r) + if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) { - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(txblob); - m_core.get_protocol()->relay_transactions(r, fake_context); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes } else diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index d7e081af3..0b6cd6c7a 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -28,6 +28,7 @@ #include "daemon_handler.h" +#include // likely included by daemon_handler.h's includes, // but including here for clarity #include "cryptonote_core/cryptonote_core.h" @@ -288,10 +289,9 @@ namespace rpc return; } - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, false, false, !relay) || tvc.m_verifivation_failed) + if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (relay ? relay_method::local : relay_method::none), false) || tvc.m_verifivation_failed) { if (tvc.m_verifivation_failed) { @@ -368,7 +368,7 @@ namespace rpc NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, fake_context); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = Message::STATUS_OK; diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index db5b7797a..9463d14ce 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -160,8 +160,8 @@ string tx2str(const cryptonote::transaction& tx, const cryptonote::hash256& tx_h return ss.str(); }*/ -bool tests::proxy_core::handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { - if (!keeped_by_block) +bool tests::proxy_core::handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { + if (tx_relay != cryptonote::relay_method::block) return true; crypto::hash tx_hash = null_hash; @@ -190,13 +190,13 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::tx_blob_entry& tx_b return true; } -bool tests::proxy_core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) +bool tests::proxy_core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, cryptonote::relay_method tx_relay, bool relayed) { tvc.resize(tx_blobs.size()); size_t i = 0; for (const auto &tx_blob: tx_blobs) { - if (!handle_incoming_tx(tx_blob, tvc[i], keeped_by_block, relayed, do_not_relay)) + if (!handle_incoming_tx(tx_blob, tvc[i], tx_relay, relayed)) return false; ++i; } diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 0e41a2be0..8732c85cc 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -34,6 +34,7 @@ #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/verification_context.h" +#include "cryptonote_core/i_core_events.h" #include namespace tests @@ -51,7 +52,7 @@ namespace tests : height(_height), id(_id), longhash(_longhash), blk(_blk), blob(_blob), txes(_txes) { } }; - class proxy_core + class proxy_core : public cryptonote::i_core_events { cryptonote::block m_genesis; std::list m_known_block_list; @@ -75,8 +76,8 @@ namespace tests bool get_stat_info(cryptonote::core_stat_info& st_inf){return true;} bool have_block(const crypto::hash& id); void get_blockchain_top(uint64_t& height, crypto::hash& top_id); - bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed); + bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, cryptonote::relay_method tx_relay, bool relayed); bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); void pause_mine(){} void resume_mine(){} @@ -90,9 +91,9 @@ namespace tests bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } - virtual void on_transaction_relayed(const cryptonote::blobdata& tx) {} + virtual void on_transactions_relayed(epee::span tx_blobs, cryptonote::relay_method tx_relay) {} cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } - bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob) const { return false; } + bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob, cryptonote::relay_category tx_category) const { return false; } bool pool_has_tx(const crypto::hash &txid) const { return false; } bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return false; } diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index f93cbf3ad..42e76e613 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -40,6 +40,7 @@ set(core_tests_sources ring_signature_1.cpp transaction_tests.cpp tx_validation.cpp + tx_pool.cpp v2_tests.cpp rct.cpp bulletproofs.cpp @@ -57,6 +58,7 @@ set(core_tests_headers integer_overflow.h multisig.h ring_signature_1.h + tx_pool.h transaction_tests.h tx_validation.h v2_tests.h diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index b78640dc9..bc0e61365 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -53,6 +53,7 @@ #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/enums.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "misc_language.h" @@ -108,17 +109,17 @@ typedef serialized_object serialized_transaction; struct event_visitor_settings { - int valid_mask; - bool txs_keeped_by_block; + int mask; enum settings { - set_txs_keeped_by_block = 1 << 0 + set_txs_keeped_by_block = 1 << 0, + set_txs_do_not_relay = 1 << 1, + set_local_relay = 1 << 2 }; - event_visitor_settings(int a_valid_mask = 0, bool a_txs_keeped_by_block = false) - : valid_mask(a_valid_mask) - , txs_keeped_by_block(a_txs_keeped_by_block) + event_visitor_settings(int a_mask = 0) + : mask(a_mask) { } @@ -128,8 +129,7 @@ private: template void serialize(Archive & ar, const unsigned int /*version*/) { - ar & valid_mask; - ar & txs_keeped_by_block; + ar & mask; } }; @@ -503,7 +503,7 @@ private: t_test_class& m_validator; size_t m_ev_index; - bool m_txs_keeped_by_block; + cryptonote::relay_method m_tx_relay; public: push_core_event_visitor(cryptonote::core& c, const std::vector& events, t_test_class& validator) @@ -511,7 +511,7 @@ public: , m_events(events) , m_validator(validator) , m_ev_index(0) - , m_txs_keeped_by_block(false) + , m_tx_relay(cryptonote::relay_method::flood) { } @@ -530,9 +530,21 @@ public: { log_event("event_visitor_settings"); - if (settings.valid_mask & event_visitor_settings::set_txs_keeped_by_block) + if (settings.mask & event_visitor_settings::set_txs_keeped_by_block) { - m_txs_keeped_by_block = settings.txs_keeped_by_block; + m_tx_relay = cryptonote::relay_method::block; + } + else if (settings.mask & event_visitor_settings::set_local_relay) + { + m_tx_relay = cryptonote::relay_method::local; + } + else if (settings.mask & event_visitor_settings::set_txs_do_not_relay) + { + m_tx_relay = cryptonote::relay_method::none; + } + else + { + m_tx_relay = cryptonote::relay_method::flood; } return true; @@ -544,7 +556,7 @@ public: cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); size_t pool_size = m_c.get_pool_transactions_count(); - m_c.handle_incoming_tx({t_serializable_object_to_blob(tx), crypto::null_hash}, tvc, m_txs_keeped_by_block, false, false); + m_c.handle_incoming_tx({t_serializable_object_to_blob(tx), crypto::null_hash}, tvc, m_tx_relay, false); bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count(); bool r = m_validator.check_tx_verification_context(tvc, tx_added, m_ev_index, tx); CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed"); @@ -564,7 +576,7 @@ public: tvcs.push_back(tvc0); } size_t pool_size = m_c.get_pool_transactions_count(); - m_c.handle_incoming_txs(tx_blobs, tvcs, m_txs_keeped_by_block, false, false); + m_c.handle_incoming_txs(tx_blobs, tvcs, m_tx_relay, false); size_t tx_added = m_c.get_pool_transactions_count() - pool_size; bool r = m_validator.check_tx_verification_context_array(tvcs, tx_added, m_ev_index, txs); CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed"); @@ -644,7 +656,7 @@ public: cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); size_t pool_size = m_c.get_pool_transactions_count(); - m_c.handle_incoming_tx(sr_tx.data, tvc, m_txs_keeped_by_block, false, false); + m_c.handle_incoming_tx(sr_tx.data, tvc, m_tx_relay, false); bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count(); cryptonote::transaction tx; @@ -955,7 +967,7 @@ inline bool do_replay_file(const std::string& filename) #define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0) -#define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT, VAL) VEC_EVENTS.push_back(event_visitor_settings(SETT, VAL)); +#define SET_EVENT_VISITOR_SETT(VEC_EVENTS, SETT) VEC_EVENTS.push_back(event_visitor_settings(SETT)); #define GENERATE(filename, genclass) \ { \ diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 8406f416f..23f3170b8 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -32,6 +32,7 @@ #include "chaingen_tests_list.h" #include "common/util.h" #include "common/command_line.h" +#include "tx_pool.h" #include "transaction_tests.h" namespace po = boost::program_options; @@ -155,6 +156,12 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_tx_output_is_not_txout_to_key); GENERATE_AND_PLAY(gen_tx_signatures_are_invalid); + // Mempool + GENERATE_AND_PLAY(txpool_spend_key_public); + GENERATE_AND_PLAY(txpool_spend_key_all); + GENERATE_AND_PLAY(txpool_double_spend_norelay); + GENERATE_AND_PLAY(txpool_double_spend_local); + // Double spend GENERATE_AND_PLAY(gen_double_spend_in_tx); GENERATE_AND_PLAY(gen_double_spend_in_tx); diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index afd212b27..4aa12c8e0 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -46,7 +46,7 @@ bool gen_double_spend_in_different_chains::generate(std::vector::generate(std::vector(), tx_1, 0)) return false; - SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); + SET_EVENT_VISITOR_SETT(events, txs_keeped_by_block ? event_visitor_settings::set_txs_keeped_by_block : 0); DO_CALLBACK(events, "mark_invalid_tx"); events.push_back(tx_1); DO_CALLBACK(events, "mark_invalid_block"); @@ -163,7 +163,7 @@ bool gen_double_spend_in_the_same_block::generate(std::vect INIT_DOUBLE_SPEND_TEST(); DO_CALLBACK(events, "mark_last_valid_block"); - SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); + SET_EVENT_VISITOR_SETT(events, txs_keeped_by_block ? event_visitor_settings::set_txs_keeped_by_block : 0); MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); cryptonote::transaction tx_1 = txs_1.front(); @@ -190,7 +190,7 @@ bool gen_double_spend_in_different_blocks::generate(std::ve INIT_DOUBLE_SPEND_TEST(); DO_CALLBACK(events, "mark_last_valid_block"); - SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); + SET_EVENT_VISITOR_SETT(events, txs_keeped_by_block ? event_visitor_settings::set_txs_keeped_by_block : 0); // Create two identical transactions, but don't push it to events list MAKE_TX(events, tx_blk_2, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); @@ -220,7 +220,7 @@ bool gen_double_spend_in_alt_chain_in_the_same_block::gener { INIT_DOUBLE_SPEND_TEST(); - SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); + SET_EVENT_VISITOR_SETT(events, txs_keeped_by_block ? event_visitor_settings::set_txs_keeped_by_block : 0); // Main chain MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_account); @@ -255,7 +255,7 @@ bool gen_double_spend_in_alt_chain_in_different_blocks::gen { INIT_DOUBLE_SPEND_TEST(); - SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); + SET_EVENT_VISITOR_SETT(events, txs_keeped_by_block ? event_visitor_settings::set_txs_keeped_by_block : 0); // Main chain MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_account); diff --git a/tests/core_tests/tx_pool.cpp b/tests/core_tests/tx_pool.cpp new file mode 100644 index 000000000..537015dca --- /dev/null +++ b/tests/core_tests/tx_pool.cpp @@ -0,0 +1,561 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#include "tx_pool.h" + +#include +#include +#include +#include "string_tools.h" + +#define INIT_MEMPOOL_TEST() \ + uint64_t send_amount = 1000; \ + uint64_t ts_start = 1338224400; \ + GENERATE_ACCOUNT(miner_account); \ + GENERATE_ACCOUNT(bob_account); \ + MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); \ + REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); \ + + +txpool_base::txpool_base() + : test_chain_unit_base() + , m_broadcasted_tx_count(0) + , m_all_tx_count(0) +{ + REGISTER_CALLBACK_METHOD(txpool_spend_key_public, increase_broadcasted_tx_count); + REGISTER_CALLBACK_METHOD(txpool_spend_key_public, increase_all_tx_count); + REGISTER_CALLBACK_METHOD(txpool_spend_key_public, check_txpool_spent_keys); +} + +bool txpool_base::increase_broadcasted_tx_count(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector& /*events*/) +{ + ++m_broadcasted_tx_count; + return true; +} + +bool txpool_base::increase_all_tx_count(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector& /*events*/) +{ + ++m_all_tx_count; + return true; +} + +bool txpool_base::check_txpool_spent_keys(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events) +{ + std::vector infos{}; + std::vector key_images{}; + if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images) || infos.size() != m_broadcasted_tx_count || key_images.size() != m_broadcasted_tx_count) + { + MERROR("Failed broadcasted spent keys retrieval - Expected Broadcasted Count: " << m_broadcasted_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size()); + return false; + } + + infos.clear(); + key_images.clear(); + if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images, false) || infos.size() != m_broadcasted_tx_count || key_images.size() != m_broadcasted_tx_count) + { + MERROR("Failed broadcasted spent keys retrieval - Expected Broadcasted Count: " << m_broadcasted_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size()); + return false; + } + + infos.clear(); + key_images.clear(); + if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images, true) || infos.size() != m_all_tx_count || key_images.size() != m_all_tx_count) + { + MERROR("Failed all spent keys retrieval - Expected All Count: " << m_all_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size()); + return false; + } + + return true; +} + +bool txpool_spend_key_public::generate(std::vector& events) const +{ + INIT_MEMPOOL_TEST(); + + DO_CALLBACK(events, "check_txpool_spent_keys"); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + DO_CALLBACK(events, "increase_broadcasted_tx_count"); + DO_CALLBACK(events, "increase_all_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + + return true; +} + +bool txpool_spend_key_all::generate(std::vector& events) +{ + INIT_MEMPOOL_TEST(); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_do_not_relay); + + DO_CALLBACK(events, "check_txpool_spent_keys"); + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + DO_CALLBACK(events, "increase_all_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + + return true; +} + +txpool_double_spend_base::txpool_double_spend_base() + : txpool_base() + , m_broadcasted_hashes() + , m_no_relay_hashes() + , m_all_hashes() + , m_no_new_index(0) + , m_new_timestamp_index(0) + , m_last_tx(crypto::hash{}) +{ + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, mark_no_new); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, mark_timestamp_change); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, timestamp_change_pause); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, check_unchanged); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, check_new_broadcasted); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, check_new_hidden); + REGISTER_CALLBACK_METHOD(txpool_double_spend_base, check_new_no_relay); +} + +bool txpool_double_spend_base::mark_no_new(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +{ + m_no_new_index = ev_index + 1; + return true; +} + +bool txpool_double_spend_base::mark_timestamp_change(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +{ + m_new_timestamp_index = ev_index + 1; + return true; +} + +bool txpool_double_spend_base::timestamp_change_pause(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector& /*events*/) +{ + boost::this_thread::sleep_for(boost::chrono::seconds{1} + boost::chrono::milliseconds{100}); + return true; +} + +bool txpool_double_spend_base::check_changed(cryptonote::core& c, const size_t ev_index, relay_test condition) +{ + const std::size_t public_hash_count = m_broadcasted_hashes.size(); + const std::size_t all_hash_count = m_all_hashes.size(); + + const std::size_t new_broadcasted_hash_count = m_broadcasted_hashes.size() + unsigned(condition == relay_test::broadcasted); + const std::size_t new_all_hash_count = m_all_hashes.size() + unsigned(condition == relay_test::hidden) + unsigned(condition == relay_test::no_relay); + + std::vector hashes{}; + if (!c.get_pool_transaction_hashes(hashes)) + { + MERROR("Failed to get broadcasted transaction pool hashes"); + return false; + } + + for (const crypto::hash& hash : hashes) + m_broadcasted_hashes.insert(hash); + + if (new_broadcasted_hash_count != m_broadcasted_hashes.size()) + { + MERROR("Expected " << new_broadcasted_hash_count << " broadcasted hashes but got " << m_broadcasted_hashes.size()); + return false; + } + + if (m_broadcasted_hashes.size() != c.get_pool_transactions_count()) + { + MERROR("Expected " << m_broadcasted_hashes.size() << " broadcasted hashes but got " << c.get_pool_transactions_count()); + return false; + } + + hashes.clear(); + if (!c.get_pool_transaction_hashes(hashes, false)) + { + MERROR("Failed to get broadcasted transaction pool hashes"); + return false; + } + + for (const crypto::hash& hash : hashes) + m_all_hashes.insert(std::make_pair(hash, 0)); + + if (new_broadcasted_hash_count != m_broadcasted_hashes.size()) + { + MERROR("Expected " << new_broadcasted_hash_count << " broadcasted hashes but got " << m_broadcasted_hashes.size()); + return false; + } + + hashes.clear(); + if (!c.get_pool_transaction_hashes(hashes, true)) + { + + MERROR("Failed to get all transaction pool hashes"); + return false; + } + + for (const crypto::hash& hash : hashes) + m_all_hashes.insert(std::make_pair(hash, 0)); + + if (new_all_hash_count != m_all_hashes.size()) + { + MERROR("Expected " << new_all_hash_count << " all hashes but got " << m_all_hashes.size()); + return false; + } + + if (condition == relay_test::no_relay) + { + if (!m_no_relay_hashes.insert(m_last_tx).second) + { + MERROR("Expected new no_relay tx but got a duplicate legacy tx"); + return false; + } + + for (const crypto::hash& hash : m_no_relay_hashes) + { + if (!c.pool_has_tx(hash)) + { + MERROR("Expected public tx " << hash << " to be listed in pool"); + return false; + } + } + } + + // check receive time changes + { + std::vector infos{}; + std::vector key_images{}; + if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images, true) || infos.size() != m_all_hashes.size()) + { + MERROR("Unable to retrieve all txpool metadata"); + return false; + } + + for (const cryptonote::tx_info& info : infos) + { + crypto::hash tx_hash; + if (!epee::string_tools::hex_to_pod(info.id_hash, tx_hash)) + { + MERROR("Unable to convert tx_hash hex to binary"); + return false; + } + + const auto entry = m_all_hashes.find(tx_hash); + if (entry == m_all_hashes.end()) + { + MERROR("Unable to find tx_hash in set of tracked hashes"); + return false; + } + + if (m_new_timestamp_index == ev_index && m_last_tx == tx_hash) + { + if (entry->second >= info.receive_time) + { + MERROR("Last relay time did not change as expected - last at " << entry->second << " and current at " << info.receive_time); + return false; + } + entry->second = info.receive_time; + } + else if (entry->second != info.receive_time) + { + MERROR("Last relayed time changed unexpectedly from " << entry->second << " to " << info.receive_time); + return false; + } + } + } + + { + std::vector txes{}; + if (!c.get_pool_transactions(txes)) + { + MERROR("Failed to get broadcasted transactions from pool"); + return false; + } + + hashes.clear(); + for (const cryptonote::transaction& tx : txes) + hashes.push_back(cryptonote::get_transaction_hash(tx)); + + std::unordered_set public_hashes = m_broadcasted_hashes; + for (const crypto::hash& hash : hashes) + { + if (!c.pool_has_tx(hash)) + { + MERROR("Expected broadcasted tx " << hash << " to be listed in pool"); + return false; + } + + if (!public_hashes.erase(hash)) + { + MERROR("An unexected transaction was returned from the public pool"); + return false; + } + } + if (!public_hashes.empty()) + { + MERROR(public_hashes.size() << " transaction(s) were missing from the public pool"); + return false; + } + } + + { + std::vector txes{}; + if (!c.get_pool_transactions(txes, false)) + { + MERROR("Failed to get broadcasted transactions from pool"); + return false; + } + + hashes.clear(); + for (const cryptonote::transaction& tx : txes) + hashes.push_back(cryptonote::get_transaction_hash(tx)); + + std::unordered_set public_hashes = m_broadcasted_hashes; + for (const crypto::hash& hash : hashes) + { + + if (!public_hashes.erase(hash)) + { + MERROR("An unexected transaction was returned from the public pool"); + return false; + } + } + if (!public_hashes.empty()) + { + MERROR(public_hashes.size() << " transaction(s) were missing from the public pool"); + return false; + } + } + + { + std::vector txes{}; + if (!c.get_pool_transactions(txes, true)) + { + MERROR("Failed to get all transactions from pool"); + return false; + } + + hashes.clear(); + for (const cryptonote::transaction& tx : txes) + hashes.push_back(cryptonote::get_transaction_hash(tx)); + + std::unordered_map all_hashes = m_all_hashes; + for (const crypto::hash& hash : hashes) + { + if (!all_hashes.erase(hash)) + { + MERROR("An unexected transaction was returned from the all pool"); + return false; + } + } + if (!all_hashes.empty()) + { + MERROR(m_broadcasted_hashes.size() << " transaction(s) were missing from the all pool"); + return false; + } + } + + { + std::vector entries{}; + if (!c.get_txpool_backlog(entries)) + { + MERROR("Failed to get broadcasted txpool backlog"); + return false; + } + + if (m_broadcasted_hashes.size() != entries.size()) + { + MERROR("Expected " << m_broadcasted_hashes.size() << " in the broadcasted txpool backlog but got " << entries.size()); + return false; + } + } + + for (const std::pair& hash : m_all_hashes) + { + cryptonote::blobdata tx_blob{}; + if (!c.get_pool_transaction(hash.first, tx_blob, cryptonote::relay_category::all)) + { + MERROR("Failed to retrieve tx expected to be in pool: " << hash.first); + return false; + } + } + + { + std::unordered_map difference = m_all_hashes; + for (const crypto::hash& hash : m_broadcasted_hashes) + difference.erase(hash); + + for (const crypto::hash& hash : m_no_relay_hashes) + difference.erase(hash); + + for (const std::pair& hash : difference) + { + if (c.pool_has_tx(hash.first)) + { + MERROR("Did not expect private/hidden tx " << hash.first << " to be listed in pool"); + return false; + } + + cryptonote::blobdata tx_blob{}; + if (c.get_pool_transaction(hash.first, tx_blob, cryptonote::relay_category::broadcasted)) + { + MERROR("Tx " << hash.first << " is not supposed to be in broadcasted pool"); + return false; + } + + if (!c.get_pool_transaction(hash.first, tx_blob, cryptonote::relay_category::all)) + { + MERROR("Tx " << hash.first << " blob could not be retrieved from pool"); + return false; + } + } + } + + { + cryptonote::txpool_stats stats{}; + if (!c.get_pool_transaction_stats(stats) || stats.txs_total != m_broadcasted_hashes.size()) + { + MERROR("Expected broadcasted stats to list " << m_broadcasted_hashes.size() << " txes but got " << stats.txs_total); + return false; + } + + if (!c.get_pool_transaction_stats(stats, false) || stats.txs_total != m_broadcasted_hashes.size()) + { + MERROR("Expected broadcasted stats to list " << m_broadcasted_hashes.size() << " txes but got " << stats.txs_total); + return false; + } + + if (!c.get_pool_transaction_stats(stats, true) || stats.txs_total != m_all_hashes.size()) + { + MERROR("Expected all stats to list " << m_all_hashes.size() << " txes but got " << stats.txs_total); + return false; + } + } + + { + std::vector infos{}; + cryptonote::rpc::key_images_with_tx_hashes key_images{}; + if (!c.get_pool_for_rpc(infos, key_images) || infos.size() != m_broadcasted_hashes.size() || key_images.size() != m_broadcasted_hashes.size()) + { + MERROR("Expected broadcasted rpc data to return " << m_broadcasted_hashes.size() << " but got " << infos.size() << " infos and " << key_images.size() << "key images"); + return false; + } + } + return true; +} + +bool txpool_double_spend_base::check_unchanged(cryptonote::core& c, size_t ev_index, const std::vector& /*events */) +{ + return check_changed(c, ev_index, relay_test::no_change); +} + +bool txpool_double_spend_base::check_new_broadcasted(cryptonote::core& c, size_t ev_index, const std::vector& /*events */) +{ + return check_changed(c, ev_index, relay_test::broadcasted); +} + +bool txpool_double_spend_base::check_new_hidden(cryptonote::core& c, size_t ev_index, const std::vector& /*events */) +{ + return check_changed(c, ev_index, relay_test::hidden); +} +bool txpool_double_spend_base::check_new_no_relay(cryptonote::core& c, size_t ev_index, const std::vector& /*events */) +{ + return check_changed(c, ev_index, relay_test::no_relay); +} + +bool txpool_double_spend_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& tx) +{ + m_last_tx = cryptonote::get_transaction_hash(tx); + if (m_no_new_index == event_idx) + return !tvc.m_verifivation_failed && !tx_added; + else + return !tvc.m_verifivation_failed && tx_added; +} + +bool txpool_double_spend_norelay::generate(std::vector& events) const +{ + INIT_MEMPOOL_TEST(); + + DO_CALLBACK(events, "check_txpool_spent_keys"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_do_not_relay); + DO_CALLBACK(events, "mark_no_new"); + + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + + DO_CALLBACK(events, "increase_all_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_new_no_relay"); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + events.push_back(tx_0); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "check_unchanged"); + SET_EVENT_VISITOR_SETT(events, 0); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + events.push_back(tx_0); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "check_unchanged"); + + // kepped by block currently does not change txpool status + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + events.push_back(tx_0); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "check_unchanged"); + + return true; +} + +bool txpool_double_spend_local::generate(std::vector& events) const +{ + INIT_MEMPOOL_TEST(); + + DO_CALLBACK(events, "check_txpool_spent_keys"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_local_relay); + DO_CALLBACK(events, "mark_no_new"); + + MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0); + + DO_CALLBACK(events, "increase_all_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_new_hidden"); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + events.push_back(tx_0); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_unchanged"); + SET_EVENT_VISITOR_SETT(events, 0); + DO_CALLBACK(events, "timestamp_change_pause"); + events.push_back(tx_0); + DO_CALLBACK(events, "increase_broadcasted_tx_count"); + DO_CALLBACK(events, "check_txpool_spent_keys"); + DO_CALLBACK(events, "mark_timestamp_change"); + DO_CALLBACK(events, "check_new_broadcasted"); + DO_CALLBACK(events, "timestamp_change_pause"); + DO_CALLBACK(events, "mark_no_new"); + events.push_back(tx_0); + DO_CALLBACK(events, "check_unchanged"); + + return true; +} + diff --git a/tests/core_tests/tx_pool.h b/tests/core_tests/tx_pool.h new file mode 100644 index 000000000..996c76698 --- /dev/null +++ b/tests/core_tests/tx_pool.h @@ -0,0 +1,118 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#pragma once + +#include +#include + +#include "chaingen.h" +#include "crypto/crypto.h" + +enum class relay_test +{ + no_change = 0, //!< No expected changes to the txpool + broadcasted, //!< A new block or fluff/flood tx is expected in txpool + hidden, //!< A new stem or local tx is expected in txpool + no_relay //!< A new no relay is expected in txpool +}; + +class txpool_base : public test_chain_unit_base +{ + size_t m_broadcasted_tx_count; + size_t m_all_tx_count; + +public: + txpool_base(); + + bool increase_broadcasted_tx_count(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events); + bool increase_all_tx_count(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events); + bool check_txpool_spent_keys(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events); +}; + +struct txpool_spend_key_public : txpool_base +{ + txpool_spend_key_public() : txpool_base() + {} + + bool generate(std::vector& events) const; +}; + +struct txpool_spend_key_all : txpool_base +{ + txpool_spend_key_all() : txpool_base() + {} + + bool generate(std::vector& events); +}; + +class txpool_double_spend_base : public txpool_base +{ + std::unordered_set m_broadcasted_hashes; + std::unordered_set m_no_relay_hashes; + std::unordered_map m_all_hashes; + size_t m_no_new_index; + size_t m_new_timestamp_index; + crypto::hash m_last_tx; + + bool check_changed(cryptonote::core& c, size_t ev_index, relay_test condition); + +public: + txpool_double_spend_base(); + + bool mark_no_new(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_timestamp_change(cryptonote::core& c, size_t ev_index, const std::vector& events); + + //! Pause for 1 second, so that `receive_time` for tx meta changes (tx hidden from public rpc being updated) + bool timestamp_change_pause(cryptonote::core& c, size_t ev_index, const std::vector& events); + + bool check_unchanged(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_new_broadcasted(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_new_hidden(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_new_no_relay(cryptonote::core& c, size_t ev_index, const std::vector& events); + + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/); +}; + +struct txpool_double_spend_norelay : txpool_double_spend_base +{ + txpool_double_spend_norelay() + : txpool_double_spend_base() + {} + + bool generate(std::vector& events) const; +}; + +struct txpool_double_spend_local : txpool_double_spend_base +{ + txpool_double_spend_local() + : txpool_double_spend_base() + {} + + bool generate(std::vector& events) const; +}; diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 5f91fc6d4..c92f70b97 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -32,6 +32,7 @@ #include "cryptonote_core/cryptonote_core.h" #include "p2p/net_node.h" #include "p2p/net_node.inl" +#include "cryptonote_core/i_core_events.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "cryptonote_protocol/cryptonote_protocol_handler.inl" @@ -43,7 +44,7 @@ namespace cryptonote { class blockchain_storage; } -class test_core +class test_core : public cryptonote::i_core_events { public: void on_synchronized(){} @@ -56,8 +57,8 @@ public: bool get_stat_info(cryptonote::core_stat_info& st_inf) const {return true;} bool have_block(const crypto::hash& id) const {return true;} void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} - bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } - bool handle_incoming_txs(const std::vector& tx_blob, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } + bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; } + bool handle_incoming_txs(const std::vector& tx_blob, std::vector& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; } bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; } void pause_mine(){} void resume_mine(){} @@ -71,9 +72,9 @@ public: bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } - virtual void on_transaction_relayed(const cryptonote::blobdata& tx) {} + virtual void on_transactions_relayed(epee::span tx_blobs, cryptonote::relay_method tx_relay) {} cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } - bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob) const { return false; } + bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob, cryptonote::relay_category tx_category) const { return false; } bool pool_has_tx(const crypto::hash &txid) const { return false; } bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return false; } From 436e4c336396f12c36df779c973824bbbae386ae Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 3 Nov 2019 00:12:31 +0000 Subject: [PATCH 040/469] Fix readline build Make sure it links to our libtinfo from our ncurses build. Hardcode some basic terminal descriptions into our libtinfo. Re-enable $HOME/.terminfo support to allow user customization. Use unlikely terminfo-dir, to prevent accidentally using differently-configured system databases. --- contrib/depends/packages/ncurses.mk | 10 +- contrib/depends/packages/readline.mk | 2 +- contrib/depends/patches/ncurses/fallback.c | 6621 ++++++++++++++++++++ contrib/depends/toolchain.cmake.in | 2 +- 4 files changed, 6630 insertions(+), 5 deletions(-) create mode 100644 contrib/depends/patches/ncurses/fallback.c diff --git a/contrib/depends/packages/ncurses.mk b/contrib/depends/packages/ncurses.mk index c3b16baab..1acf1faca 100644 --- a/contrib/depends/packages/ncurses.mk +++ b/contrib/depends/packages/ncurses.mk @@ -3,6 +3,7 @@ $(package)_version=6.1 $(package)_download_path=https://ftp.gnu.org/gnu/ncurses $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=aa057eeeb4a14d470101eff4597d5833dcef5965331be3528c08d99cebaa0d17 +$(package)_patches=fallback.c define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" @@ -35,9 +36,8 @@ define $(package)_set_vars $(pacakge)_config_opts+=--disable-symlinks $(pacakge)_config_opts+=--enable-warnings $(pacakge)_config_opts+=--enable-assertions - $(pacakge)_config_opts+=--disable-home-terminfo - $(package)_config_opts+=--with-default-terminfo-dir=/etc/terminfo - $(package)_config_opts+=--with-terminfo-dirs="/etc/terminfo:/lib/terminfo:/usr/share/terminfo" + $(package)_config_opts+=--with-default-terminfo-dir=/etc/_terminfo_ + $(package)_config_opts+=--with-terminfo-dirs=/etc/_terminfo_ $(pacakge)_config_opts+=--enable-database $(pacakge)_config_opts+=--enable-sp-funcs $(pacakge)_config_opts+=--disable-term-driver @@ -46,6 +46,10 @@ define $(package)_set_vars $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" endef +define $(package)_preprocess_cmds + cp $($(package)_patch_dir)/fallback.c ncurses +endef + define $(package)_config_cmds ./configure $($(package)_config_opts) endef diff --git a/contrib/depends/packages/readline.mk b/contrib/depends/packages/readline.mk index 8f234ab6a..29ae783a2 100644 --- a/contrib/depends/packages/readline.mk +++ b/contrib/depends/packages/readline.mk @@ -7,7 +7,7 @@ $(package)_dependencies=ncurses define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" - $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" LDFLAGS="-L$(host_prefix)/lib" $(package)_config_env_darwin=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" $(package)_config_opts+=--prefix=$(host_prefix) $(package)_config_opts+=--exec-prefix=$(host_prefix) diff --git a/contrib/depends/patches/ncurses/fallback.c b/contrib/depends/patches/ncurses/fallback.c new file mode 100644 index 000000000..fab108c37 --- /dev/null +++ b/contrib/depends/patches/ncurses/fallback.c @@ -0,0 +1,6621 @@ +/* This file was generated by tinfo/MKfallback.sh */ + +/* + * DO NOT EDIT THIS FILE BY HAND! + */ + +#include + +#include + +/* fallback entries for: linux rxvt vt100 xterm xterm-256color screen screen.linux screen.rxvt screen.xterm-new screen.xterm-256color */ +/* linux */ + +static char linux_alias_data[] = "linux|linux console"; + +static char linux_s_bel [] = "\007"; +static char linux_s_cr [] = "\015"; +static char linux_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char linux_s_tbc [] = "\033[3g"; +static char linux_s_clear [] = "\033[H\033[J"; +static char linux_s_el [] = "\033[K"; +static char linux_s_ed [] = "\033[J"; +static char linux_s_hpa [] = "\033[%i%p1%dG"; +static char linux_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char linux_s_cud1 [] = "\012"; +static char linux_s_home [] = "\033[H"; +static char linux_s_civis [] = "\033[?25l\033[?1c"; +static char linux_s_cub1 [] = "\010"; +static char linux_s_cnorm [] = "\033[?25h\033[?0c"; +static char linux_s_cuf1 [] = "\033[C"; +static char linux_s_cuu1 [] = "\033[A"; +static char linux_s_cvvis [] = "\033[?25h\033[?8c"; +static char linux_s_dch1 [] = "\033[P"; +static char linux_s_dl1 [] = "\033[M"; +static char linux_s_smacs [] = "\016"; +static char linux_s_blink [] = "\033[5m"; +static char linux_s_bold [] = "\033[1m"; +static char linux_s_dim [] = "\033[2m"; +static char linux_s_smir [] = "\033[4h"; +static char linux_s_rev [] = "\033[7m"; +static char linux_s_smso [] = "\033[7m"; +static char linux_s_smul [] = "\033[4m"; +static char linux_s_ech [] = "\033[%p1%dX"; +static char linux_s_rmacs [] = "\017"; +static char linux_s_sgr0 [] = "\033[m\017"; +static char linux_s_rmir [] = "\033[4l"; +static char linux_s_rmso [] = "\033[27m"; +static char linux_s_rmul [] = "\033[24m"; +static char linux_s_flash [] = "\033[?5h$<200/>\033[?5l"; +static char linux_s_ich1 [] = "\033[@"; +static char linux_s_il1 [] = "\033[L"; +static char linux_s_kbs [] = "\177"; +static char linux_s_kdch1 [] = "\033[3~"; +static char linux_s_kcud1 [] = "\033[B"; +static char linux_s_kf1 [] = "\033[[A"; +static char linux_s_kf10 [] = "\033[21~"; +static char linux_s_kf2 [] = "\033[[B"; +static char linux_s_kf3 [] = "\033[[C"; +static char linux_s_kf4 [] = "\033[[D"; +static char linux_s_kf5 [] = "\033[[E"; +static char linux_s_kf6 [] = "\033[17~"; +static char linux_s_kf7 [] = "\033[18~"; +static char linux_s_kf8 [] = "\033[19~"; +static char linux_s_kf9 [] = "\033[20~"; +static char linux_s_khome [] = "\033[1~"; +static char linux_s_kich1 [] = "\033[2~"; +static char linux_s_kcub1 [] = "\033[D"; +static char linux_s_knp [] = "\033[6~"; +static char linux_s_kpp [] = "\033[5~"; +static char linux_s_kcuf1 [] = "\033[C"; +static char linux_s_kcuu1 [] = "\033[A"; +static char linux_s_nel [] = "\015\012"; +static char linux_s_dch [] = "\033[%p1%dP"; +static char linux_s_dl [] = "\033[%p1%dM"; +static char linux_s_cud [] = "\033[%p1%dB"; +static char linux_s_ich [] = "\033[%p1%d@"; +static char linux_s_il [] = "\033[%p1%dL"; +static char linux_s_cub [] = "\033[%p1%dD"; +static char linux_s_cuf [] = "\033[%p1%dC"; +static char linux_s_cuu [] = "\033[%p1%dA"; +static char linux_s_rs1 [] = "\033c\033]R"; +static char linux_s_rc [] = "\0338"; +static char linux_s_vpa [] = "\033[%i%p1%dd"; +static char linux_s_sc [] = "\0337"; +static char linux_s_ind [] = "\012"; +static char linux_s_ri [] = "\033M"; +static char linux_s_sgr [] = "\033[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;m%?%p9%t\016%e\017%;"; +static char linux_s_hts [] = "\033H"; +static char linux_s_ht [] = "\011"; +static char linux_s_kb2 [] = "\033[G"; +static char linux_s_acsc [] = "++,,--..00__``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}c~~"; +static char linux_s_kcbt [] = "\033[Z"; +static char linux_s_smam [] = "\033[?7h"; +static char linux_s_rmam [] = "\033[?7l"; +static char linux_s_enacs [] = "\033)0"; +static char linux_s_kend [] = "\033[4~"; +static char linux_s_kspd [] = "\032"; +static char linux_s_kf11 [] = "\033[23~"; +static char linux_s_kf12 [] = "\033[24~"; +static char linux_s_kf13 [] = "\033[25~"; +static char linux_s_kf14 [] = "\033[26~"; +static char linux_s_kf15 [] = "\033[28~"; +static char linux_s_kf16 [] = "\033[29~"; +static char linux_s_kf17 [] = "\033[31~"; +static char linux_s_kf18 [] = "\033[32~"; +static char linux_s_kf19 [] = "\033[33~"; +static char linux_s_kf20 [] = "\033[34~"; +static char linux_s_el1 [] = "\033[1K"; +static char linux_s_u6 [] = "\033[%i%d;%dR"; +static char linux_s_u7 [] = "\033[6n"; +static char linux_s_u8 [] = "\033[?6c"; +static char linux_s_u9 [] = "\033[c"; +static char linux_s_op [] = "\033[39;49m"; +static char linux_s_oc [] = "\033]R"; +static char linux_s_initc [] = "\033]P%p1%x%p2%{255}%*%{1000}%/%02x%p3%{255}%*%{1000}%/%02x%p4%{255}%*%{1000}%/%02x"; +static char linux_s_kmous [] = "\033[M"; +static char linux_s_setaf [] = "\033[3%p1%dm"; +static char linux_s_setab [] = "\033[4%p1%dm"; +static char linux_s_smpch [] = "\033[11m"; +static char linux_s_rmpch [] = "\033[10m"; + +static char linux_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ TRUE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ FALSE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ TRUE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ TRUE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ FALSE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 linux_number_data[] = { + /* 0: cols */ ABSENT_NUMERIC, + /* 1: it */ 8, + /* 2: lines */ ABSENT_NUMERIC, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ 18, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * linux_string_data[] = { + /* 0: cbt */ ABSENT_STRING, + /* 1: bel */ linux_s_bel, + /* 2: cr */ linux_s_cr, + /* 3: csr */ linux_s_csr, + /* 4: tbc */ linux_s_tbc, + /* 5: clear */ linux_s_clear, + /* 6: el */ linux_s_el, + /* 7: ed */ linux_s_ed, + /* 8: hpa */ linux_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ linux_s_cup, + /* 11: cud1 */ linux_s_cud1, + /* 12: home */ linux_s_home, + /* 13: civis */ linux_s_civis, + /* 14: cub1 */ linux_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ linux_s_cnorm, + /* 17: cuf1 */ linux_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ linux_s_cuu1, + /* 20: cvvis */ linux_s_cvvis, + /* 21: dch1 */ linux_s_dch1, + /* 22: dl1 */ linux_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ linux_s_smacs, + /* 26: blink */ linux_s_blink, + /* 27: bold */ linux_s_bold, + /* 28: smcup */ ABSENT_STRING, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ linux_s_dim, + /* 31: smir */ linux_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ linux_s_rev, + /* 35: smso */ linux_s_smso, + /* 36: smul */ linux_s_smul, + /* 37: ech */ linux_s_ech, + /* 38: rmacs */ linux_s_rmacs, + /* 39: sgr0 */ linux_s_sgr0, + /* 40: rmcup */ ABSENT_STRING, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ linux_s_rmir, + /* 43: rmso */ linux_s_rmso, + /* 44: rmul */ linux_s_rmul, + /* 45: flash */ linux_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ ABSENT_STRING, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ linux_s_ich1, + /* 53: il1 */ linux_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ linux_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ linux_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ linux_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ linux_s_kf1, + /* 67: kf10 */ linux_s_kf10, + /* 68: kf2 */ linux_s_kf2, + /* 69: kf3 */ linux_s_kf3, + /* 70: kf4 */ linux_s_kf4, + /* 71: kf5 */ linux_s_kf5, + /* 72: kf6 */ linux_s_kf6, + /* 73: kf7 */ linux_s_kf7, + /* 74: kf8 */ linux_s_kf8, + /* 75: kf9 */ linux_s_kf9, + /* 76: khome */ linux_s_khome, + /* 77: kich1 */ linux_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ linux_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ linux_s_knp, + /* 82: kpp */ linux_s_kpp, + /* 83: kcuf1 */ linux_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ linux_s_kcuu1, + /* 88: rmkx */ ABSENT_STRING, + /* 89: smkx */ ABSENT_STRING, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ linux_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ linux_s_dch, + /* 106: dl */ linux_s_dl, + /* 107: cud */ linux_s_cud, + /* 108: ich */ linux_s_ich, + /* 109: indn */ ABSENT_STRING, + /* 110: il */ linux_s_il, + /* 111: cub */ linux_s_cub, + /* 112: cuf */ linux_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ linux_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ linux_s_rs1, + /* 123: rs2 */ ABSENT_STRING, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ linux_s_rc, + /* 127: vpa */ linux_s_vpa, + /* 128: sc */ linux_s_sc, + /* 129: ind */ linux_s_ind, + /* 130: ri */ linux_s_ri, + /* 131: sgr */ linux_s_sgr, + /* 132: hts */ linux_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ linux_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ linux_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ linux_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ linux_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ linux_s_smam, + /* 152: rmam */ linux_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ linux_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ linux_s_kend, + /* 165: kent */ ABSENT_STRING, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ linux_s_kspd, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ linux_s_kf11, + /* 217: kf12 */ linux_s_kf12, + /* 218: kf13 */ linux_s_kf13, + /* 219: kf14 */ linux_s_kf14, + /* 220: kf15 */ linux_s_kf15, + /* 221: kf16 */ linux_s_kf16, + /* 222: kf17 */ linux_s_kf17, + /* 223: kf18 */ linux_s_kf18, + /* 224: kf19 */ linux_s_kf19, + /* 225: kf20 */ linux_s_kf20, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ linux_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ linux_s_u6, + /* 294: u7 */ linux_s_u7, + /* 295: u8 */ linux_s_u8, + /* 296: u9 */ linux_s_u9, + /* 297: op */ linux_s_op, + /* 298: oc */ linux_s_oc, + /* 299: initc */ linux_s_initc, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ linux_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ linux_s_setaf, + /* 360: setab */ linux_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ linux_s_smpch, + /* 380: rmpch */ linux_s_rmpch, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* rxvt */ + +static char rxvt_alias_data[] = "rxvt|rxvt terminal emulator (X Window System)"; + +static char rxvt_s_bel [] = "\007"; +static char rxvt_s_cr [] = "\015"; +static char rxvt_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char rxvt_s_tbc [] = "\033[3g"; +static char rxvt_s_clear [] = "\033[H\033[2J"; +static char rxvt_s_el [] = "\033[K"; +static char rxvt_s_ed [] = "\033[J"; +static char rxvt_s_hpa [] = "\033[%i%p1%dG"; +static char rxvt_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char rxvt_s_cud1 [] = "\012"; +static char rxvt_s_home [] = "\033[H"; +static char rxvt_s_civis [] = "\033[?25l"; +static char rxvt_s_cub1 [] = "\010"; +static char rxvt_s_cnorm [] = "\033[?25h"; +static char rxvt_s_cuf1 [] = "\033[C"; +static char rxvt_s_cuu1 [] = "\033[A"; +static char rxvt_s_dl1 [] = "\033[M"; +static char rxvt_s_smacs [] = "\016"; +static char rxvt_s_blink [] = "\033[5m"; +static char rxvt_s_bold [] = "\033[1m"; +static char rxvt_s_smcup [] = "\0337\033[?47h"; +static char rxvt_s_smir [] = "\033[4h"; +static char rxvt_s_rev [] = "\033[7m"; +static char rxvt_s_smso [] = "\033[7m"; +static char rxvt_s_smul [] = "\033[4m"; +static char rxvt_s_rmacs [] = "\017"; +static char rxvt_s_sgr0 [] = "\033[m\017"; +static char rxvt_s_rmcup [] = "\033[2J\033[?47l\0338"; +static char rxvt_s_rmir [] = "\033[4l"; +static char rxvt_s_rmso [] = "\033[27m"; +static char rxvt_s_rmul [] = "\033[24m"; +static char rxvt_s_flash [] = "\033[?5h$<100/>\033[?5l"; +static char rxvt_s_is1 [] = "\033[?47l\033=\033[?1l"; +static char rxvt_s_is2 [] = "\033[r\033[m\033[2J\033[H\033[?7h\033[?1;3;4;6l\033[4l"; +static char rxvt_s_ich1 [] = "\033[@"; +static char rxvt_s_il1 [] = "\033[L"; +static char rxvt_s_kbs [] = "\010"; +static char rxvt_s_kdch1 [] = "\033[3~"; +static char rxvt_s_kcud1 [] = "\033[B"; +static char rxvt_s_kel [] = "\033[8^"; +static char rxvt_s_kf0 [] = "\033[21~"; +static char rxvt_s_kf1 [] = "\033[11~"; +static char rxvt_s_kf10 [] = "\033[21~"; +static char rxvt_s_kf2 [] = "\033[12~"; +static char rxvt_s_kf3 [] = "\033[13~"; +static char rxvt_s_kf4 [] = "\033[14~"; +static char rxvt_s_kf5 [] = "\033[15~"; +static char rxvt_s_kf6 [] = "\033[17~"; +static char rxvt_s_kf7 [] = "\033[18~"; +static char rxvt_s_kf8 [] = "\033[19~"; +static char rxvt_s_kf9 [] = "\033[20~"; +static char rxvt_s_khome [] = "\033[7~"; +static char rxvt_s_kich1 [] = "\033[2~"; +static char rxvt_s_kcub1 [] = "\033[D"; +static char rxvt_s_knp [] = "\033[6~"; +static char rxvt_s_kpp [] = "\033[5~"; +static char rxvt_s_kcuf1 [] = "\033[C"; +static char rxvt_s_kind [] = "\033[a"; +static char rxvt_s_kri [] = "\033[b"; +static char rxvt_s_kcuu1 [] = "\033[A"; +static char rxvt_s_rmkx [] = "\033>"; +static char rxvt_s_smkx [] = "\033="; +static char rxvt_s_dl [] = "\033[%p1%dM"; +static char rxvt_s_cud [] = "\033[%p1%dB"; +static char rxvt_s_ich [] = "\033[%p1%d@"; +static char rxvt_s_il [] = "\033[%p1%dL"; +static char rxvt_s_cub [] = "\033[%p1%dD"; +static char rxvt_s_cuf [] = "\033[%p1%dC"; +static char rxvt_s_cuu [] = "\033[%p1%dA"; +static char rxvt_s_rs1 [] = "\033>\033[1;3;4;5;6l\033[?7h\033[m\033[r\033[2J\033[H"; +static char rxvt_s_rs2 [] = "\033[r\033[m\033[2J\033[H\033[?7h\033[?1;3;4;6l\033[4l\033>\033[?1000l\033[?25h"; +static char rxvt_s_rc [] = "\0338"; +static char rxvt_s_vpa [] = "\033[%i%p1%dd"; +static char rxvt_s_sc [] = "\0337"; +static char rxvt_s_ind [] = "\012"; +static char rxvt_s_ri [] = "\033M"; +static char rxvt_s_sgr [] = "\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;"; +static char rxvt_s_hts [] = "\033H"; +static char rxvt_s_ht [] = "\011"; +static char rxvt_s_ka1 [] = "\033Ow"; +static char rxvt_s_ka3 [] = "\033Oy"; +static char rxvt_s_kb2 [] = "\033Ou"; +static char rxvt_s_kc1 [] = "\033Oq"; +static char rxvt_s_kc3 [] = "\033Os"; +static char rxvt_s_acsc [] = "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char rxvt_s_kcbt [] = "\033[Z"; +static char rxvt_s_enacs [] = "\033(B\033)0"; +static char rxvt_s_kend [] = "\033[8~"; +static char rxvt_s_kent [] = "\033OM"; +static char rxvt_s_kfnd [] = "\033[1~"; +static char rxvt_s_kDC [] = "\033[3$"; +static char rxvt_s_kslt [] = "\033[4~"; +static char rxvt_s_kEND [] = "\033[8$"; +static char rxvt_s_kHOM [] = "\033[7$"; +static char rxvt_s_kIC [] = "\033[2$"; +static char rxvt_s_kLFT [] = "\033[d"; +static char rxvt_s_kNXT [] = "\033[6$"; +static char rxvt_s_kPRV [] = "\033[5$"; +static char rxvt_s_kRIT [] = "\033[c"; +static char rxvt_s_kf11 [] = "\033[23~"; +static char rxvt_s_kf12 [] = "\033[24~"; +static char rxvt_s_kf13 [] = "\033[25~"; +static char rxvt_s_kf14 [] = "\033[26~"; +static char rxvt_s_kf15 [] = "\033[28~"; +static char rxvt_s_kf16 [] = "\033[29~"; +static char rxvt_s_kf17 [] = "\033[31~"; +static char rxvt_s_kf18 [] = "\033[32~"; +static char rxvt_s_kf19 [] = "\033[33~"; +static char rxvt_s_kf20 [] = "\033[34~"; +static char rxvt_s_kf21 [] = "\033[23$"; +static char rxvt_s_kf22 [] = "\033[24$"; +static char rxvt_s_kf23 [] = "\033[11^"; +static char rxvt_s_kf24 [] = "\033[12^"; +static char rxvt_s_kf25 [] = "\033[13^"; +static char rxvt_s_kf26 [] = "\033[14^"; +static char rxvt_s_kf27 [] = "\033[15^"; +static char rxvt_s_kf28 [] = "\033[17^"; +static char rxvt_s_kf29 [] = "\033[18^"; +static char rxvt_s_kf30 [] = "\033[19^"; +static char rxvt_s_kf31 [] = "\033[20^"; +static char rxvt_s_kf32 [] = "\033[21^"; +static char rxvt_s_kf33 [] = "\033[23^"; +static char rxvt_s_kf34 [] = "\033[24^"; +static char rxvt_s_kf35 [] = "\033[25^"; +static char rxvt_s_kf36 [] = "\033[26^"; +static char rxvt_s_kf37 [] = "\033[28^"; +static char rxvt_s_kf38 [] = "\033[29^"; +static char rxvt_s_kf39 [] = "\033[31^"; +static char rxvt_s_kf40 [] = "\033[32^"; +static char rxvt_s_kf41 [] = "\033[33^"; +static char rxvt_s_kf42 [] = "\033[34^"; +static char rxvt_s_kf43 [] = "\033[23@"; +static char rxvt_s_kf44 [] = "\033[24@"; +static char rxvt_s_el1 [] = "\033[1K"; +static char rxvt_s_u6 [] = "\033[%i%d;%dR"; +static char rxvt_s_u7 [] = "\033[6n"; +static char rxvt_s_u8 [] = "\033[?1;2c"; +static char rxvt_s_u9 [] = "\033[c"; +static char rxvt_s_op [] = "\033[39;49m"; +static char rxvt_s_kmous [] = "\033[M"; +static char rxvt_s_setaf [] = "\033[3%p1%dm"; +static char rxvt_s_setab [] = "\033[4%p1%dm"; +static char rxvt_s_s0ds [] = "\033(B"; +static char rxvt_s_s1ds [] = "\033(0"; + +static char rxvt_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ TRUE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ FALSE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ TRUE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 rxvt_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ CANCELLED_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * rxvt_string_data[] = { + /* 0: cbt */ ABSENT_STRING, + /* 1: bel */ rxvt_s_bel, + /* 2: cr */ rxvt_s_cr, + /* 3: csr */ rxvt_s_csr, + /* 4: tbc */ rxvt_s_tbc, + /* 5: clear */ rxvt_s_clear, + /* 6: el */ rxvt_s_el, + /* 7: ed */ rxvt_s_ed, + /* 8: hpa */ rxvt_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ rxvt_s_cup, + /* 11: cud1 */ rxvt_s_cud1, + /* 12: home */ rxvt_s_home, + /* 13: civis */ rxvt_s_civis, + /* 14: cub1 */ rxvt_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ rxvt_s_cnorm, + /* 17: cuf1 */ rxvt_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ rxvt_s_cuu1, + /* 20: cvvis */ ABSENT_STRING, + /* 21: dch1 */ ABSENT_STRING, + /* 22: dl1 */ rxvt_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ rxvt_s_smacs, + /* 26: blink */ rxvt_s_blink, + /* 27: bold */ rxvt_s_bold, + /* 28: smcup */ rxvt_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ ABSENT_STRING, + /* 31: smir */ rxvt_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ rxvt_s_rev, + /* 35: smso */ rxvt_s_smso, + /* 36: smul */ rxvt_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ rxvt_s_rmacs, + /* 39: sgr0 */ rxvt_s_sgr0, + /* 40: rmcup */ rxvt_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ rxvt_s_rmir, + /* 43: rmso */ rxvt_s_rmso, + /* 44: rmul */ rxvt_s_rmul, + /* 45: flash */ rxvt_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ rxvt_s_is1, + /* 49: is2 */ rxvt_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ rxvt_s_ich1, + /* 53: il1 */ rxvt_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ rxvt_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ rxvt_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ rxvt_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ rxvt_s_kel, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ rxvt_s_kf0, + /* 66: kf1 */ rxvt_s_kf1, + /* 67: kf10 */ rxvt_s_kf10, + /* 68: kf2 */ rxvt_s_kf2, + /* 69: kf3 */ rxvt_s_kf3, + /* 70: kf4 */ rxvt_s_kf4, + /* 71: kf5 */ rxvt_s_kf5, + /* 72: kf6 */ rxvt_s_kf6, + /* 73: kf7 */ rxvt_s_kf7, + /* 74: kf8 */ rxvt_s_kf8, + /* 75: kf9 */ rxvt_s_kf9, + /* 76: khome */ rxvt_s_khome, + /* 77: kich1 */ rxvt_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ rxvt_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ rxvt_s_knp, + /* 82: kpp */ rxvt_s_kpp, + /* 83: kcuf1 */ rxvt_s_kcuf1, + /* 84: kind */ rxvt_s_kind, + /* 85: kri */ rxvt_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ rxvt_s_kcuu1, + /* 88: rmkx */ rxvt_s_rmkx, + /* 89: smkx */ rxvt_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ ABSENT_STRING, + /* 106: dl */ rxvt_s_dl, + /* 107: cud */ rxvt_s_cud, + /* 108: ich */ rxvt_s_ich, + /* 109: indn */ ABSENT_STRING, + /* 110: il */ rxvt_s_il, + /* 111: cub */ rxvt_s_cub, + /* 112: cuf */ rxvt_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ rxvt_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ rxvt_s_rs1, + /* 123: rs2 */ rxvt_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ rxvt_s_rc, + /* 127: vpa */ rxvt_s_vpa, + /* 128: sc */ rxvt_s_sc, + /* 129: ind */ rxvt_s_ind, + /* 130: ri */ rxvt_s_ri, + /* 131: sgr */ rxvt_s_sgr, + /* 132: hts */ rxvt_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ rxvt_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ rxvt_s_ka1, + /* 140: ka3 */ rxvt_s_ka3, + /* 141: kb2 */ rxvt_s_kb2, + /* 142: kc1 */ rxvt_s_kc1, + /* 143: kc3 */ rxvt_s_kc3, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ rxvt_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ rxvt_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ rxvt_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ rxvt_s_kend, + /* 165: kent */ rxvt_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ rxvt_s_kfnd, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ rxvt_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ rxvt_s_kslt, + /* 194: kEND */ rxvt_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ rxvt_s_kHOM, + /* 200: kIC */ rxvt_s_kIC, + /* 201: kLFT */ rxvt_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ rxvt_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ rxvt_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ rxvt_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ rxvt_s_kf11, + /* 217: kf12 */ rxvt_s_kf12, + /* 218: kf13 */ rxvt_s_kf13, + /* 219: kf14 */ rxvt_s_kf14, + /* 220: kf15 */ rxvt_s_kf15, + /* 221: kf16 */ rxvt_s_kf16, + /* 222: kf17 */ rxvt_s_kf17, + /* 223: kf18 */ rxvt_s_kf18, + /* 224: kf19 */ rxvt_s_kf19, + /* 225: kf20 */ rxvt_s_kf20, + /* 226: kf21 */ rxvt_s_kf21, + /* 227: kf22 */ rxvt_s_kf22, + /* 228: kf23 */ rxvt_s_kf23, + /* 229: kf24 */ rxvt_s_kf24, + /* 230: kf25 */ rxvt_s_kf25, + /* 231: kf26 */ rxvt_s_kf26, + /* 232: kf27 */ rxvt_s_kf27, + /* 233: kf28 */ rxvt_s_kf28, + /* 234: kf29 */ rxvt_s_kf29, + /* 235: kf30 */ rxvt_s_kf30, + /* 236: kf31 */ rxvt_s_kf31, + /* 237: kf32 */ rxvt_s_kf32, + /* 238: kf33 */ rxvt_s_kf33, + /* 239: kf34 */ rxvt_s_kf34, + /* 240: kf35 */ rxvt_s_kf35, + /* 241: kf36 */ rxvt_s_kf36, + /* 242: kf37 */ rxvt_s_kf37, + /* 243: kf38 */ rxvt_s_kf38, + /* 244: kf39 */ rxvt_s_kf39, + /* 245: kf40 */ rxvt_s_kf40, + /* 246: kf41 */ rxvt_s_kf41, + /* 247: kf42 */ rxvt_s_kf42, + /* 248: kf43 */ rxvt_s_kf43, + /* 249: kf44 */ rxvt_s_kf44, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ rxvt_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ rxvt_s_u6, + /* 294: u7 */ rxvt_s_u7, + /* 295: u8 */ rxvt_s_u8, + /* 296: u9 */ rxvt_s_u9, + /* 297: op */ rxvt_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ rxvt_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ rxvt_s_setaf, + /* 360: setab */ rxvt_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ rxvt_s_s0ds, + /* 365: s1ds */ rxvt_s_s1ds, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* vt100 */ + +static char vt100_alias_data[] = "vt100|vt100-am|dec vt100 (w/advanced video)"; + +static char vt100_s_bel [] = "\007"; +static char vt100_s_cr [] = "\015"; +static char vt100_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char vt100_s_tbc [] = "\033[3g"; +static char vt100_s_clear [] = "\033[H\033[J$<50>"; +static char vt100_s_el [] = "\033[K$<3>"; +static char vt100_s_ed [] = "\033[J$<50>"; +static char vt100_s_cup [] = "\033[%i%p1%d;%p2%dH$<5>"; +static char vt100_s_cud1 [] = "\012"; +static char vt100_s_home [] = "\033[H"; +static char vt100_s_cub1 [] = "\010"; +static char vt100_s_cuf1 [] = "\033[C$<2>"; +static char vt100_s_cuu1 [] = "\033[A$<2>"; +static char vt100_s_smacs [] = "\016"; +static char vt100_s_blink [] = "\033[5m$<2>"; +static char vt100_s_bold [] = "\033[1m$<2>"; +static char vt100_s_rev [] = "\033[7m$<2>"; +static char vt100_s_smso [] = "\033[7m$<2>"; +static char vt100_s_smul [] = "\033[4m$<2>"; +static char vt100_s_rmacs [] = "\017"; +static char vt100_s_sgr0 [] = "\033[m\017$<2>"; +static char vt100_s_rmso [] = "\033[m$<2>"; +static char vt100_s_rmul [] = "\033[m$<2>"; +static char vt100_s_kbs [] = "\010"; +static char vt100_s_kcud1 [] = "\033OB"; +static char vt100_s_kf0 [] = "\033Oy"; +static char vt100_s_kf1 [] = "\033OP"; +static char vt100_s_kf10 [] = "\033Ox"; +static char vt100_s_kf2 [] = "\033OQ"; +static char vt100_s_kf3 [] = "\033OR"; +static char vt100_s_kf4 [] = "\033OS"; +static char vt100_s_kf5 [] = "\033Ot"; +static char vt100_s_kf6 [] = "\033Ou"; +static char vt100_s_kf7 [] = "\033Ov"; +static char vt100_s_kf8 [] = "\033Ol"; +static char vt100_s_kf9 [] = "\033Ow"; +static char vt100_s_kcub1 [] = "\033OD"; +static char vt100_s_kcuf1 [] = "\033OC"; +static char vt100_s_kcuu1 [] = "\033OA"; +static char vt100_s_rmkx [] = "\033[?1l\033>"; +static char vt100_s_smkx [] = "\033[?1h\033="; +static char vt100_s_lf1 [] = "pf1"; +static char vt100_s_lf2 [] = "pf2"; +static char vt100_s_lf3 [] = "pf3"; +static char vt100_s_lf4 [] = "pf4"; +static char vt100_s_cud [] = "\033[%p1%dB"; +static char vt100_s_cub [] = "\033[%p1%dD"; +static char vt100_s_cuf [] = "\033[%p1%dC"; +static char vt100_s_cuu [] = "\033[%p1%dA"; +static char vt100_s_mc0 [] = "\033[0i"; +static char vt100_s_mc4 [] = "\033[4i"; +static char vt100_s_mc5 [] = "\033[5i"; +static char vt100_s_rs2 [] = "\033<\033>\033[?3;4;5l\033[?7;8h\033[r"; +static char vt100_s_rc [] = "\0338"; +static char vt100_s_sc [] = "\0337"; +static char vt100_s_ind [] = "\012"; +static char vt100_s_ri [] = "\033M$<5>"; +static char vt100_s_sgr [] = "\033[0%?%p1%p6%|%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;$<2>"; +static char vt100_s_hts [] = "\033H"; +static char vt100_s_ht [] = "\011"; +static char vt100_s_ka1 [] = "\033Oq"; +static char vt100_s_ka3 [] = "\033Os"; +static char vt100_s_kb2 [] = "\033Or"; +static char vt100_s_kc1 [] = "\033Op"; +static char vt100_s_kc3 [] = "\033On"; +static char vt100_s_acsc [] = "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char vt100_s_smam [] = "\033[?7h"; +static char vt100_s_rmam [] = "\033[?7l"; +static char vt100_s_enacs [] = "\033(B\033)0"; +static char vt100_s_kent [] = "\033OM"; +static char vt100_s_el1 [] = "\033[1K$<3>"; + +static char vt100_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ FALSE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ FALSE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ TRUE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 vt100_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ 3, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ ABSENT_NUMERIC, + /* 14: pairs */ ABSENT_NUMERIC, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * vt100_string_data[] = { + /* 0: cbt */ ABSENT_STRING, + /* 1: bel */ vt100_s_bel, + /* 2: cr */ vt100_s_cr, + /* 3: csr */ vt100_s_csr, + /* 4: tbc */ vt100_s_tbc, + /* 5: clear */ vt100_s_clear, + /* 6: el */ vt100_s_el, + /* 7: ed */ vt100_s_ed, + /* 8: hpa */ ABSENT_STRING, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ vt100_s_cup, + /* 11: cud1 */ vt100_s_cud1, + /* 12: home */ vt100_s_home, + /* 13: civis */ ABSENT_STRING, + /* 14: cub1 */ vt100_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ ABSENT_STRING, + /* 17: cuf1 */ vt100_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ vt100_s_cuu1, + /* 20: cvvis */ ABSENT_STRING, + /* 21: dch1 */ ABSENT_STRING, + /* 22: dl1 */ ABSENT_STRING, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ vt100_s_smacs, + /* 26: blink */ vt100_s_blink, + /* 27: bold */ vt100_s_bold, + /* 28: smcup */ ABSENT_STRING, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ ABSENT_STRING, + /* 31: smir */ ABSENT_STRING, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ vt100_s_rev, + /* 35: smso */ vt100_s_smso, + /* 36: smul */ vt100_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ vt100_s_rmacs, + /* 39: sgr0 */ vt100_s_sgr0, + /* 40: rmcup */ ABSENT_STRING, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ ABSENT_STRING, + /* 43: rmso */ vt100_s_rmso, + /* 44: rmul */ vt100_s_rmul, + /* 45: flash */ ABSENT_STRING, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ ABSENT_STRING, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ ABSENT_STRING, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ vt100_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ ABSENT_STRING, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ vt100_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ vt100_s_kf0, + /* 66: kf1 */ vt100_s_kf1, + /* 67: kf10 */ vt100_s_kf10, + /* 68: kf2 */ vt100_s_kf2, + /* 69: kf3 */ vt100_s_kf3, + /* 70: kf4 */ vt100_s_kf4, + /* 71: kf5 */ vt100_s_kf5, + /* 72: kf6 */ vt100_s_kf6, + /* 73: kf7 */ vt100_s_kf7, + /* 74: kf8 */ vt100_s_kf8, + /* 75: kf9 */ vt100_s_kf9, + /* 76: khome */ ABSENT_STRING, + /* 77: kich1 */ ABSENT_STRING, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ vt100_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ ABSENT_STRING, + /* 82: kpp */ ABSENT_STRING, + /* 83: kcuf1 */ vt100_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ vt100_s_kcuu1, + /* 88: rmkx */ vt100_s_rmkx, + /* 89: smkx */ vt100_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ vt100_s_lf1, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ vt100_s_lf2, + /* 94: lf3 */ vt100_s_lf3, + /* 95: lf4 */ vt100_s_lf4, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ ABSENT_STRING, + /* 106: dl */ ABSENT_STRING, + /* 107: cud */ vt100_s_cud, + /* 108: ich */ ABSENT_STRING, + /* 109: indn */ ABSENT_STRING, + /* 110: il */ ABSENT_STRING, + /* 111: cub */ vt100_s_cub, + /* 112: cuf */ vt100_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ vt100_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ vt100_s_mc0, + /* 119: mc4 */ vt100_s_mc4, + /* 120: mc5 */ vt100_s_mc5, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ vt100_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ vt100_s_rc, + /* 127: vpa */ ABSENT_STRING, + /* 128: sc */ vt100_s_sc, + /* 129: ind */ vt100_s_ind, + /* 130: ri */ vt100_s_ri, + /* 131: sgr */ vt100_s_sgr, + /* 132: hts */ vt100_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ vt100_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ vt100_s_ka1, + /* 140: ka3 */ vt100_s_ka3, + /* 141: kb2 */ vt100_s_kb2, + /* 142: kc1 */ vt100_s_kc1, + /* 143: kc3 */ vt100_s_kc3, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ vt100_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ ABSENT_STRING, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ vt100_s_smam, + /* 152: rmam */ vt100_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ vt100_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ ABSENT_STRING, + /* 165: kent */ vt100_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ ABSENT_STRING, + /* 217: kf12 */ ABSENT_STRING, + /* 218: kf13 */ ABSENT_STRING, + /* 219: kf14 */ ABSENT_STRING, + /* 220: kf15 */ ABSENT_STRING, + /* 221: kf16 */ ABSENT_STRING, + /* 222: kf17 */ ABSENT_STRING, + /* 223: kf18 */ ABSENT_STRING, + /* 224: kf19 */ ABSENT_STRING, + /* 225: kf20 */ ABSENT_STRING, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ vt100_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ ABSENT_STRING, + /* 294: u7 */ ABSENT_STRING, + /* 295: u8 */ ABSENT_STRING, + /* 296: u9 */ ABSENT_STRING, + /* 297: op */ ABSENT_STRING, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ ABSENT_STRING, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ ABSENT_STRING, + /* 360: setab */ ABSENT_STRING, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* xterm */ + +static char xterm_alias_data[] = "xterm|xterm terminal emulator (X Window System)"; + +static char xterm_s_cbt [] = "\033[Z"; +static char xterm_s_bel [] = "\007"; +static char xterm_s_cr [] = "\015"; +static char xterm_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char xterm_s_tbc [] = "\033[3g"; +static char xterm_s_clear [] = "\033[H\033[2J"; +static char xterm_s_el [] = "\033[K"; +static char xterm_s_ed [] = "\033[J"; +static char xterm_s_hpa [] = "\033[%i%p1%dG"; +static char xterm_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char xterm_s_cud1 [] = "\012"; +static char xterm_s_home [] = "\033[H"; +static char xterm_s_civis [] = "\033[?25l"; +static char xterm_s_cub1 [] = "\010"; +static char xterm_s_cnorm [] = "\033[?12l\033[?25h"; +static char xterm_s_cuf1 [] = "\033[C"; +static char xterm_s_cuu1 [] = "\033[A"; +static char xterm_s_cvvis [] = "\033[?12;25h"; +static char xterm_s_dch1 [] = "\033[P"; +static char xterm_s_dl1 [] = "\033[M"; +static char xterm_s_smacs [] = "\033(0"; +static char xterm_s_blink [] = "\033[5m"; +static char xterm_s_bold [] = "\033[1m"; +static char xterm_s_smcup [] = "\033[?1049h\033[22;0;0t"; +static char xterm_s_dim [] = "\033[2m"; +static char xterm_s_smir [] = "\033[4h"; +static char xterm_s_invis [] = "\033[8m"; +static char xterm_s_rev [] = "\033[7m"; +static char xterm_s_smso [] = "\033[7m"; +static char xterm_s_smul [] = "\033[4m"; +static char xterm_s_ech [] = "\033[%p1%dX"; +static char xterm_s_rmacs [] = "\033(B"; +static char xterm_s_sgr0 [] = "\033(B\033[m"; +static char xterm_s_rmcup [] = "\033[?1049l\033[23;0;0t"; +static char xterm_s_rmir [] = "\033[4l"; +static char xterm_s_rmso [] = "\033[27m"; +static char xterm_s_rmul [] = "\033[24m"; +static char xterm_s_flash [] = "\033[?5h$<100/>\033[?5l"; +static char xterm_s_is2 [] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_s_il1 [] = "\033[L"; +static char xterm_s_kbs [] = "\010"; +static char xterm_s_kdch1 [] = "\033[3~"; +static char xterm_s_kcud1 [] = "\033OB"; +static char xterm_s_kf1 [] = "\033OP"; +static char xterm_s_kf10 [] = "\033[21~"; +static char xterm_s_kf2 [] = "\033OQ"; +static char xterm_s_kf3 [] = "\033OR"; +static char xterm_s_kf4 [] = "\033OS"; +static char xterm_s_kf5 [] = "\033[15~"; +static char xterm_s_kf6 [] = "\033[17~"; +static char xterm_s_kf7 [] = "\033[18~"; +static char xterm_s_kf8 [] = "\033[19~"; +static char xterm_s_kf9 [] = "\033[20~"; +static char xterm_s_khome [] = "\033OH"; +static char xterm_s_kich1 [] = "\033[2~"; +static char xterm_s_kcub1 [] = "\033OD"; +static char xterm_s_knp [] = "\033[6~"; +static char xterm_s_kpp [] = "\033[5~"; +static char xterm_s_kcuf1 [] = "\033OC"; +static char xterm_s_kind [] = "\033[1;2B"; +static char xterm_s_kri [] = "\033[1;2A"; +static char xterm_s_kcuu1 [] = "\033OA"; +static char xterm_s_rmkx [] = "\033[?1l\033>"; +static char xterm_s_smkx [] = "\033[?1h\033="; +static char xterm_s_rmm [] = "\033[?1034l"; +static char xterm_s_smm [] = "\033[?1034h"; +static char xterm_s_dch [] = "\033[%p1%dP"; +static char xterm_s_dl [] = "\033[%p1%dM"; +static char xterm_s_cud [] = "\033[%p1%dB"; +static char xterm_s_ich [] = "\033[%p1%d@"; +static char xterm_s_indn [] = "\033[%p1%dS"; +static char xterm_s_il [] = "\033[%p1%dL"; +static char xterm_s_cub [] = "\033[%p1%dD"; +static char xterm_s_cuf [] = "\033[%p1%dC"; +static char xterm_s_rin [] = "\033[%p1%dT"; +static char xterm_s_cuu [] = "\033[%p1%dA"; +static char xterm_s_mc0 [] = "\033[i"; +static char xterm_s_mc4 [] = "\033[4i"; +static char xterm_s_mc5 [] = "\033[5i"; +static char xterm_s_rep [] = "%p1%c\033[%p2%{1}%-%db"; +static char xterm_s_rs1 [] = "\033c"; +static char xterm_s_rs2 [] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_s_rc [] = "\0338"; +static char xterm_s_vpa [] = "\033[%i%p1%dd"; +static char xterm_s_sc [] = "\0337"; +static char xterm_s_ind [] = "\012"; +static char xterm_s_ri [] = "\033M"; +static char xterm_s_sgr [] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m"; +static char xterm_s_hts [] = "\033H"; +static char xterm_s_ht [] = "\011"; +static char xterm_s_kb2 [] = "\033OE"; +static char xterm_s_acsc [] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char xterm_s_kcbt [] = "\033[Z"; +static char xterm_s_smam [] = "\033[?7h"; +static char xterm_s_rmam [] = "\033[?7l"; +static char xterm_s_kend [] = "\033OF"; +static char xterm_s_kent [] = "\033OM"; +static char xterm_s_kDC [] = "\033[3;2~"; +static char xterm_s_kEND [] = "\033[1;2F"; +static char xterm_s_kHOM [] = "\033[1;2H"; +static char xterm_s_kIC [] = "\033[2;2~"; +static char xterm_s_kLFT [] = "\033[1;2D"; +static char xterm_s_kNXT [] = "\033[6;2~"; +static char xterm_s_kPRV [] = "\033[5;2~"; +static char xterm_s_kRIT [] = "\033[1;2C"; +static char xterm_s_kf11 [] = "\033[23~"; +static char xterm_s_kf12 [] = "\033[24~"; +static char xterm_s_kf13 [] = "\033[1;2P"; +static char xterm_s_kf14 [] = "\033[1;2Q"; +static char xterm_s_kf15 [] = "\033[1;2R"; +static char xterm_s_kf16 [] = "\033[1;2S"; +static char xterm_s_kf17 [] = "\033[15;2~"; +static char xterm_s_kf18 [] = "\033[17;2~"; +static char xterm_s_kf19 [] = "\033[18;2~"; +static char xterm_s_kf20 [] = "\033[19;2~"; +static char xterm_s_kf21 [] = "\033[20;2~"; +static char xterm_s_kf22 [] = "\033[21;2~"; +static char xterm_s_kf23 [] = "\033[23;2~"; +static char xterm_s_kf24 [] = "\033[24;2~"; +static char xterm_s_kf25 [] = "\033[1;5P"; +static char xterm_s_kf26 [] = "\033[1;5Q"; +static char xterm_s_kf27 [] = "\033[1;5R"; +static char xterm_s_kf28 [] = "\033[1;5S"; +static char xterm_s_kf29 [] = "\033[15;5~"; +static char xterm_s_kf30 [] = "\033[17;5~"; +static char xterm_s_kf31 [] = "\033[18;5~"; +static char xterm_s_kf32 [] = "\033[19;5~"; +static char xterm_s_kf33 [] = "\033[20;5~"; +static char xterm_s_kf34 [] = "\033[21;5~"; +static char xterm_s_kf35 [] = "\033[23;5~"; +static char xterm_s_kf36 [] = "\033[24;5~"; +static char xterm_s_kf37 [] = "\033[1;6P"; +static char xterm_s_kf38 [] = "\033[1;6Q"; +static char xterm_s_kf39 [] = "\033[1;6R"; +static char xterm_s_kf40 [] = "\033[1;6S"; +static char xterm_s_kf41 [] = "\033[15;6~"; +static char xterm_s_kf42 [] = "\033[17;6~"; +static char xterm_s_kf43 [] = "\033[18;6~"; +static char xterm_s_kf44 [] = "\033[19;6~"; +static char xterm_s_kf45 [] = "\033[20;6~"; +static char xterm_s_kf46 [] = "\033[21;6~"; +static char xterm_s_kf47 [] = "\033[23;6~"; +static char xterm_s_kf48 [] = "\033[24;6~"; +static char xterm_s_kf49 [] = "\033[1;3P"; +static char xterm_s_kf50 [] = "\033[1;3Q"; +static char xterm_s_kf51 [] = "\033[1;3R"; +static char xterm_s_kf52 [] = "\033[1;3S"; +static char xterm_s_kf53 [] = "\033[15;3~"; +static char xterm_s_kf54 [] = "\033[17;3~"; +static char xterm_s_kf55 [] = "\033[18;3~"; +static char xterm_s_kf56 [] = "\033[19;3~"; +static char xterm_s_kf57 [] = "\033[20;3~"; +static char xterm_s_kf58 [] = "\033[21;3~"; +static char xterm_s_kf59 [] = "\033[23;3~"; +static char xterm_s_kf60 [] = "\033[24;3~"; +static char xterm_s_kf61 [] = "\033[1;4P"; +static char xterm_s_kf62 [] = "\033[1;4Q"; +static char xterm_s_kf63 [] = "\033[1;4R"; +static char xterm_s_el1 [] = "\033[1K"; +static char xterm_s_u6 [] = "\033[%i%d;%dR"; +static char xterm_s_u7 [] = "\033[6n"; +static char xterm_s_u8 [] = "\033[?%[;0123456789]c"; +static char xterm_s_u9 [] = "\033[c"; +static char xterm_s_op [] = "\033[39;49m"; +static char xterm_s_setf [] = "\033[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char xterm_s_setb [] = "\033[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char xterm_s_sitm [] = "\033[3m"; +static char xterm_s_ritm [] = "\033[23m"; +static char xterm_s_kmous [] = "\033[<"; +static char xterm_s_setaf [] = "\033[3%p1%dm"; +static char xterm_s_setab [] = "\033[4%p1%dm"; +static char xterm_s_meml [] = "\033l"; +static char xterm_s_memu [] = "\033m"; + +static char xterm_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 xterm_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * xterm_string_data[] = { + /* 0: cbt */ xterm_s_cbt, + /* 1: bel */ xterm_s_bel, + /* 2: cr */ xterm_s_cr, + /* 3: csr */ xterm_s_csr, + /* 4: tbc */ xterm_s_tbc, + /* 5: clear */ xterm_s_clear, + /* 6: el */ xterm_s_el, + /* 7: ed */ xterm_s_ed, + /* 8: hpa */ xterm_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ xterm_s_cup, + /* 11: cud1 */ xterm_s_cud1, + /* 12: home */ xterm_s_home, + /* 13: civis */ xterm_s_civis, + /* 14: cub1 */ xterm_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ xterm_s_cnorm, + /* 17: cuf1 */ xterm_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ xterm_s_cuu1, + /* 20: cvvis */ xterm_s_cvvis, + /* 21: dch1 */ xterm_s_dch1, + /* 22: dl1 */ xterm_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ xterm_s_smacs, + /* 26: blink */ xterm_s_blink, + /* 27: bold */ xterm_s_bold, + /* 28: smcup */ xterm_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ xterm_s_dim, + /* 31: smir */ xterm_s_smir, + /* 32: invis */ xterm_s_invis, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ xterm_s_rev, + /* 35: smso */ xterm_s_smso, + /* 36: smul */ xterm_s_smul, + /* 37: ech */ xterm_s_ech, + /* 38: rmacs */ xterm_s_rmacs, + /* 39: sgr0 */ xterm_s_sgr0, + /* 40: rmcup */ xterm_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ xterm_s_rmir, + /* 43: rmso */ xterm_s_rmso, + /* 44: rmul */ xterm_s_rmul, + /* 45: flash */ xterm_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ xterm_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ xterm_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ xterm_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ xterm_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ xterm_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ xterm_s_kf1, + /* 67: kf10 */ xterm_s_kf10, + /* 68: kf2 */ xterm_s_kf2, + /* 69: kf3 */ xterm_s_kf3, + /* 70: kf4 */ xterm_s_kf4, + /* 71: kf5 */ xterm_s_kf5, + /* 72: kf6 */ xterm_s_kf6, + /* 73: kf7 */ xterm_s_kf7, + /* 74: kf8 */ xterm_s_kf8, + /* 75: kf9 */ xterm_s_kf9, + /* 76: khome */ xterm_s_khome, + /* 77: kich1 */ xterm_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ xterm_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ xterm_s_knp, + /* 82: kpp */ xterm_s_kpp, + /* 83: kcuf1 */ xterm_s_kcuf1, + /* 84: kind */ xterm_s_kind, + /* 85: kri */ xterm_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ xterm_s_kcuu1, + /* 88: rmkx */ xterm_s_rmkx, + /* 89: smkx */ xterm_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ xterm_s_rmm, + /* 102: smm */ xterm_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ xterm_s_dch, + /* 106: dl */ xterm_s_dl, + /* 107: cud */ xterm_s_cud, + /* 108: ich */ xterm_s_ich, + /* 109: indn */ xterm_s_indn, + /* 110: il */ xterm_s_il, + /* 111: cub */ xterm_s_cub, + /* 112: cuf */ xterm_s_cuf, + /* 113: rin */ xterm_s_rin, + /* 114: cuu */ xterm_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ xterm_s_mc0, + /* 119: mc4 */ xterm_s_mc4, + /* 120: mc5 */ xterm_s_mc5, + /* 121: rep */ xterm_s_rep, + /* 122: rs1 */ xterm_s_rs1, + /* 123: rs2 */ xterm_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ xterm_s_rc, + /* 127: vpa */ xterm_s_vpa, + /* 128: sc */ xterm_s_sc, + /* 129: ind */ xterm_s_ind, + /* 130: ri */ xterm_s_ri, + /* 131: sgr */ xterm_s_sgr, + /* 132: hts */ xterm_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ xterm_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ xterm_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ xterm_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ xterm_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ xterm_s_smam, + /* 152: rmam */ xterm_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ xterm_s_kend, + /* 165: kent */ xterm_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ xterm_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ xterm_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ xterm_s_kHOM, + /* 200: kIC */ xterm_s_kIC, + /* 201: kLFT */ xterm_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ xterm_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ xterm_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ xterm_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ xterm_s_kf11, + /* 217: kf12 */ xterm_s_kf12, + /* 218: kf13 */ xterm_s_kf13, + /* 219: kf14 */ xterm_s_kf14, + /* 220: kf15 */ xterm_s_kf15, + /* 221: kf16 */ xterm_s_kf16, + /* 222: kf17 */ xterm_s_kf17, + /* 223: kf18 */ xterm_s_kf18, + /* 224: kf19 */ xterm_s_kf19, + /* 225: kf20 */ xterm_s_kf20, + /* 226: kf21 */ xterm_s_kf21, + /* 227: kf22 */ xterm_s_kf22, + /* 228: kf23 */ xterm_s_kf23, + /* 229: kf24 */ xterm_s_kf24, + /* 230: kf25 */ xterm_s_kf25, + /* 231: kf26 */ xterm_s_kf26, + /* 232: kf27 */ xterm_s_kf27, + /* 233: kf28 */ xterm_s_kf28, + /* 234: kf29 */ xterm_s_kf29, + /* 235: kf30 */ xterm_s_kf30, + /* 236: kf31 */ xterm_s_kf31, + /* 237: kf32 */ xterm_s_kf32, + /* 238: kf33 */ xterm_s_kf33, + /* 239: kf34 */ xterm_s_kf34, + /* 240: kf35 */ xterm_s_kf35, + /* 241: kf36 */ xterm_s_kf36, + /* 242: kf37 */ xterm_s_kf37, + /* 243: kf38 */ xterm_s_kf38, + /* 244: kf39 */ xterm_s_kf39, + /* 245: kf40 */ xterm_s_kf40, + /* 246: kf41 */ xterm_s_kf41, + /* 247: kf42 */ xterm_s_kf42, + /* 248: kf43 */ xterm_s_kf43, + /* 249: kf44 */ xterm_s_kf44, + /* 250: kf45 */ xterm_s_kf45, + /* 251: kf46 */ xterm_s_kf46, + /* 252: kf47 */ xterm_s_kf47, + /* 253: kf48 */ xterm_s_kf48, + /* 254: kf49 */ xterm_s_kf49, + /* 255: kf50 */ xterm_s_kf50, + /* 256: kf51 */ xterm_s_kf51, + /* 257: kf52 */ xterm_s_kf52, + /* 258: kf53 */ xterm_s_kf53, + /* 259: kf54 */ xterm_s_kf54, + /* 260: kf55 */ xterm_s_kf55, + /* 261: kf56 */ xterm_s_kf56, + /* 262: kf57 */ xterm_s_kf57, + /* 263: kf58 */ xterm_s_kf58, + /* 264: kf59 */ xterm_s_kf59, + /* 265: kf60 */ xterm_s_kf60, + /* 266: kf61 */ xterm_s_kf61, + /* 267: kf62 */ xterm_s_kf62, + /* 268: kf63 */ xterm_s_kf63, + /* 269: el1 */ xterm_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ xterm_s_u6, + /* 294: u7 */ xterm_s_u7, + /* 295: u8 */ xterm_s_u8, + /* 296: u9 */ xterm_s_u9, + /* 297: op */ xterm_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ xterm_s_setf, + /* 303: setb */ xterm_s_setb, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ xterm_s_sitm, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ xterm_s_ritm, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ xterm_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ xterm_s_setaf, + /* 360: setab */ xterm_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ xterm_s_meml, + /* 412: memu */ xterm_s_memu, + /* 413: box1 */ ABSENT_STRING, +}; +/* xterm-256color */ + +static char xterm_256color_alias_data[] = "xterm-256color|xterm with 256 colors"; + +static char xterm_256color_s_cbt[] = "\033[Z"; +static char xterm_256color_s_bel[] = "\007"; +static char xterm_256color_s_cr [] = "\015"; +static char xterm_256color_s_csr[] = "\033[%i%p1%d;%p2%dr"; +static char xterm_256color_s_tbc[] = "\033[3g"; +static char xterm_256color_s_clear[] = "\033[H\033[2J"; +static char xterm_256color_s_el [] = "\033[K"; +static char xterm_256color_s_ed [] = "\033[J"; +static char xterm_256color_s_hpa[] = "\033[%i%p1%dG"; +static char xterm_256color_s_cup[] = "\033[%i%p1%d;%p2%dH"; +static char xterm_256color_s_cud1[] = "\012"; +static char xterm_256color_s_home[] = "\033[H"; +static char xterm_256color_s_civis[] = "\033[?25l"; +static char xterm_256color_s_cub1[] = "\010"; +static char xterm_256color_s_cnorm[] = "\033[?12l\033[?25h"; +static char xterm_256color_s_cuf1[] = "\033[C"; +static char xterm_256color_s_cuu1[] = "\033[A"; +static char xterm_256color_s_cvvis[] = "\033[?12;25h"; +static char xterm_256color_s_dch1[] = "\033[P"; +static char xterm_256color_s_dl1[] = "\033[M"; +static char xterm_256color_s_smacs[] = "\033(0"; +static char xterm_256color_s_blink[] = "\033[5m"; +static char xterm_256color_s_bold[] = "\033[1m"; +static char xterm_256color_s_smcup[] = "\033[?1049h\033[22;0;0t"; +static char xterm_256color_s_dim[] = "\033[2m"; +static char xterm_256color_s_smir[] = "\033[4h"; +static char xterm_256color_s_invis[] = "\033[8m"; +static char xterm_256color_s_rev[] = "\033[7m"; +static char xterm_256color_s_smso[] = "\033[7m"; +static char xterm_256color_s_smul[] = "\033[4m"; +static char xterm_256color_s_ech[] = "\033[%p1%dX"; +static char xterm_256color_s_rmacs[] = "\033(B"; +static char xterm_256color_s_sgr0[] = "\033(B\033[m"; +static char xterm_256color_s_rmcup[] = "\033[?1049l\033[23;0;0t"; +static char xterm_256color_s_rmir[] = "\033[4l"; +static char xterm_256color_s_rmso[] = "\033[27m"; +static char xterm_256color_s_rmul[] = "\033[24m"; +static char xterm_256color_s_flash[] = "\033[?5h$<100/>\033[?5l"; +static char xterm_256color_s_is2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_256color_s_il1[] = "\033[L"; +static char xterm_256color_s_kbs[] = "\010"; +static char xterm_256color_s_kdch1[] = "\033[3~"; +static char xterm_256color_s_kcud1[] = "\033OB"; +static char xterm_256color_s_kf1[] = "\033OP"; +static char xterm_256color_s_kf10[] = "\033[21~"; +static char xterm_256color_s_kf2[] = "\033OQ"; +static char xterm_256color_s_kf3[] = "\033OR"; +static char xterm_256color_s_kf4[] = "\033OS"; +static char xterm_256color_s_kf5[] = "\033[15~"; +static char xterm_256color_s_kf6[] = "\033[17~"; +static char xterm_256color_s_kf7[] = "\033[18~"; +static char xterm_256color_s_kf8[] = "\033[19~"; +static char xterm_256color_s_kf9[] = "\033[20~"; +static char xterm_256color_s_khome[] = "\033OH"; +static char xterm_256color_s_kich1[] = "\033[2~"; +static char xterm_256color_s_kcub1[] = "\033OD"; +static char xterm_256color_s_knp[] = "\033[6~"; +static char xterm_256color_s_kpp[] = "\033[5~"; +static char xterm_256color_s_kcuf1[] = "\033OC"; +static char xterm_256color_s_kind[] = "\033[1;2B"; +static char xterm_256color_s_kri[] = "\033[1;2A"; +static char xterm_256color_s_kcuu1[] = "\033OA"; +static char xterm_256color_s_rmkx[] = "\033[?1l\033>"; +static char xterm_256color_s_smkx[] = "\033[?1h\033="; +static char xterm_256color_s_rmm[] = "\033[?1034l"; +static char xterm_256color_s_smm[] = "\033[?1034h"; +static char xterm_256color_s_dch[] = "\033[%p1%dP"; +static char xterm_256color_s_dl [] = "\033[%p1%dM"; +static char xterm_256color_s_cud[] = "\033[%p1%dB"; +static char xterm_256color_s_ich[] = "\033[%p1%d@"; +static char xterm_256color_s_indn[] = "\033[%p1%dS"; +static char xterm_256color_s_il [] = "\033[%p1%dL"; +static char xterm_256color_s_cub[] = "\033[%p1%dD"; +static char xterm_256color_s_cuf[] = "\033[%p1%dC"; +static char xterm_256color_s_rin[] = "\033[%p1%dT"; +static char xterm_256color_s_cuu[] = "\033[%p1%dA"; +static char xterm_256color_s_mc0[] = "\033[i"; +static char xterm_256color_s_mc4[] = "\033[4i"; +static char xterm_256color_s_mc5[] = "\033[5i"; +static char xterm_256color_s_rep[] = "%p1%c\033[%p2%{1}%-%db"; +static char xterm_256color_s_rs1[] = "\033c\033]104\007"; +static char xterm_256color_s_rs2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_256color_s_rc [] = "\0338"; +static char xterm_256color_s_vpa[] = "\033[%i%p1%dd"; +static char xterm_256color_s_sc [] = "\0337"; +static char xterm_256color_s_ind[] = "\012"; +static char xterm_256color_s_ri [] = "\033M"; +static char xterm_256color_s_sgr[] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m"; +static char xterm_256color_s_hts[] = "\033H"; +static char xterm_256color_s_ht [] = "\011"; +static char xterm_256color_s_kb2[] = "\033OE"; +static char xterm_256color_s_acsc[] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char xterm_256color_s_kcbt[] = "\033[Z"; +static char xterm_256color_s_smam[] = "\033[?7h"; +static char xterm_256color_s_rmam[] = "\033[?7l"; +static char xterm_256color_s_kend[] = "\033OF"; +static char xterm_256color_s_kent[] = "\033OM"; +static char xterm_256color_s_kDC[] = "\033[3;2~"; +static char xterm_256color_s_kEND[] = "\033[1;2F"; +static char xterm_256color_s_kHOM[] = "\033[1;2H"; +static char xterm_256color_s_kIC[] = "\033[2;2~"; +static char xterm_256color_s_kLFT[] = "\033[1;2D"; +static char xterm_256color_s_kNXT[] = "\033[6;2~"; +static char xterm_256color_s_kPRV[] = "\033[5;2~"; +static char xterm_256color_s_kRIT[] = "\033[1;2C"; +static char xterm_256color_s_kf11[] = "\033[23~"; +static char xterm_256color_s_kf12[] = "\033[24~"; +static char xterm_256color_s_kf13[] = "\033[1;2P"; +static char xterm_256color_s_kf14[] = "\033[1;2Q"; +static char xterm_256color_s_kf15[] = "\033[1;2R"; +static char xterm_256color_s_kf16[] = "\033[1;2S"; +static char xterm_256color_s_kf17[] = "\033[15;2~"; +static char xterm_256color_s_kf18[] = "\033[17;2~"; +static char xterm_256color_s_kf19[] = "\033[18;2~"; +static char xterm_256color_s_kf20[] = "\033[19;2~"; +static char xterm_256color_s_kf21[] = "\033[20;2~"; +static char xterm_256color_s_kf22[] = "\033[21;2~"; +static char xterm_256color_s_kf23[] = "\033[23;2~"; +static char xterm_256color_s_kf24[] = "\033[24;2~"; +static char xterm_256color_s_kf25[] = "\033[1;5P"; +static char xterm_256color_s_kf26[] = "\033[1;5Q"; +static char xterm_256color_s_kf27[] = "\033[1;5R"; +static char xterm_256color_s_kf28[] = "\033[1;5S"; +static char xterm_256color_s_kf29[] = "\033[15;5~"; +static char xterm_256color_s_kf30[] = "\033[17;5~"; +static char xterm_256color_s_kf31[] = "\033[18;5~"; +static char xterm_256color_s_kf32[] = "\033[19;5~"; +static char xterm_256color_s_kf33[] = "\033[20;5~"; +static char xterm_256color_s_kf34[] = "\033[21;5~"; +static char xterm_256color_s_kf35[] = "\033[23;5~"; +static char xterm_256color_s_kf36[] = "\033[24;5~"; +static char xterm_256color_s_kf37[] = "\033[1;6P"; +static char xterm_256color_s_kf38[] = "\033[1;6Q"; +static char xterm_256color_s_kf39[] = "\033[1;6R"; +static char xterm_256color_s_kf40[] = "\033[1;6S"; +static char xterm_256color_s_kf41[] = "\033[15;6~"; +static char xterm_256color_s_kf42[] = "\033[17;6~"; +static char xterm_256color_s_kf43[] = "\033[18;6~"; +static char xterm_256color_s_kf44[] = "\033[19;6~"; +static char xterm_256color_s_kf45[] = "\033[20;6~"; +static char xterm_256color_s_kf46[] = "\033[21;6~"; +static char xterm_256color_s_kf47[] = "\033[23;6~"; +static char xterm_256color_s_kf48[] = "\033[24;6~"; +static char xterm_256color_s_kf49[] = "\033[1;3P"; +static char xterm_256color_s_kf50[] = "\033[1;3Q"; +static char xterm_256color_s_kf51[] = "\033[1;3R"; +static char xterm_256color_s_kf52[] = "\033[1;3S"; +static char xterm_256color_s_kf53[] = "\033[15;3~"; +static char xterm_256color_s_kf54[] = "\033[17;3~"; +static char xterm_256color_s_kf55[] = "\033[18;3~"; +static char xterm_256color_s_kf56[] = "\033[19;3~"; +static char xterm_256color_s_kf57[] = "\033[20;3~"; +static char xterm_256color_s_kf58[] = "\033[21;3~"; +static char xterm_256color_s_kf59[] = "\033[23;3~"; +static char xterm_256color_s_kf60[] = "\033[24;3~"; +static char xterm_256color_s_kf61[] = "\033[1;4P"; +static char xterm_256color_s_kf62[] = "\033[1;4Q"; +static char xterm_256color_s_kf63[] = "\033[1;4R"; +static char xterm_256color_s_el1[] = "\033[1K"; +static char xterm_256color_s_u6 [] = "\033[%i%d;%dR"; +static char xterm_256color_s_u7 [] = "\033[6n"; +static char xterm_256color_s_u8 [] = "\033[?%[;0123456789]c"; +static char xterm_256color_s_u9 [] = "\033[c"; +static char xterm_256color_s_op [] = "\033[39;49m"; +static char xterm_256color_s_oc [] = "\033]104\007"; +static char xterm_256color_s_initc[] = "\033]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\033\134"; +static char xterm_256color_s_sitm[] = "\033[3m"; +static char xterm_256color_s_ritm[] = "\033[23m"; +static char xterm_256color_s_kmous[] = "\033[<"; +static char xterm_256color_s_setaf[] = "\033[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"; +static char xterm_256color_s_setab[] = "\033[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"; +static char xterm_256color_s_meml[] = "\033l"; +static char xterm_256color_s_memu[] = "\033m"; + +static char xterm_256color_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ TRUE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 xterm_256color_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 256, + /* 14: pairs */ 32767, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * xterm_256color_string_data[] = { + /* 0: cbt */ xterm_256color_s_cbt, + /* 1: bel */ xterm_256color_s_bel, + /* 2: cr */ xterm_256color_s_cr, + /* 3: csr */ xterm_256color_s_csr, + /* 4: tbc */ xterm_256color_s_tbc, + /* 5: clear */ xterm_256color_s_clear, + /* 6: el */ xterm_256color_s_el, + /* 7: ed */ xterm_256color_s_ed, + /* 8: hpa */ xterm_256color_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ xterm_256color_s_cup, + /* 11: cud1 */ xterm_256color_s_cud1, + /* 12: home */ xterm_256color_s_home, + /* 13: civis */ xterm_256color_s_civis, + /* 14: cub1 */ xterm_256color_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ xterm_256color_s_cnorm, + /* 17: cuf1 */ xterm_256color_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ xterm_256color_s_cuu1, + /* 20: cvvis */ xterm_256color_s_cvvis, + /* 21: dch1 */ xterm_256color_s_dch1, + /* 22: dl1 */ xterm_256color_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ xterm_256color_s_smacs, + /* 26: blink */ xterm_256color_s_blink, + /* 27: bold */ xterm_256color_s_bold, + /* 28: smcup */ xterm_256color_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ xterm_256color_s_dim, + /* 31: smir */ xterm_256color_s_smir, + /* 32: invis */ xterm_256color_s_invis, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ xterm_256color_s_rev, + /* 35: smso */ xterm_256color_s_smso, + /* 36: smul */ xterm_256color_s_smul, + /* 37: ech */ xterm_256color_s_ech, + /* 38: rmacs */ xterm_256color_s_rmacs, + /* 39: sgr0 */ xterm_256color_s_sgr0, + /* 40: rmcup */ xterm_256color_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ xterm_256color_s_rmir, + /* 43: rmso */ xterm_256color_s_rmso, + /* 44: rmul */ xterm_256color_s_rmul, + /* 45: flash */ xterm_256color_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ xterm_256color_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ xterm_256color_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ xterm_256color_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ xterm_256color_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ xterm_256color_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ xterm_256color_s_kf1, + /* 67: kf10 */ xterm_256color_s_kf10, + /* 68: kf2 */ xterm_256color_s_kf2, + /* 69: kf3 */ xterm_256color_s_kf3, + /* 70: kf4 */ xterm_256color_s_kf4, + /* 71: kf5 */ xterm_256color_s_kf5, + /* 72: kf6 */ xterm_256color_s_kf6, + /* 73: kf7 */ xterm_256color_s_kf7, + /* 74: kf8 */ xterm_256color_s_kf8, + /* 75: kf9 */ xterm_256color_s_kf9, + /* 76: khome */ xterm_256color_s_khome, + /* 77: kich1 */ xterm_256color_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ xterm_256color_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ xterm_256color_s_knp, + /* 82: kpp */ xterm_256color_s_kpp, + /* 83: kcuf1 */ xterm_256color_s_kcuf1, + /* 84: kind */ xterm_256color_s_kind, + /* 85: kri */ xterm_256color_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ xterm_256color_s_kcuu1, + /* 88: rmkx */ xterm_256color_s_rmkx, + /* 89: smkx */ xterm_256color_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ xterm_256color_s_rmm, + /* 102: smm */ xterm_256color_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ xterm_256color_s_dch, + /* 106: dl */ xterm_256color_s_dl, + /* 107: cud */ xterm_256color_s_cud, + /* 108: ich */ xterm_256color_s_ich, + /* 109: indn */ xterm_256color_s_indn, + /* 110: il */ xterm_256color_s_il, + /* 111: cub */ xterm_256color_s_cub, + /* 112: cuf */ xterm_256color_s_cuf, + /* 113: rin */ xterm_256color_s_rin, + /* 114: cuu */ xterm_256color_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ xterm_256color_s_mc0, + /* 119: mc4 */ xterm_256color_s_mc4, + /* 120: mc5 */ xterm_256color_s_mc5, + /* 121: rep */ xterm_256color_s_rep, + /* 122: rs1 */ xterm_256color_s_rs1, + /* 123: rs2 */ xterm_256color_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ xterm_256color_s_rc, + /* 127: vpa */ xterm_256color_s_vpa, + /* 128: sc */ xterm_256color_s_sc, + /* 129: ind */ xterm_256color_s_ind, + /* 130: ri */ xterm_256color_s_ri, + /* 131: sgr */ xterm_256color_s_sgr, + /* 132: hts */ xterm_256color_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ xterm_256color_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ xterm_256color_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ xterm_256color_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ xterm_256color_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ xterm_256color_s_smam, + /* 152: rmam */ xterm_256color_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ xterm_256color_s_kend, + /* 165: kent */ xterm_256color_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ xterm_256color_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ xterm_256color_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ xterm_256color_s_kHOM, + /* 200: kIC */ xterm_256color_s_kIC, + /* 201: kLFT */ xterm_256color_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ xterm_256color_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ xterm_256color_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ xterm_256color_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ xterm_256color_s_kf11, + /* 217: kf12 */ xterm_256color_s_kf12, + /* 218: kf13 */ xterm_256color_s_kf13, + /* 219: kf14 */ xterm_256color_s_kf14, + /* 220: kf15 */ xterm_256color_s_kf15, + /* 221: kf16 */ xterm_256color_s_kf16, + /* 222: kf17 */ xterm_256color_s_kf17, + /* 223: kf18 */ xterm_256color_s_kf18, + /* 224: kf19 */ xterm_256color_s_kf19, + /* 225: kf20 */ xterm_256color_s_kf20, + /* 226: kf21 */ xterm_256color_s_kf21, + /* 227: kf22 */ xterm_256color_s_kf22, + /* 228: kf23 */ xterm_256color_s_kf23, + /* 229: kf24 */ xterm_256color_s_kf24, + /* 230: kf25 */ xterm_256color_s_kf25, + /* 231: kf26 */ xterm_256color_s_kf26, + /* 232: kf27 */ xterm_256color_s_kf27, + /* 233: kf28 */ xterm_256color_s_kf28, + /* 234: kf29 */ xterm_256color_s_kf29, + /* 235: kf30 */ xterm_256color_s_kf30, + /* 236: kf31 */ xterm_256color_s_kf31, + /* 237: kf32 */ xterm_256color_s_kf32, + /* 238: kf33 */ xterm_256color_s_kf33, + /* 239: kf34 */ xterm_256color_s_kf34, + /* 240: kf35 */ xterm_256color_s_kf35, + /* 241: kf36 */ xterm_256color_s_kf36, + /* 242: kf37 */ xterm_256color_s_kf37, + /* 243: kf38 */ xterm_256color_s_kf38, + /* 244: kf39 */ xterm_256color_s_kf39, + /* 245: kf40 */ xterm_256color_s_kf40, + /* 246: kf41 */ xterm_256color_s_kf41, + /* 247: kf42 */ xterm_256color_s_kf42, + /* 248: kf43 */ xterm_256color_s_kf43, + /* 249: kf44 */ xterm_256color_s_kf44, + /* 250: kf45 */ xterm_256color_s_kf45, + /* 251: kf46 */ xterm_256color_s_kf46, + /* 252: kf47 */ xterm_256color_s_kf47, + /* 253: kf48 */ xterm_256color_s_kf48, + /* 254: kf49 */ xterm_256color_s_kf49, + /* 255: kf50 */ xterm_256color_s_kf50, + /* 256: kf51 */ xterm_256color_s_kf51, + /* 257: kf52 */ xterm_256color_s_kf52, + /* 258: kf53 */ xterm_256color_s_kf53, + /* 259: kf54 */ xterm_256color_s_kf54, + /* 260: kf55 */ xterm_256color_s_kf55, + /* 261: kf56 */ xterm_256color_s_kf56, + /* 262: kf57 */ xterm_256color_s_kf57, + /* 263: kf58 */ xterm_256color_s_kf58, + /* 264: kf59 */ xterm_256color_s_kf59, + /* 265: kf60 */ xterm_256color_s_kf60, + /* 266: kf61 */ xterm_256color_s_kf61, + /* 267: kf62 */ xterm_256color_s_kf62, + /* 268: kf63 */ xterm_256color_s_kf63, + /* 269: el1 */ xterm_256color_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ xterm_256color_s_u6, + /* 294: u7 */ xterm_256color_s_u7, + /* 295: u8 */ xterm_256color_s_u8, + /* 296: u9 */ xterm_256color_s_u9, + /* 297: op */ xterm_256color_s_op, + /* 298: oc */ xterm_256color_s_oc, + /* 299: initc */ xterm_256color_s_initc, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ xterm_256color_s_sitm, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ xterm_256color_s_ritm, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ xterm_256color_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ xterm_256color_s_setaf, + /* 360: setab */ xterm_256color_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ xterm_256color_s_meml, + /* 412: memu */ xterm_256color_s_memu, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen */ + +static char screen_alias_data[] = "screen|VT 100/ANSI X3.64 virtual terminal"; + +static char screen_s_cbt [] = "\033[Z"; +static char screen_s_bel [] = "\007"; +static char screen_s_cr [] = "\015"; +static char screen_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char screen_s_tbc [] = "\033[3g"; +static char screen_s_clear [] = "\033[H\033[J"; +static char screen_s_el [] = "\033[K"; +static char screen_s_ed [] = "\033[J"; +static char screen_s_hpa [] = "\033[%i%p1%dG"; +static char screen_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char screen_s_cud1 [] = "\012"; +static char screen_s_home [] = "\033[H"; +static char screen_s_civis [] = "\033[?25l"; +static char screen_s_cub1 [] = "\010"; +static char screen_s_cnorm [] = "\033[34h\033[?25h"; +static char screen_s_cuf1 [] = "\033[C"; +static char screen_s_cuu1 [] = "\033M"; +static char screen_s_cvvis [] = "\033[34l"; +static char screen_s_dch1 [] = "\033[P"; +static char screen_s_dl1 [] = "\033[M"; +static char screen_s_smacs [] = "\016"; +static char screen_s_blink [] = "\033[5m"; +static char screen_s_bold [] = "\033[1m"; +static char screen_s_smcup [] = "\033[?1049h"; +static char screen_s_dim [] = "\033[2m"; +static char screen_s_smir [] = "\033[4h"; +static char screen_s_rev [] = "\033[7m"; +static char screen_s_smso [] = "\033[3m"; +static char screen_s_smul [] = "\033[4m"; +static char screen_s_rmacs [] = "\017"; +static char screen_s_sgr0 [] = "\033[m\017"; +static char screen_s_rmcup [] = "\033[?1049l"; +static char screen_s_rmir [] = "\033[4l"; +static char screen_s_rmso [] = "\033[23m"; +static char screen_s_rmul [] = "\033[24m"; +static char screen_s_flash [] = "\033g"; +static char screen_s_is2 [] = "\033)0"; +static char screen_s_il1 [] = "\033[L"; +static char screen_s_kbs [] = "\010"; +static char screen_s_kdch1 [] = "\033[3~"; +static char screen_s_kcud1 [] = "\033OB"; +static char screen_s_kf1 [] = "\033OP"; +static char screen_s_kf10 [] = "\033[21~"; +static char screen_s_kf2 [] = "\033OQ"; +static char screen_s_kf3 [] = "\033OR"; +static char screen_s_kf4 [] = "\033OS"; +static char screen_s_kf5 [] = "\033[15~"; +static char screen_s_kf6 [] = "\033[17~"; +static char screen_s_kf7 [] = "\033[18~"; +static char screen_s_kf8 [] = "\033[19~"; +static char screen_s_kf9 [] = "\033[20~"; +static char screen_s_khome [] = "\033[1~"; +static char screen_s_kich1 [] = "\033[2~"; +static char screen_s_kcub1 [] = "\033OD"; +static char screen_s_knp [] = "\033[6~"; +static char screen_s_kpp [] = "\033[5~"; +static char screen_s_kcuf1 [] = "\033OC"; +static char screen_s_kcuu1 [] = "\033OA"; +static char screen_s_rmkx [] = "\033[?1l\033>"; +static char screen_s_smkx [] = "\033[?1h\033="; +static char screen_s_nel [] = "\033E"; +static char screen_s_dch [] = "\033[%p1%dP"; +static char screen_s_dl [] = "\033[%p1%dM"; +static char screen_s_cud [] = "\033[%p1%dB"; +static char screen_s_ich [] = "\033[%p1%d@"; +static char screen_s_indn [] = "\033[%p1%dS"; +static char screen_s_il [] = "\033[%p1%dL"; +static char screen_s_cub [] = "\033[%p1%dD"; +static char screen_s_cuf [] = "\033[%p1%dC"; +static char screen_s_cuu [] = "\033[%p1%dA"; +static char screen_s_rs2 [] = "\033c\033[?1000l\033[?25h"; +static char screen_s_rc [] = "\0338"; +static char screen_s_vpa [] = "\033[%i%p1%dd"; +static char screen_s_sc [] = "\0337"; +static char screen_s_ind [] = "\012"; +static char screen_s_ri [] = "\033M"; +static char screen_s_sgr [] = "\033[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;"; +static char screen_s_hts [] = "\033H"; +static char screen_s_ht [] = "\011"; +static char screen_s_acsc [] = "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_s_kcbt [] = "\033[Z"; +static char screen_s_enacs [] = "\033(B\033)0"; +static char screen_s_kend [] = "\033[4~"; +static char screen_s_kf11 [] = "\033[23~"; +static char screen_s_kf12 [] = "\033[24~"; +static char screen_s_el1 [] = "\033[1K"; +static char screen_s_op [] = "\033[39;49m"; +static char screen_s_kmous [] = "\033[M"; +static char screen_s_setaf [] = "\033[3%p1%dm"; +static char screen_s_setab [] = "\033[4%p1%dm"; + +static char screen_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ TRUE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ CANCELLED_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_string_data[] = { + /* 0: cbt */ screen_s_cbt, + /* 1: bel */ screen_s_bel, + /* 2: cr */ screen_s_cr, + /* 3: csr */ screen_s_csr, + /* 4: tbc */ screen_s_tbc, + /* 5: clear */ screen_s_clear, + /* 6: el */ screen_s_el, + /* 7: ed */ screen_s_ed, + /* 8: hpa */ screen_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_s_cup, + /* 11: cud1 */ screen_s_cud1, + /* 12: home */ screen_s_home, + /* 13: civis */ screen_s_civis, + /* 14: cub1 */ screen_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_s_cnorm, + /* 17: cuf1 */ screen_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_s_cuu1, + /* 20: cvvis */ screen_s_cvvis, + /* 21: dch1 */ screen_s_dch1, + /* 22: dl1 */ screen_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_s_smacs, + /* 26: blink */ screen_s_blink, + /* 27: bold */ screen_s_bold, + /* 28: smcup */ screen_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_s_dim, + /* 31: smir */ screen_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_s_rev, + /* 35: smso */ screen_s_smso, + /* 36: smul */ screen_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ screen_s_rmacs, + /* 39: sgr0 */ screen_s_sgr0, + /* 40: rmcup */ screen_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_s_rmir, + /* 43: rmso */ screen_s_rmso, + /* 44: rmul */ screen_s_rmul, + /* 45: flash */ screen_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_s_kf1, + /* 67: kf10 */ screen_s_kf10, + /* 68: kf2 */ screen_s_kf2, + /* 69: kf3 */ screen_s_kf3, + /* 70: kf4 */ screen_s_kf4, + /* 71: kf5 */ screen_s_kf5, + /* 72: kf6 */ screen_s_kf6, + /* 73: kf7 */ screen_s_kf7, + /* 74: kf8 */ screen_s_kf8, + /* 75: kf9 */ screen_s_kf9, + /* 76: khome */ screen_s_khome, + /* 77: kich1 */ screen_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_s_knp, + /* 82: kpp */ screen_s_kpp, + /* 83: kcuf1 */ screen_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_s_kcuu1, + /* 88: rmkx */ screen_s_rmkx, + /* 89: smkx */ screen_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ screen_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_s_dch, + /* 106: dl */ screen_s_dl, + /* 107: cud */ screen_s_cud, + /* 108: ich */ screen_s_ich, + /* 109: indn */ screen_s_indn, + /* 110: il */ screen_s_il, + /* 111: cub */ screen_s_cub, + /* 112: cuf */ screen_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ screen_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ screen_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_s_rc, + /* 127: vpa */ screen_s_vpa, + /* 128: sc */ screen_s_sc, + /* 129: ind */ screen_s_ind, + /* 130: ri */ screen_s_ri, + /* 131: sgr */ screen_s_sgr, + /* 132: hts */ screen_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ ABSENT_STRING, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ screen_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_s_kend, + /* 165: kent */ ABSENT_STRING, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_s_kf11, + /* 217: kf12 */ screen_s_kf12, + /* 218: kf13 */ ABSENT_STRING, + /* 219: kf14 */ ABSENT_STRING, + /* 220: kf15 */ ABSENT_STRING, + /* 221: kf16 */ ABSENT_STRING, + /* 222: kf17 */ ABSENT_STRING, + /* 223: kf18 */ ABSENT_STRING, + /* 224: kf19 */ ABSENT_STRING, + /* 225: kf20 */ ABSENT_STRING, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ screen_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ ABSENT_STRING, + /* 294: u7 */ ABSENT_STRING, + /* 295: u8 */ ABSENT_STRING, + /* 296: u9 */ ABSENT_STRING, + /* 297: op */ screen_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_s_setaf, + /* 360: setab */ screen_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.linux */ + +static char screen_linux_alias_data[] = "screen.linux|screen in linux console"; + +static char screen_linux_s_cbt [] = "\033[Z"; +static char screen_linux_s_bel [] = "\007"; +static char screen_linux_s_cr [] = "\015"; +static char screen_linux_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char screen_linux_s_tbc [] = "\033[3g"; +static char screen_linux_s_clear[] = "\033[H\033[J"; +static char screen_linux_s_el [] = "\033[K"; +static char screen_linux_s_ed [] = "\033[J"; +static char screen_linux_s_hpa [] = "\033[%i%p1%dG"; +static char screen_linux_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char screen_linux_s_cud1 [] = "\012"; +static char screen_linux_s_home [] = "\033[H"; +static char screen_linux_s_civis[] = "\033[?25l"; +static char screen_linux_s_cub1 [] = "\010"; +static char screen_linux_s_cnorm[] = "\033[34h\033[?25h"; +static char screen_linux_s_cuf1 [] = "\033[C"; +static char screen_linux_s_cuu1 [] = "\033M"; +static char screen_linux_s_cvvis[] = "\033[34l"; +static char screen_linux_s_dch1 [] = "\033[P"; +static char screen_linux_s_dl1 [] = "\033[M"; +static char screen_linux_s_smacs[] = "\016"; +static char screen_linux_s_blink[] = "\033[5m"; +static char screen_linux_s_bold [] = "\033[1m"; +static char screen_linux_s_smcup[] = "\033[?1049h"; +static char screen_linux_s_dim [] = "\033[2m"; +static char screen_linux_s_smir [] = "\033[4h"; +static char screen_linux_s_rev [] = "\033[7m"; +static char screen_linux_s_smso [] = "\033[3m"; +static char screen_linux_s_smul [] = "\033[4m"; +static char screen_linux_s_rmacs[] = "\017"; +static char screen_linux_s_sgr0 [] = "\033[m\017"; +static char screen_linux_s_rmcup[] = "\033[?1049l"; +static char screen_linux_s_rmir [] = "\033[4l"; +static char screen_linux_s_rmso [] = "\033[23m"; +static char screen_linux_s_rmul [] = "\033[24m"; +static char screen_linux_s_flash[] = "\033g"; +static char screen_linux_s_is2 [] = "\033)0"; +static char screen_linux_s_il1 [] = "\033[L"; +static char screen_linux_s_kbs [] = "\177"; +static char screen_linux_s_kdch1[] = "\033[3~"; +static char screen_linux_s_kcud1[] = "\033OB"; +static char screen_linux_s_kf1 [] = "\033OP"; +static char screen_linux_s_kf10 [] = "\033[21~"; +static char screen_linux_s_kf2 [] = "\033OQ"; +static char screen_linux_s_kf3 [] = "\033OR"; +static char screen_linux_s_kf4 [] = "\033OS"; +static char screen_linux_s_kf5 [] = "\033[15~"; +static char screen_linux_s_kf6 [] = "\033[17~"; +static char screen_linux_s_kf7 [] = "\033[18~"; +static char screen_linux_s_kf8 [] = "\033[19~"; +static char screen_linux_s_kf9 [] = "\033[20~"; +static char screen_linux_s_khome[] = "\033[1~"; +static char screen_linux_s_kich1[] = "\033[2~"; +static char screen_linux_s_kcub1[] = "\033OD"; +static char screen_linux_s_knp [] = "\033[6~"; +static char screen_linux_s_kpp [] = "\033[5~"; +static char screen_linux_s_kcuf1[] = "\033OC"; +static char screen_linux_s_kcuu1[] = "\033OA"; +static char screen_linux_s_rmkx [] = "\033[?1l\033>"; +static char screen_linux_s_smkx [] = "\033[?1h\033="; +static char screen_linux_s_nel [] = "\033E"; +static char screen_linux_s_dch [] = "\033[%p1%dP"; +static char screen_linux_s_dl [] = "\033[%p1%dM"; +static char screen_linux_s_cud [] = "\033[%p1%dB"; +static char screen_linux_s_ich [] = "\033[%p1%d@"; +static char screen_linux_s_indn [] = "\033[%p1%dS"; +static char screen_linux_s_il [] = "\033[%p1%dL"; +static char screen_linux_s_cub [] = "\033[%p1%dD"; +static char screen_linux_s_cuf [] = "\033[%p1%dC"; +static char screen_linux_s_cuu [] = "\033[%p1%dA"; +static char screen_linux_s_rs2 [] = "\033c\033[?1000l\033[?25h"; +static char screen_linux_s_rc [] = "\0338"; +static char screen_linux_s_vpa [] = "\033[%i%p1%dd"; +static char screen_linux_s_sc [] = "\0337"; +static char screen_linux_s_ind [] = "\012"; +static char screen_linux_s_ri [] = "\033M"; +static char screen_linux_s_sgr [] = "\033[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;"; +static char screen_linux_s_hts [] = "\033H"; +static char screen_linux_s_ht [] = "\011"; +static char screen_linux_s_acsc [] = "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_linux_s_enacs[] = "\033(B\033)0"; +static char screen_linux_s_kend [] = "\033[4~"; +static char screen_linux_s_kf11 [] = "\033[23~"; +static char screen_linux_s_kf12 [] = "\033[24~"; +static char screen_linux_s_el1 [] = "\033[1K"; +static char screen_linux_s_op [] = "\033[39;49m"; +static char screen_linux_s_kmous[] = "\033[M"; +static char screen_linux_s_setaf[] = "\033[3%p1%dm"; +static char screen_linux_s_setab[] = "\033[4%p1%dm"; + +static char screen_linux_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ TRUE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_linux_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_linux_string_data[] = { + /* 0: cbt */ screen_linux_s_cbt, + /* 1: bel */ screen_linux_s_bel, + /* 2: cr */ screen_linux_s_cr, + /* 3: csr */ screen_linux_s_csr, + /* 4: tbc */ screen_linux_s_tbc, + /* 5: clear */ screen_linux_s_clear, + /* 6: el */ screen_linux_s_el, + /* 7: ed */ screen_linux_s_ed, + /* 8: hpa */ screen_linux_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_linux_s_cup, + /* 11: cud1 */ screen_linux_s_cud1, + /* 12: home */ screen_linux_s_home, + /* 13: civis */ screen_linux_s_civis, + /* 14: cub1 */ screen_linux_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_linux_s_cnorm, + /* 17: cuf1 */ screen_linux_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_linux_s_cuu1, + /* 20: cvvis */ screen_linux_s_cvvis, + /* 21: dch1 */ screen_linux_s_dch1, + /* 22: dl1 */ screen_linux_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_linux_s_smacs, + /* 26: blink */ screen_linux_s_blink, + /* 27: bold */ screen_linux_s_bold, + /* 28: smcup */ screen_linux_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_linux_s_dim, + /* 31: smir */ screen_linux_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_linux_s_rev, + /* 35: smso */ screen_linux_s_smso, + /* 36: smul */ screen_linux_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ screen_linux_s_rmacs, + /* 39: sgr0 */ screen_linux_s_sgr0, + /* 40: rmcup */ screen_linux_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_linux_s_rmir, + /* 43: rmso */ screen_linux_s_rmso, + /* 44: rmul */ screen_linux_s_rmul, + /* 45: flash */ screen_linux_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_linux_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_linux_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_linux_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_linux_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_linux_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_linux_s_kf1, + /* 67: kf10 */ screen_linux_s_kf10, + /* 68: kf2 */ screen_linux_s_kf2, + /* 69: kf3 */ screen_linux_s_kf3, + /* 70: kf4 */ screen_linux_s_kf4, + /* 71: kf5 */ screen_linux_s_kf5, + /* 72: kf6 */ screen_linux_s_kf6, + /* 73: kf7 */ screen_linux_s_kf7, + /* 74: kf8 */ screen_linux_s_kf8, + /* 75: kf9 */ screen_linux_s_kf9, + /* 76: khome */ screen_linux_s_khome, + /* 77: kich1 */ screen_linux_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_linux_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_linux_s_knp, + /* 82: kpp */ screen_linux_s_kpp, + /* 83: kcuf1 */ screen_linux_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_linux_s_kcuu1, + /* 88: rmkx */ screen_linux_s_rmkx, + /* 89: smkx */ screen_linux_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ screen_linux_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_linux_s_dch, + /* 106: dl */ screen_linux_s_dl, + /* 107: cud */ screen_linux_s_cud, + /* 108: ich */ screen_linux_s_ich, + /* 109: indn */ screen_linux_s_indn, + /* 110: il */ screen_linux_s_il, + /* 111: cub */ screen_linux_s_cub, + /* 112: cuf */ screen_linux_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ screen_linux_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ screen_linux_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_linux_s_rc, + /* 127: vpa */ screen_linux_s_vpa, + /* 128: sc */ screen_linux_s_sc, + /* 129: ind */ screen_linux_s_ind, + /* 130: ri */ screen_linux_s_ri, + /* 131: sgr */ screen_linux_s_sgr, + /* 132: hts */ screen_linux_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_linux_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ ABSENT_STRING, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_linux_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ CANCELLED_STRING, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ screen_linux_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_linux_s_kend, + /* 165: kent */ ABSENT_STRING, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_linux_s_kf11, + /* 217: kf12 */ screen_linux_s_kf12, + /* 218: kf13 */ ABSENT_STRING, + /* 219: kf14 */ ABSENT_STRING, + /* 220: kf15 */ ABSENT_STRING, + /* 221: kf16 */ ABSENT_STRING, + /* 222: kf17 */ ABSENT_STRING, + /* 223: kf18 */ ABSENT_STRING, + /* 224: kf19 */ ABSENT_STRING, + /* 225: kf20 */ ABSENT_STRING, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ screen_linux_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ ABSENT_STRING, + /* 294: u7 */ ABSENT_STRING, + /* 295: u8 */ ABSENT_STRING, + /* 296: u9 */ ABSENT_STRING, + /* 297: op */ screen_linux_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_linux_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_linux_s_setaf, + /* 360: setab */ screen_linux_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.rxvt */ + +static char screen_rxvt_alias_data[] = "screen.rxvt|screen in rxvt"; + +static char screen_rxvt_s_cbt [] = "\033[Z"; +static char screen_rxvt_s_bel [] = "\007"; +static char screen_rxvt_s_cr [] = "\015"; +static char screen_rxvt_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char screen_rxvt_s_tbc [] = "\033[3g"; +static char screen_rxvt_s_clear [] = "\033[H\033[J"; +static char screen_rxvt_s_el [] = "\033[K"; +static char screen_rxvt_s_ed [] = "\033[J"; +static char screen_rxvt_s_hpa [] = "\033[%i%p1%dG"; +static char screen_rxvt_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char screen_rxvt_s_cud1 [] = "\012"; +static char screen_rxvt_s_home [] = "\033[H"; +static char screen_rxvt_s_civis [] = "\033[?25l"; +static char screen_rxvt_s_cub1 [] = "\010"; +static char screen_rxvt_s_cnorm [] = "\033[34h\033[?25h"; +static char screen_rxvt_s_cuf1 [] = "\033[C"; +static char screen_rxvt_s_cuu1 [] = "\033M"; +static char screen_rxvt_s_dch1 [] = "\033[P"; +static char screen_rxvt_s_dl1 [] = "\033[M"; +static char screen_rxvt_s_smacs [] = "\016"; +static char screen_rxvt_s_blink [] = "\033[5m"; +static char screen_rxvt_s_bold [] = "\033[1m"; +static char screen_rxvt_s_smcup [] = "\033[?1049h"; +static char screen_rxvt_s_dim [] = "\033[2m"; +static char screen_rxvt_s_smir [] = "\033[4h"; +static char screen_rxvt_s_rev [] = "\033[7m"; +static char screen_rxvt_s_smso [] = "\033[3m"; +static char screen_rxvt_s_smul [] = "\033[4m"; +static char screen_rxvt_s_rmacs [] = "\017"; +static char screen_rxvt_s_sgr0 [] = "\033[m\017"; +static char screen_rxvt_s_rmcup [] = "\033[?1049l"; +static char screen_rxvt_s_rmir [] = "\033[4l"; +static char screen_rxvt_s_rmso [] = "\033[23m"; +static char screen_rxvt_s_rmul [] = "\033[24m"; +static char screen_rxvt_s_is2 [] = "\033)0"; +static char screen_rxvt_s_il1 [] = "\033[L"; +static char screen_rxvt_s_kbs [] = "\010"; +static char screen_rxvt_s_kdch1 [] = "\033[3~"; +static char screen_rxvt_s_kcud1 [] = "\033OB"; +static char screen_rxvt_s_kel [] = "\033[8^"; +static char screen_rxvt_s_kf1 [] = "\033OP"; +static char screen_rxvt_s_kf10 [] = "\033[21~"; +static char screen_rxvt_s_kf2 [] = "\033OQ"; +static char screen_rxvt_s_kf3 [] = "\033OR"; +static char screen_rxvt_s_kf4 [] = "\033OS"; +static char screen_rxvt_s_kf5 [] = "\033[15~"; +static char screen_rxvt_s_kf6 [] = "\033[17~"; +static char screen_rxvt_s_kf7 [] = "\033[18~"; +static char screen_rxvt_s_kf8 [] = "\033[19~"; +static char screen_rxvt_s_kf9 [] = "\033[20~"; +static char screen_rxvt_s_khome [] = "\033[1~"; +static char screen_rxvt_s_kich1 [] = "\033[2~"; +static char screen_rxvt_s_kcub1 [] = "\033OD"; +static char screen_rxvt_s_knp [] = "\033[6~"; +static char screen_rxvt_s_kpp [] = "\033[5~"; +static char screen_rxvt_s_kcuf1 [] = "\033OC"; +static char screen_rxvt_s_kind [] = "\033[a"; +static char screen_rxvt_s_kri [] = "\033[b"; +static char screen_rxvt_s_kcuu1 [] = "\033OA"; +static char screen_rxvt_s_rmkx [] = "\033[?1l\033>"; +static char screen_rxvt_s_smkx [] = "\033[?1h\033="; +static char screen_rxvt_s_nel [] = "\033E"; +static char screen_rxvt_s_dch [] = "\033[%p1%dP"; +static char screen_rxvt_s_dl [] = "\033[%p1%dM"; +static char screen_rxvt_s_cud [] = "\033[%p1%dB"; +static char screen_rxvt_s_ich [] = "\033[%p1%d@"; +static char screen_rxvt_s_indn [] = "\033[%p1%dS"; +static char screen_rxvt_s_il [] = "\033[%p1%dL"; +static char screen_rxvt_s_cub [] = "\033[%p1%dD"; +static char screen_rxvt_s_cuf [] = "\033[%p1%dC"; +static char screen_rxvt_s_cuu [] = "\033[%p1%dA"; +static char screen_rxvt_s_rs2 [] = "\033c\033[?1000l\033[?25h"; +static char screen_rxvt_s_rc [] = "\0338"; +static char screen_rxvt_s_vpa [] = "\033[%i%p1%dd"; +static char screen_rxvt_s_sc [] = "\0337"; +static char screen_rxvt_s_ind [] = "\012"; +static char screen_rxvt_s_ri [] = "\033M"; +static char screen_rxvt_s_sgr [] = "\033[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;"; +static char screen_rxvt_s_hts [] = "\033H"; +static char screen_rxvt_s_ht [] = "\011"; +static char screen_rxvt_s_ka1 [] = "\033Ow"; +static char screen_rxvt_s_ka3 [] = "\033Oy"; +static char screen_rxvt_s_kb2 [] = "\033Ou"; +static char screen_rxvt_s_kc1 [] = "\033Oq"; +static char screen_rxvt_s_kc3 [] = "\033Os"; +static char screen_rxvt_s_acsc [] = "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_rxvt_s_kcbt [] = "\033[Z"; +static char screen_rxvt_s_enacs [] = "\033(B\033)0"; +static char screen_rxvt_s_kend [] = "\033[4~"; +static char screen_rxvt_s_kent [] = "\033OM"; +static char screen_rxvt_s_kDC [] = "\033[3$"; +static char screen_rxvt_s_kEND [] = "\033[8$"; +static char screen_rxvt_s_kHOM [] = "\033[7$"; +static char screen_rxvt_s_kIC [] = "\033[2$"; +static char screen_rxvt_s_kLFT [] = "\033[d"; +static char screen_rxvt_s_kNXT [] = "\033[6$"; +static char screen_rxvt_s_kPRV [] = "\033[5$"; +static char screen_rxvt_s_kRIT [] = "\033[c"; +static char screen_rxvt_s_kf11 [] = "\033[23~"; +static char screen_rxvt_s_kf12 [] = "\033[24~"; +static char screen_rxvt_s_kf13 [] = "\033[25~"; +static char screen_rxvt_s_kf14 [] = "\033[26~"; +static char screen_rxvt_s_kf15 [] = "\033[28~"; +static char screen_rxvt_s_kf16 [] = "\033[29~"; +static char screen_rxvt_s_kf17 [] = "\033[31~"; +static char screen_rxvt_s_kf18 [] = "\033[32~"; +static char screen_rxvt_s_kf19 [] = "\033[33~"; +static char screen_rxvt_s_kf20 [] = "\033[34~"; +static char screen_rxvt_s_kf21 [] = "\033[23$"; +static char screen_rxvt_s_kf22 [] = "\033[24$"; +static char screen_rxvt_s_kf23 [] = "\033[11^"; +static char screen_rxvt_s_kf24 [] = "\033[12^"; +static char screen_rxvt_s_kf25 [] = "\033[13^"; +static char screen_rxvt_s_kf26 [] = "\033[14^"; +static char screen_rxvt_s_kf27 [] = "\033[15^"; +static char screen_rxvt_s_kf28 [] = "\033[17^"; +static char screen_rxvt_s_kf29 [] = "\033[18^"; +static char screen_rxvt_s_kf30 [] = "\033[19^"; +static char screen_rxvt_s_kf31 [] = "\033[20^"; +static char screen_rxvt_s_kf32 [] = "\033[21^"; +static char screen_rxvt_s_kf33 [] = "\033[23^"; +static char screen_rxvt_s_kf34 [] = "\033[24^"; +static char screen_rxvt_s_kf35 [] = "\033[25^"; +static char screen_rxvt_s_kf36 [] = "\033[26^"; +static char screen_rxvt_s_kf37 [] = "\033[28^"; +static char screen_rxvt_s_kf38 [] = "\033[29^"; +static char screen_rxvt_s_kf39 [] = "\033[31^"; +static char screen_rxvt_s_kf40 [] = "\033[32^"; +static char screen_rxvt_s_kf41 [] = "\033[33^"; +static char screen_rxvt_s_kf42 [] = "\033[34^"; +static char screen_rxvt_s_kf43 [] = "\033[23@"; +static char screen_rxvt_s_kf44 [] = "\033[24@"; +static char screen_rxvt_s_el1 [] = "\033[1K"; +static char screen_rxvt_s_u6 [] = "\033[%i%d;%dR"; +static char screen_rxvt_s_u7 [] = "\033[6n"; +static char screen_rxvt_s_u8 [] = "\033[?1;2c"; +static char screen_rxvt_s_u9 [] = "\033[c"; +static char screen_rxvt_s_op [] = "\033[39;49m"; +static char screen_rxvt_s_kmous [] = "\033[M"; +static char screen_rxvt_s_setaf [] = "\033[3%p1%dm"; +static char screen_rxvt_s_setab [] = "\033[4%p1%dm"; + +static char screen_rxvt_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ TRUE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_rxvt_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_rxvt_string_data[] = { + /* 0: cbt */ screen_rxvt_s_cbt, + /* 1: bel */ screen_rxvt_s_bel, + /* 2: cr */ screen_rxvt_s_cr, + /* 3: csr */ screen_rxvt_s_csr, + /* 4: tbc */ screen_rxvt_s_tbc, + /* 5: clear */ screen_rxvt_s_clear, + /* 6: el */ screen_rxvt_s_el, + /* 7: ed */ screen_rxvt_s_ed, + /* 8: hpa */ screen_rxvt_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_rxvt_s_cup, + /* 11: cud1 */ screen_rxvt_s_cud1, + /* 12: home */ screen_rxvt_s_home, + /* 13: civis */ screen_rxvt_s_civis, + /* 14: cub1 */ screen_rxvt_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_rxvt_s_cnorm, + /* 17: cuf1 */ screen_rxvt_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_rxvt_s_cuu1, + /* 20: cvvis */ CANCELLED_STRING, + /* 21: dch1 */ screen_rxvt_s_dch1, + /* 22: dl1 */ screen_rxvt_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_rxvt_s_smacs, + /* 26: blink */ screen_rxvt_s_blink, + /* 27: bold */ screen_rxvt_s_bold, + /* 28: smcup */ screen_rxvt_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_rxvt_s_dim, + /* 31: smir */ screen_rxvt_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_rxvt_s_rev, + /* 35: smso */ screen_rxvt_s_smso, + /* 36: smul */ screen_rxvt_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ screen_rxvt_s_rmacs, + /* 39: sgr0 */ screen_rxvt_s_sgr0, + /* 40: rmcup */ screen_rxvt_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_rxvt_s_rmir, + /* 43: rmso */ screen_rxvt_s_rmso, + /* 44: rmul */ screen_rxvt_s_rmul, + /* 45: flash */ CANCELLED_STRING, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_rxvt_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_rxvt_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_rxvt_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_rxvt_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_rxvt_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ screen_rxvt_s_kel, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_rxvt_s_kf1, + /* 67: kf10 */ screen_rxvt_s_kf10, + /* 68: kf2 */ screen_rxvt_s_kf2, + /* 69: kf3 */ screen_rxvt_s_kf3, + /* 70: kf4 */ screen_rxvt_s_kf4, + /* 71: kf5 */ screen_rxvt_s_kf5, + /* 72: kf6 */ screen_rxvt_s_kf6, + /* 73: kf7 */ screen_rxvt_s_kf7, + /* 74: kf8 */ screen_rxvt_s_kf8, + /* 75: kf9 */ screen_rxvt_s_kf9, + /* 76: khome */ screen_rxvt_s_khome, + /* 77: kich1 */ screen_rxvt_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_rxvt_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_rxvt_s_knp, + /* 82: kpp */ screen_rxvt_s_kpp, + /* 83: kcuf1 */ screen_rxvt_s_kcuf1, + /* 84: kind */ screen_rxvt_s_kind, + /* 85: kri */ screen_rxvt_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_rxvt_s_kcuu1, + /* 88: rmkx */ screen_rxvt_s_rmkx, + /* 89: smkx */ screen_rxvt_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ screen_rxvt_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_rxvt_s_dch, + /* 106: dl */ screen_rxvt_s_dl, + /* 107: cud */ screen_rxvt_s_cud, + /* 108: ich */ screen_rxvt_s_ich, + /* 109: indn */ screen_rxvt_s_indn, + /* 110: il */ screen_rxvt_s_il, + /* 111: cub */ screen_rxvt_s_cub, + /* 112: cuf */ screen_rxvt_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ screen_rxvt_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ screen_rxvt_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_rxvt_s_rc, + /* 127: vpa */ screen_rxvt_s_vpa, + /* 128: sc */ screen_rxvt_s_sc, + /* 129: ind */ screen_rxvt_s_ind, + /* 130: ri */ screen_rxvt_s_ri, + /* 131: sgr */ screen_rxvt_s_sgr, + /* 132: hts */ screen_rxvt_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_rxvt_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ screen_rxvt_s_ka1, + /* 140: ka3 */ screen_rxvt_s_ka3, + /* 141: kb2 */ screen_rxvt_s_kb2, + /* 142: kc1 */ screen_rxvt_s_kc1, + /* 143: kc3 */ screen_rxvt_s_kc3, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_rxvt_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_rxvt_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ screen_rxvt_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_rxvt_s_kend, + /* 165: kent */ screen_rxvt_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ screen_rxvt_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ screen_rxvt_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ screen_rxvt_s_kHOM, + /* 200: kIC */ screen_rxvt_s_kIC, + /* 201: kLFT */ screen_rxvt_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ screen_rxvt_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ screen_rxvt_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ screen_rxvt_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_rxvt_s_kf11, + /* 217: kf12 */ screen_rxvt_s_kf12, + /* 218: kf13 */ screen_rxvt_s_kf13, + /* 219: kf14 */ screen_rxvt_s_kf14, + /* 220: kf15 */ screen_rxvt_s_kf15, + /* 221: kf16 */ screen_rxvt_s_kf16, + /* 222: kf17 */ screen_rxvt_s_kf17, + /* 223: kf18 */ screen_rxvt_s_kf18, + /* 224: kf19 */ screen_rxvt_s_kf19, + /* 225: kf20 */ screen_rxvt_s_kf20, + /* 226: kf21 */ screen_rxvt_s_kf21, + /* 227: kf22 */ screen_rxvt_s_kf22, + /* 228: kf23 */ screen_rxvt_s_kf23, + /* 229: kf24 */ screen_rxvt_s_kf24, + /* 230: kf25 */ screen_rxvt_s_kf25, + /* 231: kf26 */ screen_rxvt_s_kf26, + /* 232: kf27 */ screen_rxvt_s_kf27, + /* 233: kf28 */ screen_rxvt_s_kf28, + /* 234: kf29 */ screen_rxvt_s_kf29, + /* 235: kf30 */ screen_rxvt_s_kf30, + /* 236: kf31 */ screen_rxvt_s_kf31, + /* 237: kf32 */ screen_rxvt_s_kf32, + /* 238: kf33 */ screen_rxvt_s_kf33, + /* 239: kf34 */ screen_rxvt_s_kf34, + /* 240: kf35 */ screen_rxvt_s_kf35, + /* 241: kf36 */ screen_rxvt_s_kf36, + /* 242: kf37 */ screen_rxvt_s_kf37, + /* 243: kf38 */ screen_rxvt_s_kf38, + /* 244: kf39 */ screen_rxvt_s_kf39, + /* 245: kf40 */ screen_rxvt_s_kf40, + /* 246: kf41 */ screen_rxvt_s_kf41, + /* 247: kf42 */ screen_rxvt_s_kf42, + /* 248: kf43 */ screen_rxvt_s_kf43, + /* 249: kf44 */ screen_rxvt_s_kf44, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ screen_rxvt_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ screen_rxvt_s_u6, + /* 294: u7 */ screen_rxvt_s_u7, + /* 295: u8 */ screen_rxvt_s_u8, + /* 296: u9 */ screen_rxvt_s_u9, + /* 297: op */ screen_rxvt_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_rxvt_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_rxvt_s_setaf, + /* 360: setab */ screen_rxvt_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.xterm-new */ + +static char screen_xterm_xfree86_alias_data[] = "screen.xterm-xfree86|screen.xterm-new|screen customized for modern xterm"; + +static char screen_xterm_xfree86_s_cbt[] = "\033[Z"; +static char screen_xterm_xfree86_s_bel[] = "\007"; +static char screen_xterm_xfree86_s_cr[] = "\015"; +static char screen_xterm_xfree86_s_csr[] = "\033[%i%p1%d;%p2%dr"; +static char screen_xterm_xfree86_s_tbc[] = "\033[3g"; +static char screen_xterm_xfree86_s_clear[] = "\033[H\033[2J"; +static char screen_xterm_xfree86_s_el[] = "\033[K"; +static char screen_xterm_xfree86_s_ed[] = "\033[J"; +static char screen_xterm_xfree86_s_hpa[] = "\033[%i%p1%dG"; +static char screen_xterm_xfree86_s_cup[] = "\033[%i%p1%d;%p2%dH"; +static char screen_xterm_xfree86_s_cud1[] = "\012"; +static char screen_xterm_xfree86_s_home[] = "\033[H"; +static char screen_xterm_xfree86_s_civis[] = "\033[?25l"; +static char screen_xterm_xfree86_s_cub1[] = "\010"; +static char screen_xterm_xfree86_s_cnorm[] = "\033[?12l\033[?25h"; +static char screen_xterm_xfree86_s_cuf1[] = "\033[C"; +static char screen_xterm_xfree86_s_cuu1[] = "\033[A"; +static char screen_xterm_xfree86_s_cvvis[] = "\033[?12;25h"; +static char screen_xterm_xfree86_s_dch1[] = "\033[P"; +static char screen_xterm_xfree86_s_dl1[] = "\033[M"; +static char screen_xterm_xfree86_s_smacs[] = "\033(0"; +static char screen_xterm_xfree86_s_blink[] = "\033[5m"; +static char screen_xterm_xfree86_s_bold[] = "\033[1m"; +static char screen_xterm_xfree86_s_smcup[] = "\033[?1049h\033[22;0;0t"; +static char screen_xterm_xfree86_s_dim[] = "\033[2m"; +static char screen_xterm_xfree86_s_smir[] = "\033[4h"; +static char screen_xterm_xfree86_s_rev[] = "\033[7m"; +static char screen_xterm_xfree86_s_smso[] = "\033[7m"; +static char screen_xterm_xfree86_s_smul[] = "\033[4m"; +static char screen_xterm_xfree86_s_ech[] = "\033[%p1%dX"; +static char screen_xterm_xfree86_s_rmacs[] = "\033(B"; +static char screen_xterm_xfree86_s_sgr0[] = "\033(B\033[m"; +static char screen_xterm_xfree86_s_rmcup[] = "\033[?1049l\033[23;0;0t"; +static char screen_xterm_xfree86_s_rmir[] = "\033[4l"; +static char screen_xterm_xfree86_s_rmso[] = "\033[27m"; +static char screen_xterm_xfree86_s_rmul[] = "\033[24m"; +static char screen_xterm_xfree86_s_flash[] = "\033[?5h$<100/>\033[?5l"; +static char screen_xterm_xfree86_s_is2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_xfree86_s_il1[] = "\033[L"; +static char screen_xterm_xfree86_s_kbs[] = "\010"; +static char screen_xterm_xfree86_s_kdch1[] = "\033[3~"; +static char screen_xterm_xfree86_s_kcud1[] = "\033OB"; +static char screen_xterm_xfree86_s_kf1[] = "\033OP"; +static char screen_xterm_xfree86_s_kf10[] = "\033[21~"; +static char screen_xterm_xfree86_s_kf2[] = "\033OQ"; +static char screen_xterm_xfree86_s_kf3[] = "\033OR"; +static char screen_xterm_xfree86_s_kf4[] = "\033OS"; +static char screen_xterm_xfree86_s_kf5[] = "\033[15~"; +static char screen_xterm_xfree86_s_kf6[] = "\033[17~"; +static char screen_xterm_xfree86_s_kf7[] = "\033[18~"; +static char screen_xterm_xfree86_s_kf8[] = "\033[19~"; +static char screen_xterm_xfree86_s_kf9[] = "\033[20~"; +static char screen_xterm_xfree86_s_khome[] = "\033[1~"; +static char screen_xterm_xfree86_s_kich1[] = "\033[2~"; +static char screen_xterm_xfree86_s_kcub1[] = "\033OD"; +static char screen_xterm_xfree86_s_knp[] = "\033[6~"; +static char screen_xterm_xfree86_s_kpp[] = "\033[5~"; +static char screen_xterm_xfree86_s_kcuf1[] = "\033OC"; +static char screen_xterm_xfree86_s_kind[] = "\033[1;2B"; +static char screen_xterm_xfree86_s_kri[] = "\033[1;2A"; +static char screen_xterm_xfree86_s_kcuu1[] = "\033OA"; +static char screen_xterm_xfree86_s_rmkx[] = "\033[?1l\033>"; +static char screen_xterm_xfree86_s_smkx[] = "\033[?1h\033="; +static char screen_xterm_xfree86_s_rmm[] = "\033[?1034l"; +static char screen_xterm_xfree86_s_smm[] = "\033[?1034h"; +static char screen_xterm_xfree86_s_dch[] = "\033[%p1%dP"; +static char screen_xterm_xfree86_s_dl[] = "\033[%p1%dM"; +static char screen_xterm_xfree86_s_cud[] = "\033[%p1%dB"; +static char screen_xterm_xfree86_s_ich[] = "\033[%p1%d@"; +static char screen_xterm_xfree86_s_indn[] = "\033[%p1%dS"; +static char screen_xterm_xfree86_s_il[] = "\033[%p1%dL"; +static char screen_xterm_xfree86_s_cub[] = "\033[%p1%dD"; +static char screen_xterm_xfree86_s_cuf[] = "\033[%p1%dC"; +static char screen_xterm_xfree86_s_rin[] = "\033[%p1%dT"; +static char screen_xterm_xfree86_s_cuu[] = "\033[%p1%dA"; +static char screen_xterm_xfree86_s_mc0[] = "\033[i"; +static char screen_xterm_xfree86_s_mc4[] = "\033[4i"; +static char screen_xterm_xfree86_s_mc5[] = "\033[5i"; +static char screen_xterm_xfree86_s_rs1[] = "\033c"; +static char screen_xterm_xfree86_s_rs2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_xfree86_s_rc[] = "\0338"; +static char screen_xterm_xfree86_s_vpa[] = "\033[%i%p1%dd"; +static char screen_xterm_xfree86_s_sc[] = "\0337"; +static char screen_xterm_xfree86_s_ind[] = "\012"; +static char screen_xterm_xfree86_s_ri[] = "\033M"; +static char screen_xterm_xfree86_s_sgr[] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p5%t;2%;m"; +static char screen_xterm_xfree86_s_hts[] = "\033H"; +static char screen_xterm_xfree86_s_ht[] = "\011"; +static char screen_xterm_xfree86_s_kb2[] = "\033OE"; +static char screen_xterm_xfree86_s_acsc[] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_xterm_xfree86_s_kcbt[] = "\033[Z"; +static char screen_xterm_xfree86_s_smam[] = "\033[?7h"; +static char screen_xterm_xfree86_s_rmam[] = "\033[?7l"; +static char screen_xterm_xfree86_s_kend[] = "\033[4~"; +static char screen_xterm_xfree86_s_kent[] = "\033OM"; +static char screen_xterm_xfree86_s_kDC[] = "\033[3;2~"; +static char screen_xterm_xfree86_s_kEND[] = "\033[1;2F"; +static char screen_xterm_xfree86_s_kHOM[] = "\033[1;2H"; +static char screen_xterm_xfree86_s_kLFT[] = "\033[1;2D"; +static char screen_xterm_xfree86_s_kRIT[] = "\033[1;2C"; +static char screen_xterm_xfree86_s_kf11[] = "\033[23~"; +static char screen_xterm_xfree86_s_kf12[] = "\033[24~"; +static char screen_xterm_xfree86_s_kf13[] = "\033[1;2P"; +static char screen_xterm_xfree86_s_kf14[] = "\033[1;2Q"; +static char screen_xterm_xfree86_s_kf15[] = "\033[1;2R"; +static char screen_xterm_xfree86_s_kf16[] = "\033[1;2S"; +static char screen_xterm_xfree86_s_kf17[] = "\033[15;2~"; +static char screen_xterm_xfree86_s_kf18[] = "\033[17;2~"; +static char screen_xterm_xfree86_s_kf19[] = "\033[18;2~"; +static char screen_xterm_xfree86_s_kf20[] = "\033[19;2~"; +static char screen_xterm_xfree86_s_kf21[] = "\033[20;2~"; +static char screen_xterm_xfree86_s_kf22[] = "\033[21;2~"; +static char screen_xterm_xfree86_s_kf23[] = "\033[23;2~"; +static char screen_xterm_xfree86_s_kf24[] = "\033[24;2~"; +static char screen_xterm_xfree86_s_kf25[] = "\033[1;5P"; +static char screen_xterm_xfree86_s_kf26[] = "\033[1;5Q"; +static char screen_xterm_xfree86_s_kf27[] = "\033[1;5R"; +static char screen_xterm_xfree86_s_kf28[] = "\033[1;5S"; +static char screen_xterm_xfree86_s_kf29[] = "\033[15;5~"; +static char screen_xterm_xfree86_s_kf30[] = "\033[17;5~"; +static char screen_xterm_xfree86_s_kf31[] = "\033[18;5~"; +static char screen_xterm_xfree86_s_kf32[] = "\033[19;5~"; +static char screen_xterm_xfree86_s_kf33[] = "\033[20;5~"; +static char screen_xterm_xfree86_s_kf34[] = "\033[21;5~"; +static char screen_xterm_xfree86_s_kf35[] = "\033[23;5~"; +static char screen_xterm_xfree86_s_kf36[] = "\033[24;5~"; +static char screen_xterm_xfree86_s_kf37[] = "\033[1;6P"; +static char screen_xterm_xfree86_s_kf38[] = "\033[1;6Q"; +static char screen_xterm_xfree86_s_kf39[] = "\033[1;6R"; +static char screen_xterm_xfree86_s_kf40[] = "\033[1;6S"; +static char screen_xterm_xfree86_s_kf41[] = "\033[15;6~"; +static char screen_xterm_xfree86_s_kf42[] = "\033[17;6~"; +static char screen_xterm_xfree86_s_kf43[] = "\033[18;6~"; +static char screen_xterm_xfree86_s_kf44[] = "\033[19;6~"; +static char screen_xterm_xfree86_s_kf45[] = "\033[20;6~"; +static char screen_xterm_xfree86_s_kf46[] = "\033[21;6~"; +static char screen_xterm_xfree86_s_kf47[] = "\033[23;6~"; +static char screen_xterm_xfree86_s_kf48[] = "\033[24;6~"; +static char screen_xterm_xfree86_s_kf49[] = "\033[1;3P"; +static char screen_xterm_xfree86_s_kf50[] = "\033[1;3Q"; +static char screen_xterm_xfree86_s_kf51[] = "\033[1;3R"; +static char screen_xterm_xfree86_s_kf52[] = "\033[1;3S"; +static char screen_xterm_xfree86_s_kf53[] = "\033[15;3~"; +static char screen_xterm_xfree86_s_kf54[] = "\033[17;3~"; +static char screen_xterm_xfree86_s_kf55[] = "\033[18;3~"; +static char screen_xterm_xfree86_s_kf56[] = "\033[19;3~"; +static char screen_xterm_xfree86_s_kf57[] = "\033[20;3~"; +static char screen_xterm_xfree86_s_kf58[] = "\033[21;3~"; +static char screen_xterm_xfree86_s_kf59[] = "\033[23;3~"; +static char screen_xterm_xfree86_s_kf60[] = "\033[24;3~"; +static char screen_xterm_xfree86_s_kf61[] = "\033[1;4P"; +static char screen_xterm_xfree86_s_kf62[] = "\033[1;4Q"; +static char screen_xterm_xfree86_s_kf63[] = "\033[1;4R"; +static char screen_xterm_xfree86_s_el1[] = "\033[1K"; +static char screen_xterm_xfree86_s_u6[] = "\033[%i%d;%dR"; +static char screen_xterm_xfree86_s_u7[] = "\033[6n"; +static char screen_xterm_xfree86_s_u8[] = "\033[?%[;0123456789]c"; +static char screen_xterm_xfree86_s_u9[] = "\033[c"; +static char screen_xterm_xfree86_s_op[] = "\033[39;49m"; +static char screen_xterm_xfree86_s_setf[] = "\033[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char screen_xterm_xfree86_s_setb[] = "\033[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char screen_xterm_xfree86_s_kmous[] = "\033[M"; +static char screen_xterm_xfree86_s_setaf[] = "\033[3%p1%dm"; +static char screen_xterm_xfree86_s_setab[] = "\033[4%p1%dm"; + +static char screen_xterm_xfree86_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_xterm_xfree86_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_xterm_xfree86_string_data[] = { + /* 0: cbt */ screen_xterm_xfree86_s_cbt, + /* 1: bel */ screen_xterm_xfree86_s_bel, + /* 2: cr */ screen_xterm_xfree86_s_cr, + /* 3: csr */ screen_xterm_xfree86_s_csr, + /* 4: tbc */ screen_xterm_xfree86_s_tbc, + /* 5: clear */ screen_xterm_xfree86_s_clear, + /* 6: el */ screen_xterm_xfree86_s_el, + /* 7: ed */ screen_xterm_xfree86_s_ed, + /* 8: hpa */ screen_xterm_xfree86_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_xterm_xfree86_s_cup, + /* 11: cud1 */ screen_xterm_xfree86_s_cud1, + /* 12: home */ screen_xterm_xfree86_s_home, + /* 13: civis */ screen_xterm_xfree86_s_civis, + /* 14: cub1 */ screen_xterm_xfree86_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_xterm_xfree86_s_cnorm, + /* 17: cuf1 */ screen_xterm_xfree86_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_xterm_xfree86_s_cuu1, + /* 20: cvvis */ screen_xterm_xfree86_s_cvvis, + /* 21: dch1 */ screen_xterm_xfree86_s_dch1, + /* 22: dl1 */ screen_xterm_xfree86_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_xterm_xfree86_s_smacs, + /* 26: blink */ screen_xterm_xfree86_s_blink, + /* 27: bold */ screen_xterm_xfree86_s_bold, + /* 28: smcup */ screen_xterm_xfree86_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_xterm_xfree86_s_dim, + /* 31: smir */ screen_xterm_xfree86_s_smir, + /* 32: invis */ CANCELLED_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_xterm_xfree86_s_rev, + /* 35: smso */ screen_xterm_xfree86_s_smso, + /* 36: smul */ screen_xterm_xfree86_s_smul, + /* 37: ech */ screen_xterm_xfree86_s_ech, + /* 38: rmacs */ screen_xterm_xfree86_s_rmacs, + /* 39: sgr0 */ screen_xterm_xfree86_s_sgr0, + /* 40: rmcup */ screen_xterm_xfree86_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_xterm_xfree86_s_rmir, + /* 43: rmso */ screen_xterm_xfree86_s_rmso, + /* 44: rmul */ screen_xterm_xfree86_s_rmul, + /* 45: flash */ screen_xterm_xfree86_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_xterm_xfree86_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_xterm_xfree86_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_xterm_xfree86_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_xterm_xfree86_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_xterm_xfree86_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_xterm_xfree86_s_kf1, + /* 67: kf10 */ screen_xterm_xfree86_s_kf10, + /* 68: kf2 */ screen_xterm_xfree86_s_kf2, + /* 69: kf3 */ screen_xterm_xfree86_s_kf3, + /* 70: kf4 */ screen_xterm_xfree86_s_kf4, + /* 71: kf5 */ screen_xterm_xfree86_s_kf5, + /* 72: kf6 */ screen_xterm_xfree86_s_kf6, + /* 73: kf7 */ screen_xterm_xfree86_s_kf7, + /* 74: kf8 */ screen_xterm_xfree86_s_kf8, + /* 75: kf9 */ screen_xterm_xfree86_s_kf9, + /* 76: khome */ screen_xterm_xfree86_s_khome, + /* 77: kich1 */ screen_xterm_xfree86_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_xterm_xfree86_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_xterm_xfree86_s_knp, + /* 82: kpp */ screen_xterm_xfree86_s_kpp, + /* 83: kcuf1 */ screen_xterm_xfree86_s_kcuf1, + /* 84: kind */ screen_xterm_xfree86_s_kind, + /* 85: kri */ screen_xterm_xfree86_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_xterm_xfree86_s_kcuu1, + /* 88: rmkx */ screen_xterm_xfree86_s_rmkx, + /* 89: smkx */ screen_xterm_xfree86_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ screen_xterm_xfree86_s_rmm, + /* 102: smm */ screen_xterm_xfree86_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_xterm_xfree86_s_dch, + /* 106: dl */ screen_xterm_xfree86_s_dl, + /* 107: cud */ screen_xterm_xfree86_s_cud, + /* 108: ich */ screen_xterm_xfree86_s_ich, + /* 109: indn */ screen_xterm_xfree86_s_indn, + /* 110: il */ screen_xterm_xfree86_s_il, + /* 111: cub */ screen_xterm_xfree86_s_cub, + /* 112: cuf */ screen_xterm_xfree86_s_cuf, + /* 113: rin */ screen_xterm_xfree86_s_rin, + /* 114: cuu */ screen_xterm_xfree86_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ screen_xterm_xfree86_s_mc0, + /* 119: mc4 */ screen_xterm_xfree86_s_mc4, + /* 120: mc5 */ screen_xterm_xfree86_s_mc5, + /* 121: rep */ CANCELLED_STRING, + /* 122: rs1 */ screen_xterm_xfree86_s_rs1, + /* 123: rs2 */ screen_xterm_xfree86_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_xterm_xfree86_s_rc, + /* 127: vpa */ screen_xterm_xfree86_s_vpa, + /* 128: sc */ screen_xterm_xfree86_s_sc, + /* 129: ind */ screen_xterm_xfree86_s_ind, + /* 130: ri */ screen_xterm_xfree86_s_ri, + /* 131: sgr */ screen_xterm_xfree86_s_sgr, + /* 132: hts */ screen_xterm_xfree86_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_xterm_xfree86_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ screen_xterm_xfree86_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_xterm_xfree86_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_xterm_xfree86_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ screen_xterm_xfree86_s_smam, + /* 152: rmam */ screen_xterm_xfree86_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_xterm_xfree86_s_kend, + /* 165: kent */ screen_xterm_xfree86_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ screen_xterm_xfree86_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ screen_xterm_xfree86_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ screen_xterm_xfree86_s_kHOM, + /* 200: kIC */ CANCELLED_STRING, + /* 201: kLFT */ screen_xterm_xfree86_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ CANCELLED_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ CANCELLED_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ screen_xterm_xfree86_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_xterm_xfree86_s_kf11, + /* 217: kf12 */ screen_xterm_xfree86_s_kf12, + /* 218: kf13 */ screen_xterm_xfree86_s_kf13, + /* 219: kf14 */ screen_xterm_xfree86_s_kf14, + /* 220: kf15 */ screen_xterm_xfree86_s_kf15, + /* 221: kf16 */ screen_xterm_xfree86_s_kf16, + /* 222: kf17 */ screen_xterm_xfree86_s_kf17, + /* 223: kf18 */ screen_xterm_xfree86_s_kf18, + /* 224: kf19 */ screen_xterm_xfree86_s_kf19, + /* 225: kf20 */ screen_xterm_xfree86_s_kf20, + /* 226: kf21 */ screen_xterm_xfree86_s_kf21, + /* 227: kf22 */ screen_xterm_xfree86_s_kf22, + /* 228: kf23 */ screen_xterm_xfree86_s_kf23, + /* 229: kf24 */ screen_xterm_xfree86_s_kf24, + /* 230: kf25 */ screen_xterm_xfree86_s_kf25, + /* 231: kf26 */ screen_xterm_xfree86_s_kf26, + /* 232: kf27 */ screen_xterm_xfree86_s_kf27, + /* 233: kf28 */ screen_xterm_xfree86_s_kf28, + /* 234: kf29 */ screen_xterm_xfree86_s_kf29, + /* 235: kf30 */ screen_xterm_xfree86_s_kf30, + /* 236: kf31 */ screen_xterm_xfree86_s_kf31, + /* 237: kf32 */ screen_xterm_xfree86_s_kf32, + /* 238: kf33 */ screen_xterm_xfree86_s_kf33, + /* 239: kf34 */ screen_xterm_xfree86_s_kf34, + /* 240: kf35 */ screen_xterm_xfree86_s_kf35, + /* 241: kf36 */ screen_xterm_xfree86_s_kf36, + /* 242: kf37 */ screen_xterm_xfree86_s_kf37, + /* 243: kf38 */ screen_xterm_xfree86_s_kf38, + /* 244: kf39 */ screen_xterm_xfree86_s_kf39, + /* 245: kf40 */ screen_xterm_xfree86_s_kf40, + /* 246: kf41 */ screen_xterm_xfree86_s_kf41, + /* 247: kf42 */ screen_xterm_xfree86_s_kf42, + /* 248: kf43 */ screen_xterm_xfree86_s_kf43, + /* 249: kf44 */ screen_xterm_xfree86_s_kf44, + /* 250: kf45 */ screen_xterm_xfree86_s_kf45, + /* 251: kf46 */ screen_xterm_xfree86_s_kf46, + /* 252: kf47 */ screen_xterm_xfree86_s_kf47, + /* 253: kf48 */ screen_xterm_xfree86_s_kf48, + /* 254: kf49 */ screen_xterm_xfree86_s_kf49, + /* 255: kf50 */ screen_xterm_xfree86_s_kf50, + /* 256: kf51 */ screen_xterm_xfree86_s_kf51, + /* 257: kf52 */ screen_xterm_xfree86_s_kf52, + /* 258: kf53 */ screen_xterm_xfree86_s_kf53, + /* 259: kf54 */ screen_xterm_xfree86_s_kf54, + /* 260: kf55 */ screen_xterm_xfree86_s_kf55, + /* 261: kf56 */ screen_xterm_xfree86_s_kf56, + /* 262: kf57 */ screen_xterm_xfree86_s_kf57, + /* 263: kf58 */ screen_xterm_xfree86_s_kf58, + /* 264: kf59 */ screen_xterm_xfree86_s_kf59, + /* 265: kf60 */ screen_xterm_xfree86_s_kf60, + /* 266: kf61 */ screen_xterm_xfree86_s_kf61, + /* 267: kf62 */ screen_xterm_xfree86_s_kf62, + /* 268: kf63 */ screen_xterm_xfree86_s_kf63, + /* 269: el1 */ screen_xterm_xfree86_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ screen_xterm_xfree86_s_u6, + /* 294: u7 */ screen_xterm_xfree86_s_u7, + /* 295: u8 */ screen_xterm_xfree86_s_u8, + /* 296: u9 */ screen_xterm_xfree86_s_u9, + /* 297: op */ screen_xterm_xfree86_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ screen_xterm_xfree86_s_setf, + /* 303: setb */ screen_xterm_xfree86_s_setb, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_xterm_xfree86_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_xterm_xfree86_s_setaf, + /* 360: setab */ screen_xterm_xfree86_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ CANCELLED_STRING, + /* 412: memu */ CANCELLED_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.xterm-256color */ + +static char screen_xterm_256color_alias_data[] = "screen.xterm-256color|GNU Screen with xterm using 256 colors"; + +static char screen_xterm_256color_s_cbt[] = "\033[Z"; +static char screen_xterm_256color_s_bel[] = "\007"; +static char screen_xterm_256color_s_cr[] = "\015"; +static char screen_xterm_256color_s_csr[] = "\033[%i%p1%d;%p2%dr"; +static char screen_xterm_256color_s_tbc[] = "\033[3g"; +static char screen_xterm_256color_s_clear[] = "\033[H\033[2J"; +static char screen_xterm_256color_s_el[] = "\033[K"; +static char screen_xterm_256color_s_ed[] = "\033[J"; +static char screen_xterm_256color_s_hpa[] = "\033[%i%p1%dG"; +static char screen_xterm_256color_s_cup[] = "\033[%i%p1%d;%p2%dH"; +static char screen_xterm_256color_s_cud1[] = "\012"; +static char screen_xterm_256color_s_home[] = "\033[H"; +static char screen_xterm_256color_s_civis[] = "\033[?25l"; +static char screen_xterm_256color_s_cub1[] = "\010"; +static char screen_xterm_256color_s_cnorm[] = "\033[?12l\033[?25h"; +static char screen_xterm_256color_s_cuf1[] = "\033[C"; +static char screen_xterm_256color_s_cuu1[] = "\033[A"; +static char screen_xterm_256color_s_cvvis[] = "\033[?12;25h"; +static char screen_xterm_256color_s_dch1[] = "\033[P"; +static char screen_xterm_256color_s_dl1[] = "\033[M"; +static char screen_xterm_256color_s_smacs[] = "\033(0"; +static char screen_xterm_256color_s_blink[] = "\033[5m"; +static char screen_xterm_256color_s_bold[] = "\033[1m"; +static char screen_xterm_256color_s_smcup[] = "\033[?1049h\033[22;0;0t"; +static char screen_xterm_256color_s_dim[] = "\033[2m"; +static char screen_xterm_256color_s_smir[] = "\033[4h"; +static char screen_xterm_256color_s_rev[] = "\033[7m"; +static char screen_xterm_256color_s_smso[] = "\033[7m"; +static char screen_xterm_256color_s_smul[] = "\033[4m"; +static char screen_xterm_256color_s_ech[] = "\033[%p1%dX"; +static char screen_xterm_256color_s_rmacs[] = "\033(B"; +static char screen_xterm_256color_s_sgr0[] = "\033(B\033[m"; +static char screen_xterm_256color_s_rmcup[] = "\033[?1049l\033[23;0;0t"; +static char screen_xterm_256color_s_rmir[] = "\033[4l"; +static char screen_xterm_256color_s_rmso[] = "\033[27m"; +static char screen_xterm_256color_s_rmul[] = "\033[24m"; +static char screen_xterm_256color_s_flash[] = "\033[?5h$<100/>\033[?5l"; +static char screen_xterm_256color_s_is2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_256color_s_il1[] = "\033[L"; +static char screen_xterm_256color_s_kbs[] = "\010"; +static char screen_xterm_256color_s_kdch1[] = "\033[3~"; +static char screen_xterm_256color_s_kcud1[] = "\033OB"; +static char screen_xterm_256color_s_kf1[] = "\033OP"; +static char screen_xterm_256color_s_kf10[] = "\033[21~"; +static char screen_xterm_256color_s_kf2[] = "\033OQ"; +static char screen_xterm_256color_s_kf3[] = "\033OR"; +static char screen_xterm_256color_s_kf4[] = "\033OS"; +static char screen_xterm_256color_s_kf5[] = "\033[15~"; +static char screen_xterm_256color_s_kf6[] = "\033[17~"; +static char screen_xterm_256color_s_kf7[] = "\033[18~"; +static char screen_xterm_256color_s_kf8[] = "\033[19~"; +static char screen_xterm_256color_s_kf9[] = "\033[20~"; +static char screen_xterm_256color_s_khome[] = "\033[1~"; +static char screen_xterm_256color_s_kich1[] = "\033[2~"; +static char screen_xterm_256color_s_kcub1[] = "\033OD"; +static char screen_xterm_256color_s_knp[] = "\033[6~"; +static char screen_xterm_256color_s_kpp[] = "\033[5~"; +static char screen_xterm_256color_s_kcuf1[] = "\033OC"; +static char screen_xterm_256color_s_kind[] = "\033[1;2B"; +static char screen_xterm_256color_s_kri[] = "\033[1;2A"; +static char screen_xterm_256color_s_kcuu1[] = "\033OA"; +static char screen_xterm_256color_s_rmkx[] = "\033[?1l\033>"; +static char screen_xterm_256color_s_smkx[] = "\033[?1h\033="; +static char screen_xterm_256color_s_rmm[] = "\033[?1034l"; +static char screen_xterm_256color_s_smm[] = "\033[?1034h"; +static char screen_xterm_256color_s_dch[] = "\033[%p1%dP"; +static char screen_xterm_256color_s_dl[] = "\033[%p1%dM"; +static char screen_xterm_256color_s_cud[] = "\033[%p1%dB"; +static char screen_xterm_256color_s_ich[] = "\033[%p1%d@"; +static char screen_xterm_256color_s_indn[] = "\033[%p1%dS"; +static char screen_xterm_256color_s_il[] = "\033[%p1%dL"; +static char screen_xterm_256color_s_cub[] = "\033[%p1%dD"; +static char screen_xterm_256color_s_cuf[] = "\033[%p1%dC"; +static char screen_xterm_256color_s_rin[] = "\033[%p1%dT"; +static char screen_xterm_256color_s_cuu[] = "\033[%p1%dA"; +static char screen_xterm_256color_s_mc0[] = "\033[i"; +static char screen_xterm_256color_s_mc4[] = "\033[4i"; +static char screen_xterm_256color_s_mc5[] = "\033[5i"; +static char screen_xterm_256color_s_rs1[] = "\033c"; +static char screen_xterm_256color_s_rs2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_256color_s_rc[] = "\0338"; +static char screen_xterm_256color_s_vpa[] = "\033[%i%p1%dd"; +static char screen_xterm_256color_s_sc[] = "\0337"; +static char screen_xterm_256color_s_ind[] = "\012"; +static char screen_xterm_256color_s_ri[] = "\033M"; +static char screen_xterm_256color_s_sgr[] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p5%t;2%;m"; +static char screen_xterm_256color_s_hts[] = "\033H"; +static char screen_xterm_256color_s_ht[] = "\011"; +static char screen_xterm_256color_s_kb2[] = "\033OE"; +static char screen_xterm_256color_s_acsc[] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_xterm_256color_s_kcbt[] = "\033[Z"; +static char screen_xterm_256color_s_smam[] = "\033[?7h"; +static char screen_xterm_256color_s_rmam[] = "\033[?7l"; +static char screen_xterm_256color_s_kend[] = "\033[4~"; +static char screen_xterm_256color_s_kent[] = "\033OM"; +static char screen_xterm_256color_s_kDC[] = "\033[3;2~"; +static char screen_xterm_256color_s_kEND[] = "\033[1;2F"; +static char screen_xterm_256color_s_kHOM[] = "\033[1;2H"; +static char screen_xterm_256color_s_kLFT[] = "\033[1;2D"; +static char screen_xterm_256color_s_kRIT[] = "\033[1;2C"; +static char screen_xterm_256color_s_kf11[] = "\033[23~"; +static char screen_xterm_256color_s_kf12[] = "\033[24~"; +static char screen_xterm_256color_s_kf13[] = "\033[1;2P"; +static char screen_xterm_256color_s_kf14[] = "\033[1;2Q"; +static char screen_xterm_256color_s_kf15[] = "\033[1;2R"; +static char screen_xterm_256color_s_kf16[] = "\033[1;2S"; +static char screen_xterm_256color_s_kf17[] = "\033[15;2~"; +static char screen_xterm_256color_s_kf18[] = "\033[17;2~"; +static char screen_xterm_256color_s_kf19[] = "\033[18;2~"; +static char screen_xterm_256color_s_kf20[] = "\033[19;2~"; +static char screen_xterm_256color_s_kf21[] = "\033[20;2~"; +static char screen_xterm_256color_s_kf22[] = "\033[21;2~"; +static char screen_xterm_256color_s_kf23[] = "\033[23;2~"; +static char screen_xterm_256color_s_kf24[] = "\033[24;2~"; +static char screen_xterm_256color_s_kf25[] = "\033[1;5P"; +static char screen_xterm_256color_s_kf26[] = "\033[1;5Q"; +static char screen_xterm_256color_s_kf27[] = "\033[1;5R"; +static char screen_xterm_256color_s_kf28[] = "\033[1;5S"; +static char screen_xterm_256color_s_kf29[] = "\033[15;5~"; +static char screen_xterm_256color_s_kf30[] = "\033[17;5~"; +static char screen_xterm_256color_s_kf31[] = "\033[18;5~"; +static char screen_xterm_256color_s_kf32[] = "\033[19;5~"; +static char screen_xterm_256color_s_kf33[] = "\033[20;5~"; +static char screen_xterm_256color_s_kf34[] = "\033[21;5~"; +static char screen_xterm_256color_s_kf35[] = "\033[23;5~"; +static char screen_xterm_256color_s_kf36[] = "\033[24;5~"; +static char screen_xterm_256color_s_kf37[] = "\033[1;6P"; +static char screen_xterm_256color_s_kf38[] = "\033[1;6Q"; +static char screen_xterm_256color_s_kf39[] = "\033[1;6R"; +static char screen_xterm_256color_s_kf40[] = "\033[1;6S"; +static char screen_xterm_256color_s_kf41[] = "\033[15;6~"; +static char screen_xterm_256color_s_kf42[] = "\033[17;6~"; +static char screen_xterm_256color_s_kf43[] = "\033[18;6~"; +static char screen_xterm_256color_s_kf44[] = "\033[19;6~"; +static char screen_xterm_256color_s_kf45[] = "\033[20;6~"; +static char screen_xterm_256color_s_kf46[] = "\033[21;6~"; +static char screen_xterm_256color_s_kf47[] = "\033[23;6~"; +static char screen_xterm_256color_s_kf48[] = "\033[24;6~"; +static char screen_xterm_256color_s_kf49[] = "\033[1;3P"; +static char screen_xterm_256color_s_kf50[] = "\033[1;3Q"; +static char screen_xterm_256color_s_kf51[] = "\033[1;3R"; +static char screen_xterm_256color_s_kf52[] = "\033[1;3S"; +static char screen_xterm_256color_s_kf53[] = "\033[15;3~"; +static char screen_xterm_256color_s_kf54[] = "\033[17;3~"; +static char screen_xterm_256color_s_kf55[] = "\033[18;3~"; +static char screen_xterm_256color_s_kf56[] = "\033[19;3~"; +static char screen_xterm_256color_s_kf57[] = "\033[20;3~"; +static char screen_xterm_256color_s_kf58[] = "\033[21;3~"; +static char screen_xterm_256color_s_kf59[] = "\033[23;3~"; +static char screen_xterm_256color_s_kf60[] = "\033[24;3~"; +static char screen_xterm_256color_s_kf61[] = "\033[1;4P"; +static char screen_xterm_256color_s_kf62[] = "\033[1;4Q"; +static char screen_xterm_256color_s_kf63[] = "\033[1;4R"; +static char screen_xterm_256color_s_el1[] = "\033[1K"; +static char screen_xterm_256color_s_u6[] = "\033[%i%d;%dR"; +static char screen_xterm_256color_s_u7[] = "\033[6n"; +static char screen_xterm_256color_s_u8[] = "\033[?%[;0123456789]c"; +static char screen_xterm_256color_s_u9[] = "\033[c"; +static char screen_xterm_256color_s_op[] = "\033[39;49m"; +static char screen_xterm_256color_s_kmous[] = "\033[M"; +static char screen_xterm_256color_s_setaf[] = "\033[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"; +static char screen_xterm_256color_s_setab[] = "\033[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"; + +static char screen_xterm_256color_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_xterm_256color_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 256, + /* 14: pairs */ 32767, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_xterm_256color_string_data[] = { + /* 0: cbt */ screen_xterm_256color_s_cbt, + /* 1: bel */ screen_xterm_256color_s_bel, + /* 2: cr */ screen_xterm_256color_s_cr, + /* 3: csr */ screen_xterm_256color_s_csr, + /* 4: tbc */ screen_xterm_256color_s_tbc, + /* 5: clear */ screen_xterm_256color_s_clear, + /* 6: el */ screen_xterm_256color_s_el, + /* 7: ed */ screen_xterm_256color_s_ed, + /* 8: hpa */ screen_xterm_256color_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_xterm_256color_s_cup, + /* 11: cud1 */ screen_xterm_256color_s_cud1, + /* 12: home */ screen_xterm_256color_s_home, + /* 13: civis */ screen_xterm_256color_s_civis, + /* 14: cub1 */ screen_xterm_256color_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_xterm_256color_s_cnorm, + /* 17: cuf1 */ screen_xterm_256color_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_xterm_256color_s_cuu1, + /* 20: cvvis */ screen_xterm_256color_s_cvvis, + /* 21: dch1 */ screen_xterm_256color_s_dch1, + /* 22: dl1 */ screen_xterm_256color_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_xterm_256color_s_smacs, + /* 26: blink */ screen_xterm_256color_s_blink, + /* 27: bold */ screen_xterm_256color_s_bold, + /* 28: smcup */ screen_xterm_256color_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_xterm_256color_s_dim, + /* 31: smir */ screen_xterm_256color_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_xterm_256color_s_rev, + /* 35: smso */ screen_xterm_256color_s_smso, + /* 36: smul */ screen_xterm_256color_s_smul, + /* 37: ech */ screen_xterm_256color_s_ech, + /* 38: rmacs */ screen_xterm_256color_s_rmacs, + /* 39: sgr0 */ screen_xterm_256color_s_sgr0, + /* 40: rmcup */ screen_xterm_256color_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_xterm_256color_s_rmir, + /* 43: rmso */ screen_xterm_256color_s_rmso, + /* 44: rmul */ screen_xterm_256color_s_rmul, + /* 45: flash */ screen_xterm_256color_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_xterm_256color_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_xterm_256color_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_xterm_256color_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_xterm_256color_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_xterm_256color_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_xterm_256color_s_kf1, + /* 67: kf10 */ screen_xterm_256color_s_kf10, + /* 68: kf2 */ screen_xterm_256color_s_kf2, + /* 69: kf3 */ screen_xterm_256color_s_kf3, + /* 70: kf4 */ screen_xterm_256color_s_kf4, + /* 71: kf5 */ screen_xterm_256color_s_kf5, + /* 72: kf6 */ screen_xterm_256color_s_kf6, + /* 73: kf7 */ screen_xterm_256color_s_kf7, + /* 74: kf8 */ screen_xterm_256color_s_kf8, + /* 75: kf9 */ screen_xterm_256color_s_kf9, + /* 76: khome */ screen_xterm_256color_s_khome, + /* 77: kich1 */ screen_xterm_256color_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_xterm_256color_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_xterm_256color_s_knp, + /* 82: kpp */ screen_xterm_256color_s_kpp, + /* 83: kcuf1 */ screen_xterm_256color_s_kcuf1, + /* 84: kind */ screen_xterm_256color_s_kind, + /* 85: kri */ screen_xterm_256color_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_xterm_256color_s_kcuu1, + /* 88: rmkx */ screen_xterm_256color_s_rmkx, + /* 89: smkx */ screen_xterm_256color_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ screen_xterm_256color_s_rmm, + /* 102: smm */ screen_xterm_256color_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_xterm_256color_s_dch, + /* 106: dl */ screen_xterm_256color_s_dl, + /* 107: cud */ screen_xterm_256color_s_cud, + /* 108: ich */ screen_xterm_256color_s_ich, + /* 109: indn */ screen_xterm_256color_s_indn, + /* 110: il */ screen_xterm_256color_s_il, + /* 111: cub */ screen_xterm_256color_s_cub, + /* 112: cuf */ screen_xterm_256color_s_cuf, + /* 113: rin */ screen_xterm_256color_s_rin, + /* 114: cuu */ screen_xterm_256color_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ screen_xterm_256color_s_mc0, + /* 119: mc4 */ screen_xterm_256color_s_mc4, + /* 120: mc5 */ screen_xterm_256color_s_mc5, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ screen_xterm_256color_s_rs1, + /* 123: rs2 */ screen_xterm_256color_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_xterm_256color_s_rc, + /* 127: vpa */ screen_xterm_256color_s_vpa, + /* 128: sc */ screen_xterm_256color_s_sc, + /* 129: ind */ screen_xterm_256color_s_ind, + /* 130: ri */ screen_xterm_256color_s_ri, + /* 131: sgr */ screen_xterm_256color_s_sgr, + /* 132: hts */ screen_xterm_256color_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_xterm_256color_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ screen_xterm_256color_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_xterm_256color_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_xterm_256color_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ screen_xterm_256color_s_smam, + /* 152: rmam */ screen_xterm_256color_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_xterm_256color_s_kend, + /* 165: kent */ screen_xterm_256color_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ screen_xterm_256color_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ screen_xterm_256color_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ screen_xterm_256color_s_kHOM, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ screen_xterm_256color_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ screen_xterm_256color_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_xterm_256color_s_kf11, + /* 217: kf12 */ screen_xterm_256color_s_kf12, + /* 218: kf13 */ screen_xterm_256color_s_kf13, + /* 219: kf14 */ screen_xterm_256color_s_kf14, + /* 220: kf15 */ screen_xterm_256color_s_kf15, + /* 221: kf16 */ screen_xterm_256color_s_kf16, + /* 222: kf17 */ screen_xterm_256color_s_kf17, + /* 223: kf18 */ screen_xterm_256color_s_kf18, + /* 224: kf19 */ screen_xterm_256color_s_kf19, + /* 225: kf20 */ screen_xterm_256color_s_kf20, + /* 226: kf21 */ screen_xterm_256color_s_kf21, + /* 227: kf22 */ screen_xterm_256color_s_kf22, + /* 228: kf23 */ screen_xterm_256color_s_kf23, + /* 229: kf24 */ screen_xterm_256color_s_kf24, + /* 230: kf25 */ screen_xterm_256color_s_kf25, + /* 231: kf26 */ screen_xterm_256color_s_kf26, + /* 232: kf27 */ screen_xterm_256color_s_kf27, + /* 233: kf28 */ screen_xterm_256color_s_kf28, + /* 234: kf29 */ screen_xterm_256color_s_kf29, + /* 235: kf30 */ screen_xterm_256color_s_kf30, + /* 236: kf31 */ screen_xterm_256color_s_kf31, + /* 237: kf32 */ screen_xterm_256color_s_kf32, + /* 238: kf33 */ screen_xterm_256color_s_kf33, + /* 239: kf34 */ screen_xterm_256color_s_kf34, + /* 240: kf35 */ screen_xterm_256color_s_kf35, + /* 241: kf36 */ screen_xterm_256color_s_kf36, + /* 242: kf37 */ screen_xterm_256color_s_kf37, + /* 243: kf38 */ screen_xterm_256color_s_kf38, + /* 244: kf39 */ screen_xterm_256color_s_kf39, + /* 245: kf40 */ screen_xterm_256color_s_kf40, + /* 246: kf41 */ screen_xterm_256color_s_kf41, + /* 247: kf42 */ screen_xterm_256color_s_kf42, + /* 248: kf43 */ screen_xterm_256color_s_kf43, + /* 249: kf44 */ screen_xterm_256color_s_kf44, + /* 250: kf45 */ screen_xterm_256color_s_kf45, + /* 251: kf46 */ screen_xterm_256color_s_kf46, + /* 252: kf47 */ screen_xterm_256color_s_kf47, + /* 253: kf48 */ screen_xterm_256color_s_kf48, + /* 254: kf49 */ screen_xterm_256color_s_kf49, + /* 255: kf50 */ screen_xterm_256color_s_kf50, + /* 256: kf51 */ screen_xterm_256color_s_kf51, + /* 257: kf52 */ screen_xterm_256color_s_kf52, + /* 258: kf53 */ screen_xterm_256color_s_kf53, + /* 259: kf54 */ screen_xterm_256color_s_kf54, + /* 260: kf55 */ screen_xterm_256color_s_kf55, + /* 261: kf56 */ screen_xterm_256color_s_kf56, + /* 262: kf57 */ screen_xterm_256color_s_kf57, + /* 263: kf58 */ screen_xterm_256color_s_kf58, + /* 264: kf59 */ screen_xterm_256color_s_kf59, + /* 265: kf60 */ screen_xterm_256color_s_kf60, + /* 266: kf61 */ screen_xterm_256color_s_kf61, + /* 267: kf62 */ screen_xterm_256color_s_kf62, + /* 268: kf63 */ screen_xterm_256color_s_kf63, + /* 269: el1 */ screen_xterm_256color_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ screen_xterm_256color_s_u6, + /* 294: u7 */ screen_xterm_256color_s_u7, + /* 295: u8 */ screen_xterm_256color_s_u8, + /* 296: u9 */ screen_xterm_256color_s_u9, + /* 297: op */ screen_xterm_256color_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_xterm_256color_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_xterm_256color_s_setaf, + /* 360: setab */ screen_xterm_256color_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +static const TERMTYPE2 fallbacks[10] = +{ + /* linux */ + { + linux_alias_data, + (char *)0, /* pointer to string table */ + linux_bool_data, + linux_number_data, + linux_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* rxvt */ + { + rxvt_alias_data, + (char *)0, /* pointer to string table */ + rxvt_bool_data, + rxvt_number_data, + rxvt_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* vt100 */ + { + vt100_alias_data, + (char *)0, /* pointer to string table */ + vt100_bool_data, + vt100_number_data, + vt100_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* xterm */ + { + xterm_alias_data, + (char *)0, /* pointer to string table */ + xterm_bool_data, + xterm_number_data, + xterm_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* xterm-256color */ + { + xterm_256color_alias_data, + (char *)0, /* pointer to string table */ + xterm_256color_bool_data, + xterm_256color_number_data, + xterm_256color_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen */ + { + screen_alias_data, + (char *)0, /* pointer to string table */ + screen_bool_data, + screen_number_data, + screen_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.linux */ + { + screen_linux_alias_data, + (char *)0, /* pointer to string table */ + screen_linux_bool_data, + screen_linux_number_data, + screen_linux_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.rxvt */ + { + screen_rxvt_alias_data, + (char *)0, /* pointer to string table */ + screen_rxvt_bool_data, + screen_rxvt_number_data, + screen_rxvt_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.xterm-new */ + { + screen_xterm_xfree86_alias_data, + (char *)0, /* pointer to string table */ + screen_xterm_xfree86_bool_data, + screen_xterm_xfree86_number_data, + screen_xterm_xfree86_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.xterm-256color */ + { + screen_xterm_256color_alias_data, + (char *)0, /* pointer to string table */ + screen_xterm_256color_bool_data, + screen_xterm_256color_number_data, + screen_xterm_256color_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +}; + +NCURSES_EXPORT(const TERMTYPE2 *) +_nc_fallback2 (const char *name GCC_UNUSED) +{ + const TERMTYPE2 *tp; + + for (tp = fallbacks; + tp < fallbacks + sizeof(fallbacks)/sizeof(TERMTYPE2); + tp++) { + if (_nc_name_match(tp->term_names, name, "|")) { + return(tp); + } + } + return((const TERMTYPE2 *)0); +} + +#if NCURSES_EXT_NUMBERS +#undef _nc_fallback + +/* + * This entrypoint is used by tack. + */ +NCURSES_EXPORT(const TERMTYPE *) +_nc_fallback (const char *name) +{ + const TERMTYPE2 *tp = _nc_fallback2(name); + const TERMTYPE *result = 0; + if (tp != 0) { + static TERMTYPE temp; + _nc_export_termtype2(&temp, tp); + result = &temp; + } + return result; +} +#endif diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index ebe96b69c..94902c57a 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -23,7 +23,7 @@ SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE) SET(Readline_ROOT_DIR @prefix@) SET(Readline_INCLUDE_DIR @prefix@/include) SET(Readline_LIBRARY @prefix@/lib/libreadline.a) -SET(Termcap_LIBRARY @prefix@/lib/libtinfo.a) +SET(Terminfo_LIBRARY @prefix@/lib/libtinfo.a) SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) From 643860776e923428d2d2207ec70ead52adcc9272 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Fri, 1 Nov 2019 03:38:13 +0000 Subject: [PATCH 041/469] Add Android support --- contrib/depends/Makefile | 6 + contrib/depends/hosts/android.mk | 20 +++ contrib/depends/packages/android_ndk.mk | 22 +++ contrib/depends/packages/boost.mk | 2 + contrib/depends/packages/openssl.mk | 5 +- contrib/depends/packages/packages.mk | 10 +- contrib/depends/packages/sodium.mk | 1 + contrib/depends/packages/zeromq.mk | 3 +- ...2d3398d5e0191f554f61049aa7ec9fc892ae.patch | 38 +++++ contrib/depends/toolchain.cmake.in | 36 ++++- contrib/gitian/gitian-android.yml | 135 ++++++++++++++++++ contrib/gitian/gitian-build.py | 12 +- 12 files changed, 281 insertions(+), 9 deletions(-) create mode 100644 contrib/depends/hosts/android.mk create mode 100644 contrib/depends/packages/android_ndk.mk create mode 100644 contrib/depends/patches/zeromq/ffe62d3398d5e0191f554f61049aa7ec9fc892ae.patch create mode 100644 contrib/gitian/gitian-android.yml diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index ec0e4cfae..7eedee15e 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -53,7 +53,10 @@ endif host_arch=$(firstword $(subst -, ,$(canonical_host))) host_vendor=$(word 2,$(subst -, ,$(canonical_host))) full_host_os:=$(subst $(host_arch)-$(host_vendor)-,,$(canonical_host)) +host_os:=$(findstring android,$(full_host_os)) +ifeq ($(host_os),) host_os:=$(findstring linux,$(full_host_os)) +endif host_os+=$(findstring darwin,$(full_host_os)) host_os+=$(findstring mingw32,$(full_host_os)) host_os:=$(strip $(host_os)) @@ -74,6 +77,9 @@ endif ifeq ($(host_os),darwin) host_cmake=Darwin endif +ifeq ($(host_os),android) +host_cmake=Android +endif AT_$(V):= AT_:=@ diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk new file mode 100644 index 000000000..2a24435d4 --- /dev/null +++ b/contrib/depends/hosts/android.mk @@ -0,0 +1,20 @@ +ANDROID_API=21 + +ifeq ($(host_arch),arm) +host_toolchain=arm-linux-androideabi- +endif + +android_CC=$(host_toolchain)clang +android_CXX=$(host_toolchain)clang++ + +android_CFLAGS=-pipe +android_CXXFLAGS=$(android_CFLAGS) + +android_release_CFLAGS=-O2 +android_release_CXXFLAGS=$(android_release_CFLAGS) + +android_debug_CFLAGS=-g -O0 +android_debug_CXXFLAGS=$(android_debug_CFLAGS) + +android_native_toolchain=android_ndk + diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk new file mode 100644 index 000000000..9b8a5332f --- /dev/null +++ b/contrib/depends/packages/android_ndk.mk @@ -0,0 +1,22 @@ +package=android_ndk +$(package)_version=17b +$(package)_download_path=https://dl.google.com/android/repository/ +$(package)_file_name=android-ndk-r$($(package)_version)-linux-x86_64.zip +$(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd + +define $(package)_set_vars +$(package)_config_opts_arm=--arch arm +$(package)_config_opts_aarch64=--arch arm64 +endef + +define $(package)_extract_cmds + echo $($(package)_sha256_hash) $($(1)_source_dir)/$($(package)_file_name) | sha256sum -c &&\ + unzip -q $($(1)_source_dir)/$($(package)_file_name) +endef + +define $(package)_stage_cmds + android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api 21 \ + --install-dir $(build_prefix) --stl=libc++ $($(package)_config_opts) &&\ + mv $(build_prefix) $($(package)_staging_dir)/$(host_prefix) +endef + diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index e60a1c677..3231df925 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -3,6 +3,7 @@ $(package)_version=1_64_0 $(package)_download_path=https://dl.bintray.com/boostorg/release/1.64.0/source/ $(package)_file_name=$(package)_$($(package)_version).tar.bz2 $(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 +$(package)_dependencies=libiconv define $(package)_set_vars $(package)_config_opts_release=variant=release @@ -10,6 +11,7 @@ $(package)_config_opts_debug=variant=debug $(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam $(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1 $(package)_config_opts_linux=threadapi=pthread runtime-link=shared +$(package)_config_opts_android=threadapi=pthread runtime-link=static target-os=android $(package)_config_opts_darwin=--toolset=darwin-4.2.1 runtime-link=shared $(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static $(package)_config_opts_x86_64_mingw32=address-model=64 diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk index 8d08900f5..f3b781f28 100644 --- a/contrib/depends/packages/openssl.mk +++ b/contrib/depends/packages/openssl.mk @@ -40,6 +40,8 @@ $(package)_config_opts_x86_64_linux=linux-x86_64 $(package)_config_opts_i686_linux=linux-generic32 $(package)_config_opts_arm_linux=linux-generic32 $(package)_config_opts_aarch64_linux=linux-generic64 +$(package)_config_opts_arm_android=--static android-armv7 no-asm +$(package)_config_opts_aarch64_android=--static android no-asm $(package)_config_opts_riscv64_linux=linux-generic64 $(package)_config_opts_mipsel_linux=linux-generic32 $(package)_config_opts_mips_linux=linux-generic32 @@ -51,7 +53,8 @@ endef define $(package)_preprocess_cmds sed -i.old "/define DATE/d" util/mkbuildinf.pl && \ - sed -i.old "s|engines apps test|engines|" Makefile.org + sed -i.old "s|engines apps test|engines|" Makefile.org && \ + sed -i -e "s/-mandroid //" Configure endef define $(package)_config_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index f4b9c6407..2d8030448 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,6 +1,14 @@ -packages:=boost openssl zeromq cppzmq expat ldns libiconv hidapi protobuf libusb +ifeq ($(host_os),android) +packages:=boost openssl zeromq libiconv +else +packages:=boost openssl zeromq lexpat dns libiconv hidapi protobuf libusb +endif + native_packages := native_ccache native_protobuf +android_native_packages = android_ndk +android_packages = ncurses readline sodium + darwin_native_packages = native_biplist native_ds_store native_mac_alias darwin_packages = sodium-darwin ncurses readline diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index b71f4383e..cdc690e69 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -8,6 +8,7 @@ $(package)_patches=fix-whitespace.patch define $(package)_set_vars $(package)_config_opts=--enable-static --disable-shared $(package)_config_opts+=--prefix=$(host_prefix) +$(package)_config_opts_android=RANLIB=$($(package)_ranlib) AR=$($(package)_ar) CC=$($(package)_cc) endef define $(package)_config_cmds diff --git a/contrib/depends/packages/zeromq.mk b/contrib/depends/packages/zeromq.mk index c3a405a60..46caf14cb 100644 --- a/contrib/depends/packages/zeromq.mk +++ b/contrib/depends/packages/zeromq.mk @@ -3,7 +3,7 @@ $(package)_version=4.1.7 $(package)_download_path=https://github.com/zeromq/zeromq4-1/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=31c383cfcd3be1dc8a66e448c403029e793687e70473b89c4cc0bd626e7da299 -$(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b100cd38acecc16ce7db02905e27c.patch +$(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b100cd38acecc16ce7db02905e27c.patch ffe62d3398d5e0191f554f61049aa7ec9fc892ae.patch define $(package)_set_vars $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve @@ -14,6 +14,7 @@ endef define $(package)_preprocess_cmds patch -p1 < $($(package)_patch_dir)/9114d3957725acd34aa8b8d011585812f3369411.patch && \ patch -p1 < $($(package)_patch_dir)/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch && \ + patch -p1 < $($(package)_patch_dir)/ffe62d3398d5e0191f554f61049aa7ec9fc892ae.patch && \ ./autogen.sh endef diff --git a/contrib/depends/patches/zeromq/ffe62d3398d5e0191f554f61049aa7ec9fc892ae.patch b/contrib/depends/patches/zeromq/ffe62d3398d5e0191f554f61049aa7ec9fc892ae.patch new file mode 100644 index 000000000..a532df1b6 --- /dev/null +++ b/contrib/depends/patches/zeromq/ffe62d3398d5e0191f554f61049aa7ec9fc892ae.patch @@ -0,0 +1,38 @@ +From ffe62d3398d5e0191f554f61049aa7ec9fc892ae Mon Sep 17 00:00:00 2001 +From: Gregory Lemercier +Date: Sun, 7 Oct 2018 18:06:54 +0200 +Subject: [PATCH] Fix build on arm64 architectures with some strict compilers + +This patch fixes an issue that occurs on 64-bit architetures under +strict compiler rules. The code initially checked that the received +size stored in 'uint64_t' was not bigger than the max value of a +'size_t' variable, which is legitimate on 32-bit architectures where +'size_t' variables are stored on 32 bits. On 64-bit architectures, +this test no longer makes sense since 'uint64_t' and 'size_t' types +have the same size. The issue is fixed by ignoring this portion +of code when built for arm64. +--- + src/v1_decoder.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/v1_decoder.cpp b/src/v1_decoder.cpp +index b002dc9d..2c8c97a7 100644 +--- a/src/v1_decoder.cpp ++++ b/src/v1_decoder.cpp +@@ -114,11 +114,13 @@ int zmq::v1_decoder_t::eight_byte_size_ready () + return -1; + } + ++#ifndef __aarch64__ + // Message size must fit within range of size_t data type. + if (payload_length - 1 > std::numeric_limits ::max ()) { + errno = EMSGSIZE; + return -1; + } ++#endif + + const size_t msg_size = static_cast (payload_length - 1); + +-- +2.20.1 + diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index ebe96b69c..0728515e3 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -1,4 +1,4 @@ -# Set the system name, either Darwin, Linux, or Windows +# Set the system name to one of Android, Darwin, Linux, or Windows SET(CMAKE_SYSTEM_NAME @depends@) SET(CMAKE_BUILD_TYPE @release_type@) @@ -18,13 +18,14 @@ SET(CMAKE_FIND_ROOT_PATH @prefix@ /usr) SET(ENV{PKG_CONFIG_PATH} @prefix@/lib/pkgconfig) -SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE) - SET(Readline_ROOT_DIR @prefix@) SET(Readline_INCLUDE_DIR @prefix@/include) SET(Readline_LIBRARY @prefix@/lib/libreadline.a) SET(Termcap_LIBRARY @prefix@/lib/libtinfo.a) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") +SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE) + SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) @@ -37,6 +38,7 @@ SET(Protobuf_PROTOC_EXECUTABLE @prefix@/native/bin/protoc CACHE FILEPATH "Path t SET(Protobuf_INCLUDE_DIR @prefix@/include CACHE PATH "Protobuf include dir") SET(Protobuf_INCLUDE_DIRS @prefix@/include CACHE PATH "Protobuf include dir") SET(Protobuf_LIBRARY @prefix@/lib/libprotobuf.a CACHE FILEPATH "Protobuf library") +endif() SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) @@ -78,6 +80,22 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") SET(CMAKE_OSX_ARCHITECTURES "x86_64") SET(LLVM_ENABLE_PIC OFF) SET(LLVM_ENABLE_PIE OFF) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") + SET(ANDROID TRUE) + if(ARCHITECTURE STREQUAL "arm") + SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") + SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") + SET(CMAKE_ANDROID_ARM_MODE ON) + SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi) + SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi) + SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi-) + elseif(ARCHITECTURE STREQUAL "aarch64") + SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") + SET(CMAKE_SYSTEM_PROCESSOR "aarch64") + endif() + SET(CMAKE_ANDROID_STANDALONE_TOOLCHAIN @prefix@/native) + SET(CMAKE_C_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang") + SET(CMAKE_CXX_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang++") else() SET(CMAKE_C_COMPILER @CC@) SET(CMAKE_CXX_COMPILER @CXX@) @@ -89,13 +107,21 @@ if(ARCHITECTURE STREQUAL "arm") set(ARM_ID "armv7-a") set(BUILD_64 OFF) set(CMAKE_BUILD_TYPE release) - set(BUILD_TAG "linux-armv7") + if(ANDROID) + set(BUILD_TAG "android-armv7") + else() + set(BUILD_TAG "linux-armv7") + endif() set(ARM7) elseif(ARCHITECTURE STREQUAL "aarch64") set(ARCH "armv8-a") set(ARM ON) set(ARM_ID "armv8-a") - set(BUILD_TAG "linux-armv8") + if(ANDROID) + set(BUILD_TAG "android-armv8") + else() + set(BUILD_TAG "linux-armv8") + endif() set(BUILD_64 ON) endif() diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml new file mode 100644 index 000000000..0de588409 --- /dev/null +++ b/contrib/gitian/gitian-android.yml @@ -0,0 +1,135 @@ +--- +name: "monero-android-0.15" +enable_cache: true +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "curl" +- "gperf" +- "gcc-7" +- "g++-7" +- "gcc" +- "g++" +- "binutils-gold" +- "git" +- "pkg-config" +- "build-essential" +- "autoconf" +- "libtool" +- "automake" +- "faketime" +- "bsdmainutils" +- "ca-certificates" +- "python" +- "cmake" +- "ccache" +- "protobuf-compiler" +- "libdbus-1-dev" +- "libharfbuzz-dev" +- "libprotobuf-dev" +- "python3-zmq" +- "unzip" +remotes: +- "url": "https://github.com/monero-project/monero.git" + "dir": "monero" +files: [] +script: | + + WRAP_DIR=$HOME/wrapped + HOSTS="arm-linux-android aarch64-linux-android" + FAKETIME_HOST_PROGS="clang clang++ ar ranlib nm" + FAKETIME_PROGS="date" + HOST_CFLAGS="-O2 -g" + HOST_CXXFLAGS="-O2 -g" + HOST_LDFLAGS=-static-libstdc++ + + export GZIP="-9n" + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export TZ="UTC" + export BUILD_DIR=`pwd` + mkdir -p ${WRAP_DIR} + if test -n "$GBUILD_CACHE_ENABLED"; then + export SOURCES_PATH=${GBUILD_COMMON_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + fi + + export ZERO_AR_DATE=1 + + function create_global_faketime_wrappers { + for prog in ${FAKETIME_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} + echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${prog} + chmod +x ${WRAP_DIR}/${prog} + done + } + + function create_per-host_faketime_wrappers { + for i in $HOSTS; do + ABI=$i + if expr $i : arm- > /dev/null + then + ABI=$i"eabi" + fi + NDKDIR="${BUILD_DIR}/monero/contrib/depends/$i/native/bin" + for prog in ${FAKETIME_HOST_PROGS}; do + WRAPPER=${WRAP_DIR}/${ABI}-${prog} + echo '#!/usr/bin/env bash' > ${WRAPPER} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAPPER} + echo "export FAKETIME=\"$1\"" >> ${WRAPPER} + echo "$NDKDIR/${ABI}-$prog \$@" >> ${WRAPPER} + chmod +x ${WRAPPER} + done + done + } + + # Faketime for depends so intermediate results are comparable + DUMMYTIME="2000-01-01 12:00:00" + export PATH_orig=${PATH} + create_global_faketime_wrappers "$DUMMYTIME" + create_per-host_faketime_wrappers "$DUMMYTIME" + export PATH=${WRAP_DIR}:${PATH} + + # gcc 7+ honors SOURCE_DATE_EPOCH, no faketime needed + export SOURCE_DATE_EPOCH=`date -d "$DUMMYTIME" +%s` + + git config --global core.abbrev 9 + cd monero + # Set the version string that gets added to the tar archive name + version="`git describe`" + if [[ $version == *"-"*"-"* ]]; then + version="`git rev-parse --short=9 HEAD`" + version="`echo $version | head -c 9`" + fi + + BASEPREFIX=`pwd`/contrib/depends + # Build dependencies for each host + for i in $HOSTS; do + make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" + done + + # Faketime for binaries + export PATH=${PATH_orig} + create_global_faketime_wrappers "${REFERENCE_DATETIME}" + create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" + export PATH=${WRAP_DIR}:${PATH} + + ORIGPATH="$PATH" + # Build in a new dir for each host + for i in ${HOSTS}; do + export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + mkdir build && cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DCMAKE_BUILD_TYPE=Release + make ${MAKEOPTS} + DISTNAME=monero-${i}-${version} + mv bin ${DISTNAME} + find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 + cd .. + rm -rf build + done + diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index a8d164c2c..138a5ac4f 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -66,6 +66,12 @@ def build(): subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../sigs/', 'inputs/monero/contrib/gitian/gitian-linux.yml']) subprocess.check_call('mv build/out/monero-*.tar.bz2 ../out/'+args.version, shell=True) + if args.android: + print('\nCompiling ' + args.version + ' Android') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, 'inputs/monero/contrib/gitian/gitian-android.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-android', '--destination', '../sigs/', 'inputs/monero/contrib/gitian/gitian-android.yml']) + subprocess.check_call('mv build/out/monero-*.tar.bz2 ../out/'+args.version, shell=True) + if args.windows: print('\nCompiling ' + args.version + ' Windows') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, 'inputs/monero/contrib/gitian/gitian-win.yml']) @@ -84,6 +90,7 @@ def build(): print('\nCommitting '+args.version+' Unsigned Sigs\n') os.chdir('sigs') subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-android/'+args.signer]) subprocess.check_call(['git', 'add', args.version+'-win/'+args.signer]) subprocess.check_call(['git', 'add', args.version+'-osx/'+args.signer]) subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) @@ -95,6 +102,8 @@ def verify(): print('\nVerifying v'+args.version+' Linux\n') subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-linux', 'inputs/monero/contrib/gitian/gitian-linux.yml']) + print('\nVerifying v'+args.version+' Android\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-android', 'inputs/monero/contrib/gitian/gitian-android.yml']) print('\nVerifying v'+args.version+' Windows\n') subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-win', 'inputs/monero/contrib/gitian/gitian-win.yml']) print('\nVerifying v'+args.version+' MacOS\n') @@ -111,7 +120,7 @@ def main(): parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') - parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS') + parser.add_argument('-o', '--os', dest='os', default='lawm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, a for Android, w for Windows, m for MacOS') parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s') parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC') @@ -127,6 +136,7 @@ def main(): workdir = os.getcwd() args.linux = 'l' in args.os + args.android = 'a' in args.os args.windows = 'w' in args.os args.macos = 'm' in args.os From 240dbb124354677941dc7caa9326386c9b5faa77 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 3 Nov 2019 13:48:55 +0000 Subject: [PATCH 042/469] gitian: add --rebuild option Avoids delays when sourceforge is slow to respond; allows rebuilding when disconnected from networks. --- contrib/gitian/README.md | 16 +++++++++++++ contrib/gitian/gitian-build.py | 44 ++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 3c40d09e0..4fa393283 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -226,3 +226,19 @@ To get all build options run: ./gitian-build.py --help ``` +Doing Successive Builds +----------------------- + +If you need to do multiple iterations (while developing/testing) you can use the +`--rebuild` option instead of `--build` on subsequent iterations. This skips the +initial check for the freshness of the depends tools. In particular, doing this +check all the time prevents rebuilding when you have no network access. + + +Local-Only Builds +----------------- + +If you need to run builds while disconnected from the internet, make sure you have +local up-to-date repos in advance. Then specify your local repo using the `--url` +option when building. This will avoid attempts to git pull across a network. + diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 138a5ac4f..a24c71b4f 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -46,19 +46,11 @@ def setup(): print('Reboot is required') sys.exit(0) -def build(): +def rebuild(): global args, workdir - os.makedirs('out/' + args.version, exist_ok=True) print('\nBuilding Dependencies\n') - os.chdir('builder') - os.makedirs('inputs', exist_ok=True) - - subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) - subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) - subprocess.check_output(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True) - subprocess.check_output(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True) - subprocess.check_call(['make', '-C', 'inputs/monero/contrib/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) + os.makedirs('out/' + args.version, exist_ok=True) if args.linux: print('\nCompiling ' + args.version + ' Linux') @@ -96,6 +88,23 @@ def build(): subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) os.chdir(workdir) + +def build(): + global args, workdir + + print('\nChecking Depends Freshness\n') + os.chdir('builder') + os.makedirs('inputs', exist_ok=True) + + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) + subprocess.check_output(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True) + subprocess.check_output(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True) + subprocess.check_call(['make', '-C', 'inputs/monero/contrib/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) + + rebuild() + + def verify(): global args, workdir os.chdir('builder') @@ -121,6 +130,8 @@ def main(): parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') parser.add_argument('-o', '--os', dest='os', default='lawm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, a for Android, w for Windows, m for MacOS') + parser.add_argument('-r', '--rebuild', action='store_true', dest='rebuild', help='Redo a Gitian build') + parser.add_argument('-R', '--rebuildsign', action='store_true', dest='rebuildsign', help='Redo and sign a Gitian build') parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s') parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC') @@ -146,6 +157,10 @@ def main(): args.build = True args.sign = True + if args.rebuildsign: + args.rebuild = True + args.sign = True + if args.kvm and args.docker: raise Exception('Error: cannot have both kvm and docker') @@ -162,9 +177,10 @@ def main(): os.environ['LXC_GUEST_IP'] = '10.0.3.5' # Disable MacOS build if no SDK found - if args.build and args.macos and not os.path.isfile('builder/inputs/MacOSX10.11.sdk.tar.gz'): - print('Cannot build for MacOS, SDK does not exist. Will build for other OSes') + if args.macos and not os.path.isfile('builder/inputs/MacOSX10.11.sdk.tar.gz'): args.macos = False + if args.build: + print('Cannot build for MacOS, SDK does not exist. Will build for other OSes') script_name = os.path.basename(sys.argv[0]) # Signer and version shouldn't be empty @@ -199,6 +215,10 @@ def main(): if args.build: build() + if args.rebuild: + os.chdir('builder') + rebuild() + if args.verify: verify() From 2bfd41b29c865165ffe10ed21fddfb35a6d66b3c Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 4 Nov 2019 00:08:00 +0100 Subject: [PATCH 043/469] depends: fix typo in packages --- contrib/depends/packages/packages.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 2d8030448..9302d5b4d 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,7 +1,7 @@ ifeq ($(host_os),android) packages:=boost openssl zeromq libiconv else -packages:=boost openssl zeromq lexpat dns libiconv hidapi protobuf libusb +packages:=boost openssl zeromq expat ldns libiconv hidapi protobuf libusb endif native_packages := native_ccache native_protobuf From a9bdc6e4c4e9ec73226e5b4d2261f54bb0b14cf1 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Mon, 4 Nov 2019 01:06:01 +0000 Subject: [PATCH 044/469] Improved performance for epee serialization: - Removed copy of field names in binary deserialization - Removed copy of array values in binary deserialization - Removed copy of string values in json deserialization - Removed unhelpful allocation in json string value parsing - Removed copy of blob data on binary and json serialization --- .../keyvalue_serialization_overloads.h | 24 +++------ .../include/storages/parserse_base_utils.h | 1 - .../epee/include/storages/portable_storage.h | 52 +++++++++++-------- .../include/storages/portable_storage_base.h | 8 +-- .../storages/portable_storage_from_bin.h | 6 +-- .../storages/portable_storage_from_json.h | 18 +++---- tests/unit_tests/net.cpp | 6 +-- 7 files changed, 56 insertions(+), 59 deletions(-) diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index fc8b90a2c..1f9d6b6d7 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -46,24 +46,12 @@ namespace epee namespace serialization { - //------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_t_val(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return stg.set_value(pname, d, hparent_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_t_val(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return stg.get_value(pname, d, hparent_section); - } //------------------------------------------------------------------------------------------------------------------- template static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { std::string blob((const char *)&d, sizeof(d)); - return stg.set_value(pname, blob, hparent_section); + return stg.set_value(pname, std::move(blob), hparent_section); } //------------------------------------------------------------------------------------------------------------------- template @@ -114,13 +102,15 @@ namespace epee template static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { + using value_type = typename stl_container::value_type; + if(!container.size()) return true; typename stl_container::const_iterator it = container.begin(); - typename t_storage::harray hval_array = stg.insert_first_value(pname, *it, hparent_section); + typename t_storage::harray hval_array = stg.insert_first_value(pname, value_type(*it), hparent_section); CHECK_AND_ASSERT_MES(hval_array, false, "failed to insert first value to storage"); it++; for(;it!= container.end();it++) - stg.insert_next_value(hval_array, *it); + stg.insert_next_value(hval_array, value_type(*it)); return true; } @@ -149,7 +139,7 @@ namespace epee *p_elem = v; p_elem++; } - return stg.set_value(pname, mb, hparent_section); + return stg.set_value(pname, std::move(mb), hparent_section); } //-------------------------------------------------------------------------------------------------------------------- template @@ -221,7 +211,7 @@ namespace epee template static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) { - return stg.set_value(pname, d, hparent_section); + return stg.set_value(pname, t_type(d), hparent_section); } //------------------------------------------------------------------------------------------------------------------- template diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index fe53628a5..8a498130c 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -157,7 +157,6 @@ namespace misc_utils while (fi != buf_end && ((lut[(uint8_t)*fi] & 32)) == 0) ++fi; val.assign(it, fi); - val.reserve(std::distance(star_end_string, buf_end)); it = fi; for(;it != buf_end;it++) { diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index d0e40d606..4b759a24f 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -28,6 +28,8 @@ #pragma once +#include + #include "misc_language.h" #include "portable_storage_base.h" #include "portable_storage_to_bin.h" @@ -59,7 +61,7 @@ namespace epee bool get_value(const std::string& value_name, t_value& val, hsection hparent_section); bool get_value(const std::string& value_name, storage_entry& val, hsection hparent_section); template - bool set_value(const std::string& value_name, const t_value& target, hsection hparent_section); + bool set_value(const std::string& value_name, t_value&& target, hsection hparent_section); //serial access for arrays of values -------------------------------------- //values @@ -68,9 +70,9 @@ namespace epee template bool get_next_value(harray hval_array, t_value& target); template - harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section); + harray insert_first_value(const std::string& value_name, t_value&& target, hsection hparent_section); template - bool insert_next_value(harray hval_array, const t_value& target); + bool insert_next_value(harray hval_array, t_value&& target); //sections harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); bool get_next_section(harray hSecArray, hsection& h_child_section); @@ -94,7 +96,7 @@ namespace epee hsection get_root_section() {return &m_root;} storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection); template - storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry); + storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, entry_type&& entry); hsection insert_new_section(const std::string& pentry_name, hsection psection); @@ -241,21 +243,22 @@ namespace epee } //--------------------------------------------------------------------------------------------------------------- template - bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section) + bool portable_storage::set_value(const std::string& value_name, t_value&& v, hsection hparent_section) { - BOOST_MPL_ASSERT(( boost::mpl::contains::type, t_value> )); + using t_real_value = typename std::decay::type; + BOOST_MPL_ASSERT(( boost::mpl::contains::type, t_real_value> )); TRY_ENTRY(); if(!hparent_section) hparent_section = &m_root; storage_entry* pentry = find_storage_entry(value_name, hparent_section); if(!pentry) { - pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, v); + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, std::forward(v)); if(!pentry) return false; return true; } - *pentry = storage_entry(v); + *pentry = std::forward(v); return true; CATCH_ENTRY("portable_storage::template<>set_value", false); } @@ -274,11 +277,12 @@ namespace epee } //--------------------------------------------------------------------------------------------------------------- template - storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry) + storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, entry_type&& entry) { + static_assert(std::is_rvalue_reference(), "unexpected copy of value"); TRY_ENTRY(); CHECK_AND_ASSERT(psection, nullptr); - auto ins_res = psection->m_entries.insert(std::pair(pentry_name, entry)); + auto ins_res = psection->m_entries.emplace(pentry_name, std::forward(entry)); return &ins_res.first->second; CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); } @@ -362,41 +366,45 @@ namespace epee } //--------------------------------------------------------------------------------------------------------------- template - harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section) + harray portable_storage::insert_first_value(const std::string& value_name, t_value&& target, hsection hparent_section) { + using t_real_value = typename std::decay::type; + static_assert(std::is_rvalue_reference(), "unexpected copy of value"); TRY_ENTRY(); if(!hparent_section) hparent_section = &m_root; storage_entry* pentry = find_storage_entry(value_name, hparent_section); if(!pentry) { - pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t())); + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t())); if(!pentry) return nullptr; } if(pentry->type() != typeid(array_entry)) - *pentry = storage_entry(array_entry(array_entry_t())); + *pentry = storage_entry(array_entry(array_entry_t())); array_entry& arr = boost::get(*pentry); - if(arr.type() != typeid(array_entry_t)) - arr = array_entry(array_entry_t()); + if(arr.type() != typeid(array_entry_t)) + arr = array_entry(array_entry_t()); - array_entry_t& arr_typed = boost::get >(arr); - arr_typed.insert_first_val(target); + array_entry_t& arr_typed = boost::get >(arr); + arr_typed.insert_first_val(std::forward(target)); return &arr; CATCH_ENTRY("portable_storage::insert_first_value", nullptr); } //--------------------------------------------------------------------------------------------------------------- template - bool portable_storage::insert_next_value(harray hval_array, const t_value& target) + bool portable_storage::insert_next_value(harray hval_array, t_value&& target) { + using t_real_value = typename std::decay::type; + static_assert(std::is_rvalue_reference(), "unexpected copy of value"); TRY_ENTRY(); CHECK_AND_ASSERT(hval_array, false); - CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t), - false, "unexpected type in insert_next_value: " << typeid(array_entry_t).name()); + CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t), + false, "unexpected type in insert_next_value: " << typeid(array_entry_t).name()); - array_entry_t& arr_typed = boost::get >(*hval_array); - arr_typed.insert_next_value(target); + array_entry_t& arr_typed = boost::get >(*hval_array); + arr_typed.insert_next_value(std::forward(target)); return true; CATCH_ENTRY("portable_storage::insert_next_value", false); } diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h index ca7c81ddc..40c7524fb 100644 --- a/contrib/epee/include/storages/portable_storage_base.h +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -111,16 +111,16 @@ namespace epee return (t_entry_type*)&(*(m_it++));//fuckoff } - t_entry_type& insert_first_val(const t_entry_type& v) + t_entry_type& insert_first_val(t_entry_type&& v) { m_array.clear(); m_it = m_array.end(); - return insert_next_value(v); + return insert_next_value(std::move(v)); } - t_entry_type& insert_next_value(const t_entry_type& v) + t_entry_type& insert_next_value(t_entry_type&& v) { - m_array.push_back(v); + m_array.push_back(std::move(v)); return m_array.back(); } diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index c0b6cc7b1..b39dc7c92 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -143,7 +143,7 @@ namespace epee //TODO: add some optimization here later while(size--) sa.m_array.push_back(read()); - return storage_entry(array_entry(sa)); + return storage_entry(array_entry(std::move(sa))); } inline @@ -213,7 +213,7 @@ namespace epee { RECURSION_LIMITATION(); section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio - storage_entry se(s); + storage_entry se(std::move(s)); section& section_entry = boost::get

(se); read(section_entry); return se; @@ -268,7 +268,7 @@ namespace epee //read section name string std::string sec_name; read_sec_name(sec_name); - sec.m_entries.insert(std::make_pair(sec_name, load_storage_entry())); + sec.m_entries.emplace(std::move(sec_name), load_storage_entry()); } } inline diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h index 3e3052541..2b2dc7ff9 100644 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -128,20 +128,20 @@ namespace epee errno = 0; int64_t nval = strtoll(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - stg.set_value(name, nval, current_section); + stg.set_value(name, int64_t(nval), current_section); }else { errno = 0; uint64_t nval = strtoull(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - stg.set_value(name, nval, current_section); + stg.set_value(name, uint64_t(nval), current_section); } }else { errno = 0; double nval = strtod(val.data(), NULL); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - stg.set_value(name, nval, current_section); + stg.set_value(name, double(nval), current_section); } state = match_state_wonder_after_value; }else if(isalpha(*it) ) @@ -219,13 +219,13 @@ namespace epee errno = 0; int64_t nval = strtoll(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - h_array = stg.insert_first_value(name, nval, current_section); + h_array = stg.insert_first_value(name, int64_t(nval), current_section); }else { errno = 0; uint64_t nval = strtoull(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - h_array = stg.insert_first_value(name, nval, current_section); + h_array = stg.insert_first_value(name, uint64_t(nval), current_section); } CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); }else @@ -233,7 +233,7 @@ namespace epee errno = 0; double nval = strtod(val.data(), NULL); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - h_array = stg.insert_first_value(name, nval, current_section); + h_array = stg.insert_first_value(name, double(nval), current_section); CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); } @@ -310,20 +310,20 @@ namespace epee errno = 0; int64_t nval = strtoll(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - insert_res = stg.insert_next_value(h_array, nval); + insert_res = stg.insert_next_value(h_array, int64_t(nval)); }else { errno = 0; uint64_t nval = strtoull(val.data(), NULL, 10); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - insert_res = stg.insert_next_value(h_array, nval); + insert_res = stg.insert_next_value(h_array, uint64_t(nval)); } }else { errno = 0; double nval = strtod(val.data(), NULL); if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); - insert_res = stg.insert_next_value(h_array, nval); + insert_res = stg.insert_next_value(h_array, double(nval)); } CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); state = match_state_array_after_value; diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index 262541bd2..221dc631d 100644 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -271,7 +271,7 @@ TEST(tor_address, epee_serializev_v2) EXPECT_EQ(std::strlen(v2_onion), host.size()); host.push_back('k'); - EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false))); + EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false))); EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` } @@ -322,7 +322,7 @@ TEST(tor_address, epee_serializev_v3) EXPECT_EQ(std::strlen(v3_onion), host.size()); host.push_back('k'); - EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false))); + EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false))); EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` } @@ -373,7 +373,7 @@ TEST(tor_address, epee_serialize_unknown) EXPECT_EQ(std::strlen(net::tor_address::unknown_str()), host.size()); host.push_back('k'); - EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false))); + EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false))); EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` } From 5b78c27c5023764205d51194be9902d00dbe4886 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Mon, 4 Nov 2019 05:05:38 +0000 Subject: [PATCH 045/469] Copy LICENSE to all archives --- contrib/gitian/gitian-android.yml | 5 ++++- contrib/gitian/gitian-linux.yml | 5 ++++- contrib/gitian/gitian-osx.yml | 5 ++++- contrib/gitian/gitian-win.yml | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml index 0de588409..ce213e056 100644 --- a/contrib/gitian/gitian-android.yml +++ b/contrib/gitian/gitian-android.yml @@ -126,9 +126,12 @@ script: | mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DCMAKE_BUILD_TYPE=Release make ${MAKEOPTS} + chmod 755 bin/* + cp ../LICENSE bin + chmod 644 bin/LICENSE DISTNAME=monero-${i}-${version} mv bin ${DISTNAME} - find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 + find ${DISTNAME}/ | sort | tar --no-recursion --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 cd .. rm -rf build done diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 8a338ba04..d2124e45c 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -162,9 +162,12 @@ script: | mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON make ${MAKEOPTS} + chmod 755 bin/* + cp ../LICENSE bin + chmod 644 bin/LICENSE DISTNAME=monero-${i}-${version} mv bin ${DISTNAME} - find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 + find ${DISTNAME}/ | sort | tar --no-recursion --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 cd .. rm -rf build done diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index e712e4195..1f9928bb5 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -109,9 +109,12 @@ script: | mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake make ${MAKEOPTS} + chmod 755 bin/* + cp ../LICENSE bin + chmod 644 bin/LICENSE DISTNAME=monero-${i}-${version} mv bin ${DISTNAME} - find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 + find ${DISTNAME}/ | sort | tar --no-recursion --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 cd .. rm -rf build done diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index 7b2b4373f..7bfb14a95 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -134,6 +134,7 @@ script: | mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake make ${MAKEOPTS} + cp ../LICENSE bin DISTNAME=monero-${i}-${version} mv bin ${DISTNAME} find ${DISTNAME}/ | sort | zip -X@ ${OUTDIR}/${DISTNAME}.zip From 70c9cd3c9cd052716c309a0bd9b77009318f4131 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Sun, 27 Oct 2019 22:32:04 +0000 Subject: [PATCH 046/469] Change to Tx diffusion (Dandelion++ fluff) instead of flooding --- src/blockchain_db/blockchain_db.cpp | 6 +- src/blockchain_db/blockchain_db.h | 2 +- src/cryptonote_config.h | 3 + src/cryptonote_core/cryptonote_core.cpp | 12 +- src/cryptonote_core/cryptonote_core.h | 8 - .../cryptonote_protocol_handler.inl | 4 +- src/cryptonote_protocol/enums.h | 2 +- src/cryptonote_protocol/levin_notify.cpp | 192 ++++++++++++++---- src/cryptonote_protocol/levin_notify.h | 12 +- src/p2p/net_node.cpp | 4 + src/p2p/net_node.h | 14 +- src/p2p/net_node.inl | 14 +- src/p2p/net_node_common.h | 4 +- tests/core_tests/chaingen.h | 4 +- tests/unit_tests/levin.cpp | 96 +++++++-- 15 files changed, 280 insertions(+), 97 deletions(-) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 5fec22d8d..1a6a19da5 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -67,7 +67,7 @@ bool matches_category(relay_method method, relay_category category) noexcept case relay_method::local: return false; case relay_method::block: - case relay_method::flood: + case relay_method::fluff: return true; case relay_method::none: break; @@ -90,7 +90,7 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept is_local = 1; break; default: - case relay_method::flood: + case relay_method::fluff: break; case relay_method::block: kept_by_block = 1; @@ -106,7 +106,7 @@ relay_method txpool_tx_meta_t::get_relay_method() const noexcept return relay_method::none; if (is_local) return relay_method::local; - return relay_method::flood; + return relay_method::fluff; } const command_line::arg_descriptor arg_db_sync_mode = { diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index b2c5d6cb4..acd7976a8 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -108,7 +108,7 @@ extern const command_line::arg_descriptor arg_db_salvage; enum class relay_category : uint8_t { - broadcasted = 0,//!< Public txes received via block/flooding/fluff + broadcasted = 0,//!< Public txes received via block/fluff relayable, //!< Every tx not marked `relay_method::none` legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons all //!< Everything in the db diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index ca127c3ee..134b630f7 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -101,6 +101,9 @@ #define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week + +#define CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE 5 // seconds + // see src/cryptonote_protocol/levin_notify.cpp #define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes #define CRYPTONOTE_NOISE_EPOCH_RANGE 30 // seconds diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index cf23a652c..23f13000c 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -174,11 +174,6 @@ namespace cryptonote , "Relay blocks as normal blocks" , false }; - static const command_line::arg_descriptor arg_pad_transactions = { - "pad-transactions" - , "Pad relayed transactions to help defend against traffic volume analysis" - , false - }; static const command_line::arg_descriptor arg_max_txpool_weight = { "max-txpool-weight" , "Set maximum txpool weight in bytes." @@ -235,8 +230,7 @@ namespace cryptonote m_disable_dns_checkpoints(false), m_update_download(0), m_nettype(UNDEFINED), - m_update_available(false), - m_pad_transactions(false) + m_update_available(false) { m_checkpoints_updating.clear(); set_cryptonote_protocol(pprotocol); @@ -333,7 +327,6 @@ namespace cryptonote command_line::add_arg(desc, arg_block_download_max_size); command_line::add_arg(desc, arg_sync_pruned_blocks); command_line::add_arg(desc, arg_max_txpool_weight); - command_line::add_arg(desc, arg_pad_transactions); command_line::add_arg(desc, arg_block_notify); command_line::add_arg(desc, arg_prune_blockchain); command_line::add_arg(desc, arg_reorg_notify); @@ -376,7 +369,6 @@ namespace cryptonote set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks); - m_pad_transactions = get_arg(vm, arg_pad_transactions); m_offline = get_arg(vm, arg_offline); m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints); if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks)) @@ -1295,7 +1287,7 @@ namespace cryptonote private_req.txs.push_back(std::move(std::get<1>(tx))); break; case relay_method::block: - case relay_method::flood: + case relay_method::fluff: public_req.txs.push_back(std::move(std::get<1>(tx))); break; } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 4b67984ab..55efb566c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -791,13 +791,6 @@ namespace cryptonote */ bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; } - /** - * @brief get whether transaction relay should be padded - * - * @return whether transaction relay should be padded - */ - bool pad_transactions() const { return m_pad_transactions; } - /** * @brief check a set of hashes against the precompiled hash set * @@ -1102,7 +1095,6 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; - bool m_pad_transactions; std::shared_ptr m_block_rate_notify; }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index e20934a25..ae4abeef5 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -910,7 +910,7 @@ namespace cryptonote for (size_t i = 0; i < arg.txs.size(); ++i) { cryptonote::tx_verification_context tvc{}; - m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::flood, true); + m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::fluff, true); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); @@ -2351,7 +2351,7 @@ skip: local mempool before doing the relay. The code was already updating the DB twice on received transactions - it is difficult to workaround this due to the internal design. */ - return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core, m_core.pad_transactions()) != epee::net_utils::zone::invalid; + return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core) != epee::net_utils::zone::invalid; } //------------------------------------------------------------------------------------------------------------------------ template diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h index ad4eedf4c..2ec622d94 100644 --- a/src/cryptonote_protocol/enums.h +++ b/src/cryptonote_protocol/enums.h @@ -38,6 +38,6 @@ namespace cryptonote none = 0, //!< Received via RPC with `do_not_relay` set local, //!< Received via RPC; trying to send over i2p/tor, etc. block, //!< Received in block, takes precedence over others - flood //!< Received/sent over public networks + fluff //!< Received/sent over public networks }; } diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 4b41b5bfc..a0a4bbbb1 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "common/expect.h" #include "common/varint.h" @@ -57,6 +58,37 @@ namespace levin constexpr const std::chrono::seconds noise_min_delay{CRYPTONOTE_NOISE_MIN_DELAY}; constexpr const std::chrono::seconds noise_delay_range{CRYPTONOTE_NOISE_DELAY_RANGE}; + /* A custom duration is used for the poisson distribution because of the + variance. If 5 seconds is given to `std::poisson_distribution`, 95% of + the values fall between 1-9s in 1s increments (not granular enough). If + 5000 milliseconds is given, 95% of the values fall between 4859ms-5141ms + in 1ms increments (not enough time variance). Providing 20 quarter + seconds yields 95% of the values between 3s-7.25s in 1/4s increments. */ + using fluff_stepsize = std::chrono::duration>; + constexpr const std::chrono::seconds fluff_average_in{CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE}; + + /*! Bitcoin Core is using 1/2 average seconds for outgoing connections + compared to incoming. The thinking is that the user controls outgoing + connections (Dandelion++ makes similar assumptions in its stem + algorithm). The randomization yields 95% values between 1s-4s in + 1/4s increments. */ + constexpr const fluff_stepsize fluff_average_out{fluff_stepsize{fluff_average_in} / 2}; + + class random_poisson + { + std::poisson_distribution dist; + public: + explicit random_poisson(fluff_stepsize average) + : dist(average.count() < 0 ? 0 : average.count()) + {} + + fluff_stepsize operator()() + { + crypto::random_device rand{}; + return fluff_stepsize{dist(rand)}; + } + }; + /*! Select a randomized duration from 0 to `range`. The precision will be to the systems `steady_clock`. As an example, supplying 3 seconds to this function will select a duration from [0, 3] seconds, and the increments @@ -129,6 +161,12 @@ namespace levin return fullBlob; } + bool make_payload_send_txs(connections& p2p, std::vector&& txs, const boost::uuids::uuid& destination, const bool pad) + { + const cryptonote::blobdata blob = make_tx_payload(std::move(txs), pad); + return p2p.notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan(blob), destination); + } + /* The current design uses `asio::strand`s. The documentation isn't as clear as it should be - a `strand` has an internal `mutex` and `bool`. The `mutex` synchronizes thread access and the `bool` is set when a thread is @@ -187,15 +225,18 @@ namespace levin { struct zone { - explicit zone(boost::asio::io_service& io_service, std::shared_ptr p2p, epee::byte_slice noise_in, bool is_public) + explicit zone(boost::asio::io_service& io_service, std::shared_ptr p2p, epee::byte_slice noise_in, bool is_public, bool pad_txs) : p2p(std::move(p2p)), noise(std::move(noise_in)), next_epoch(io_service), + flush_txs(io_service), strand(io_service), map(), channels(), + flush_time(std::chrono::steady_clock::time_point::max()), connection_count(0), - is_public(is_public) + is_public(is_public), + pad_txs(pad_txs) { for (std::size_t count = 0; !noise.empty() && count < CRYPTONOTE_NOISE_CHANNELS; ++count) channels.emplace_back(io_service); @@ -204,11 +245,14 @@ namespace levin const std::shared_ptr p2p; const epee::byte_slice noise; //!< `!empty()` means zone is using noise channels boost::asio::steady_timer next_epoch; + boost::asio::steady_timer flush_txs; boost::asio::io_service::strand strand; net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems std::deque channels; //!< Never touch after init; only update elements on `noise_channel.strand` + std::chrono::steady_clock::time_point flush_time; //!< Next expected Dandelion++ fluff flush std::atomic connection_count; //!< Only update in strand, can be read at any time const bool is_public; //!< Zone is public ipv4/ipv6 connections + const bool pad_txs; //!< Pad txs to the next boundary for privacy }; } // detail @@ -245,49 +289,112 @@ namespace levin } }; - //! Sends a message to every active connection - class flood_notify + //! Sends txs on connections with expired timers, and queues callback for next timer expiration (if any). + struct fluff_flush { std::shared_ptr zone_; - epee::byte_slice message_; // Requires manual copy - boost::uuids::uuid source_; + std::chrono::steady_clock::time_point flush_time_; - public: - explicit flood_notify(std::shared_ptr zone, epee::byte_slice message, const boost::uuids::uuid& source) - : zone_(std::move(zone)), message_(message.clone()), source_(source) - {} + static void queue(std::shared_ptr zone, const std::chrono::steady_clock::time_point flush_time) + { + assert(zone != nullptr); + assert(zone->strand.running_in_this_thread()); - flood_notify(flood_notify&&) = default; - flood_notify(const flood_notify& source) - : zone_(source.zone_), message_(source.message_.clone()), source_(source.source_) - {} + detail::zone& this_zone = *zone; + this_zone.flush_time = flush_time; + this_zone.flush_txs.expires_at(flush_time); + this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone), flush_time})); + } - void operator()() const + void operator()(const boost::system::error_code error) { if (!zone_ || !zone_->p2p) return; assert(zone_->strand.running_in_this_thread()); - /* The foreach should be quick, but then it iterates and acquires the - same lock for every connection. So do in a strand because two threads - will ping-pong each other with cacheline invalidations. Revisit if - algorithm changes or the locking strategy within the levin config - class changes. */ + const bool timer_error = bool(error); + if (timer_error) + { + if (error != boost::system::errc::operation_canceled) + throw boost::system::system_error{error, "fluff_flush timer failed"}; - std::vector connections; - connections.reserve(connection_id_reserve_size); - zone_->p2p->foreach_connection([this, &connections] (detail::p2p_context& context) { - /* Only send to outgoing connections when "flooding" over i2p/tor. - Otherwise this makes the tx linkable to a hidden service address, - making things linkable across connections. */ - if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income)) - connections.emplace_back(context.m_connection_id); + // new timer canceled this one set in future + if (zone_->flush_time < flush_time_) + return; + } + + const auto now = std::chrono::steady_clock::now(); + auto next_flush = std::chrono::steady_clock::time_point::max(); + std::vector, boost::uuids::uuid>> connections{}; + zone_->p2p->foreach_connection([timer_error, now, &next_flush, &connections] (detail::p2p_context& context) + { + if (!context.fluff_txs.empty()) + { + if (context.flush_time <= now || timer_error) // flush on canceled timer + { + context.flush_time = std::chrono::steady_clock::time_point::max(); + connections.emplace_back(std::move(context.fluff_txs), context.m_connection_id); + context.fluff_txs.clear(); + } + else // not flushing yet + next_flush = std::min(next_flush, context.flush_time); + } + else // nothing to flush + context.flush_time = std::chrono::steady_clock::time_point::max(); return true; }); - for (const boost::uuids::uuid& connection : connections) - zone_->p2p->send(message_.clone(), connection); + for (auto& connection : connections) + make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs); + + if (next_flush != std::chrono::steady_clock::time_point::max()) + fluff_flush::queue(std::move(zone_), next_flush); + else + zone_->flush_time = next_flush; // signal that no timer is set + } + }; + + /*! The "fluff" portion of the Dandelion++ algorithm. Every tx is queued + per-connection and flushed with a randomized poisson timer. This + implementation only has one system timer per-zone, and instead tracks + the lowest flush time. */ + struct fluff_notify + { + std::shared_ptr zone_; + std::vector txs_; + boost::uuids::uuid source_; + + void operator()() + { + if (!zone_ || !zone_->p2p || txs_.empty()) + return; + + assert(zone_->strand.running_in_this_thread()); + + const auto now = std::chrono::steady_clock::now(); + auto next_flush = std::chrono::steady_clock::time_point::max(); + + random_poisson in_duration(fluff_average_in); + random_poisson out_duration(fluff_average_out); + + zone_->p2p->foreach_connection([this, now, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context) + { + if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income)) + { + if (context.fluff_txs.empty()) + context.flush_time = now + (context.m_is_income ? in_duration() : out_duration()); + + next_flush = std::min(next_flush, context.flush_time); + context.fluff_txs.reserve(context.fluff_txs.size() + this->txs_.size()); + for (const blobdata& tx : this->txs_) + context.fluff_txs.push_back(tx); // must copy instead of move (multiple conns) + } + return true; + }); + + if (next_flush < zone_->flush_time) + fluff_flush::queue(std::move(zone_), next_flush); } }; @@ -451,7 +558,7 @@ namespace levin } }; - //! Prepares connections for new channel epoch and sets timer for next epoch + //! Prepares connections for new channel/dandelionpp epoch and sets timer for next epoch struct start_epoch { // Variables allow for Dandelion++ extension @@ -481,8 +588,8 @@ namespace levin }; } // anonymous - notify::notify(boost::asio::io_service& service, std::shared_ptr p2p, epee::byte_slice noise, bool is_public) - : zone_(std::make_shared(service, std::move(p2p), std::move(noise), is_public)) + notify::notify(boost::asio::io_service& service, std::shared_ptr p2p, epee::byte_slice noise, const bool is_public, const bool pad_txs) + : zone_(std::make_shared(service, std::move(p2p), std::move(noise), is_public, pad_txs)) { if (!zone_->p2p) throw std::logic_error{"cryptonote::levin::notify cannot have nullptr p2p argument"}; @@ -533,8 +640,18 @@ namespace levin channel.next_noise.cancel(); } - bool notify::send_txs(std::vector txs, const boost::uuids::uuid& source, const bool pad_txs) + void notify::run_fluff() { + if (!zone_) + return; + zone_->flush_txs.cancel(); + } + + bool notify::send_txs(std::vector txs, const boost::uuids::uuid& source) + { + if (txs.empty()) + return true; + if (!zone_) return false; @@ -565,12 +682,7 @@ namespace levin } else { - const std::string payload = make_tx_payload(std::move(txs), pad_txs); - epee::byte_slice message = - epee::levin::make_notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan(payload)); - - // traditional monero send technique - zone_->strand.dispatch(flood_notify{zone_, std::move(message), source}); + zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source}); } return true; diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 8bc9b72fa..ce652d933 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -82,7 +82,7 @@ namespace levin {} //! Construct an instance with available notification `zones`. - explicit notify(boost::asio::io_service& service, std::shared_ptr p2p, epee::byte_slice noise, bool is_public); + explicit notify(boost::asio::io_service& service, std::shared_ptr p2p, epee::byte_slice noise, bool is_public, bool pad_txs); notify(const notify&) = delete; notify(notify&&) = default; @@ -104,11 +104,14 @@ namespace levin //! Run the logic for the next stem timeout imemdiately. Only use in testing. void run_stems(); + //! Run the logic for flushing all Dandelion++ fluff queued txs. Only use in testing. + void run_fluff(); + /*! Send txs using `cryptonote_protocol_defs.h` payload format wrapped in a levin header. The message will be sent in a "discreet" manner if enabled - if `!noise.empty()` then the `command`/`payload` will be queued to send at the next available noise interval. Otherwise, a - standard Monero flood notification will be used. + Dandelion++ fluff algorithm will be used. \note Eventually Dandelion++ stem sending will be used here when enabled. @@ -117,12 +120,9 @@ namespace levin \param source The source of the notification. `is_nil()` indicates this node is the source. Dandelion++ will use this to map a source to a particular stem. - \param pad_txs A request to pad txs to help conceal origin via - statistical analysis. Ignored if noise was enabled during - construction. \return True iff the notification is queued for sending. */ - bool send_txs(std::vector txs, const boost::uuids::uuid& source, bool pad_txs); + bool send_txs(std::vector txs, const boost::uuids::uuid& source); }; } // levin } // net diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 58c1717e0..58c5706de 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -161,6 +161,10 @@ namespace nodetool const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN}; const command_line::arg_descriptor arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; + const command_line::arg_descriptor arg_pad_transactions = { + "pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false + }; + boost::optional> get_proxies(boost::program_options::variables_map const& vm) { namespace ip = boost::asio::ip; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index ee70c2806..0e9c1c942 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -109,8 +110,16 @@ namespace nodetool template struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base { - p2p_connection_context_t(): peer_id(0), support_flags(0), m_in_timedsync(false) {} + p2p_connection_context_t() + : fluff_txs(), + flush_time(std::chrono::steady_clock::time_point::max()), + peer_id(0), + support_flags(0), + m_in_timedsync(false) + {} + std::vector fluff_txs; + std::chrono::steady_clock::time_point flush_time; peerid_type peer_id; uint32_t support_flags; bool m_in_timedsync; @@ -337,7 +346,7 @@ namespace nodetool virtual void callback(p2p_connection_context& context); //----------------- i_p2p_endpoint ------------------------------------------------------------- virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections); - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs); + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core); virtual bool invoke_command_to_peer(int command, const epee::span req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); virtual bool invoke_notify_to_peer(int command, const epee::span req_buff, const epee::net_utils::connection_context_base& context); virtual bool drop_connection(const epee::net_utils::connection_context_base& context); @@ -540,6 +549,7 @@ namespace nodetool extern const command_line::arg_descriptor arg_limit_rate_up; extern const command_line::arg_descriptor arg_limit_rate_down; extern const command_line::arg_descriptor arg_limit_rate; + extern const command_line::arg_descriptor arg_pad_transactions; } POP_WARNINGS diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 45bb10593..5dab251b5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -116,6 +116,7 @@ namespace nodetool command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); command_line::add_arg(desc, arg_limit_rate); + command_line::add_arg(desc, arg_pad_transactions); } //----------------------------------------------------------------------------------- template @@ -340,6 +341,7 @@ namespace nodetool { bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + const bool pad_txs = command_line::get_arg(vm, arg_pad_transactions); m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET; network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_]; @@ -384,7 +386,7 @@ namespace nodetool m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6); m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4); public_zone.m_notifier = cryptonote::levin::notify{ - public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true + public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true, pad_txs }; if (command_line::has_arg(vm, arg_p2p_add_peer)) @@ -495,7 +497,7 @@ namespace nodetool } zone.m_notifier = cryptonote::levin::notify{ - zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false + zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false, pad_txs }; } @@ -2053,18 +2055,18 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - epee::net_utils::zone node_server::send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs) + epee::net_utils::zone node_server::send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core) { namespace enet = epee::net_utils; - const auto send = [&txs, &source, &core, pad_txs] (std::pair& network) + const auto send = [&txs, &source, &core] (std::pair& network) { const bool is_public = (network.first == enet::zone::public_); const cryptonote::relay_method tx_relay = is_public ? - cryptonote::relay_method::flood : cryptonote::relay_method::local; + cryptonote::relay_method::fluff : cryptonote::relay_method::local; core.on_transactions_relayed(epee::to_span(txs), tx_relay); - if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || !is_public))) + if (network.second.m_notifier.send_txs(std::move(txs), source)) return network.first; return enet::zone::invalid; }; diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index aa1b83b83..ed88aa28c 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -50,7 +50,7 @@ namespace nodetool struct i_p2p_endpoint { virtual bool relay_notify_to_list(int command, const epee::span data_buff, std::vector> connections)=0; - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs)=0; + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)=0; virtual bool invoke_command_to_peer(int command, const epee::span req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool invoke_notify_to_peer(int command, const epee::span req_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; @@ -75,7 +75,7 @@ namespace nodetool { return false; } - virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs) + virtual epee::net_utils::zone send_txs(std::vector txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core) { return epee::net_utils::zone::invalid; } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index bc0e61365..1594d7a08 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -511,7 +511,7 @@ public: , m_events(events) , m_validator(validator) , m_ev_index(0) - , m_tx_relay(cryptonote::relay_method::flood) + , m_tx_relay(cryptonote::relay_method::fluff) { } @@ -544,7 +544,7 @@ public: } else { - m_tx_relay = cryptonote::relay_method::flood; + m_tx_relay = cryptonote::relay_method::fluff; } return true; diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp index e5ca4e41e..38707f075 100644 --- a/tests/unit_tests/levin.cpp +++ b/tests/unit_tests/levin.cpp @@ -271,12 +271,12 @@ namespace EXPECT_EQ(connection_ids_.size(), connections_->get_connections_count()); } - cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public) + cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public, bool pad_txs) { epee::byte_slice noise = nullptr; if (noise_size) noise = epee::levin::make_noise_notify(noise_size); - return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public}; + return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public, pad_txs}; } boost::uuids::random_generator random_generator_; @@ -434,12 +434,16 @@ TEST_F(levin_notify, defaulted) EXPECT_FALSE(status.has_noise); EXPECT_FALSE(status.connections_filled); } - EXPECT_FALSE(notifier.send_txs({}, random_generator_(), false)); + EXPECT_TRUE(notifier.send_txs({}, random_generator_())); + + std::vector txs(2); + txs[0].resize(100, 'e'); + EXPECT_FALSE(notifier.send_txs(std::move(txs), random_generator_())); } -TEST_F(levin_notify, flood) +TEST_F(levin_notify, fluff_without_padding) { - cryptonote::levin::notify notifier = make_notifier(0, true); + cryptonote::levin::notify notifier = make_notifier(0, true, false); for (unsigned count = 0; count < 10; ++count) add_connection(count % 2 == 0); @@ -464,10 +468,13 @@ TEST_F(levin_notify, flood) ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false)); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) EXPECT_EQ(1u, context->process_send_queue()); @@ -480,14 +487,42 @@ TEST_F(levin_notify, flood) EXPECT_TRUE(notification._.empty()); } } +} + +TEST_F(levin_notify, fluff_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); // not tracked + } + + std::vector txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true)); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) EXPECT_EQ(1u, context->process_send_queue()); @@ -502,9 +537,9 @@ TEST_F(levin_notify, flood) } } -TEST_F(levin_notify, private_flood) +TEST_F(levin_notify, private_fluff_without_padding) { - cryptonote::levin::notify notifier = make_notifier(0, false); + cryptonote::levin::notify notifier = make_notifier(0, false, false); for (unsigned count = 0; count < 10; ++count) add_connection(count % 2 == 0); @@ -529,10 +564,14 @@ TEST_F(levin_notify, private_flood) ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false)); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) { @@ -548,14 +587,43 @@ TEST_F(levin_notify, private_flood) EXPECT_TRUE(notification._.empty()); } } +} + +TEST_F(levin_notify, private_fluff_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, false, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); // not tracked + } + + std::vector txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); ASSERT_EQ(10u, contexts_.size()); { auto context = contexts_.begin(); - EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true)); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id())); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) { @@ -582,7 +650,7 @@ TEST_F(levin_notify, noise) txs[0].resize(1900, 'h'); const boost::uuids::uuid incoming_id = random_generator_(); - cryptonote::levin::notify notifier = make_notifier(2048, false); + cryptonote::levin::notify notifier = make_notifier(2048, false, true); { const auto status = notifier.get_status(); @@ -608,7 +676,7 @@ TEST_F(levin_notify, noise) EXPECT_EQ(0u, receiver_.notified_size()); } - EXPECT_TRUE(notifier.send_txs(txs, incoming_id, false)); + EXPECT_TRUE(notifier.send_txs(txs, incoming_id)); notifier.run_stems(); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); @@ -627,7 +695,7 @@ TEST_F(levin_notify, noise) } txs[0].resize(3000, 'r'); - EXPECT_TRUE(notifier.send_txs(txs, incoming_id, true)); + EXPECT_TRUE(notifier.send_txs(txs, incoming_id)); notifier.run_stems(); io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); From e8c5ab515569676fac1408dd1532de623c1092f4 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Mon, 4 Nov 2019 02:08:10 +0100 Subject: [PATCH 047/469] gitian: fix out dir location --- contrib/gitian/gitian-build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index a24c71b4f..b60cc9309 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -50,7 +50,7 @@ def rebuild(): global args, workdir print('\nBuilding Dependencies\n') - os.makedirs('out/' + args.version, exist_ok=True) + os.makedirs('../out/' + args.version, exist_ok=True) if args.linux: print('\nCompiling ' + args.version + ' Linux') From 2e58eb5cdb3837effff95ac1a84770433d43bac6 Mon Sep 17 00:00:00 2001 From: Nathan Dorfman Date: Mon, 4 Nov 2019 02:28:45 -0700 Subject: [PATCH 048/469] lmdb: Remove meaningless const qualifier on function type --- src/lmdb/table.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lmdb/table.h b/src/lmdb/table.h index 41a3de296..4ded4ba54 100644 --- a/src/lmdb/table.h +++ b/src/lmdb/table.h @@ -15,8 +15,8 @@ namespace lmdb { char const* const name; const unsigned flags; - MDB_cmp_func const* const key_cmp; - MDB_cmp_func const* const value_cmp; + MDB_cmp_func* const key_cmp; + MDB_cmp_func* const value_cmp; //! \pre `name != nullptr` \return Open table. expect open(MDB_txn& write_txn) const noexcept; From 236d2a88e25f50e2249323d1dc71762f12a10364 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 4 Nov 2019 15:08:41 +0000 Subject: [PATCH 049/469] blockchain_stats: make it work on pruned blockchains It reports the actual size as pruned, however --- src/blockchain_utilities/blockchain_stats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index 2f66d54aa..0d18b8819 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -264,12 +264,12 @@ skip: { throw std::runtime_error("Aborting: tx == null_hash"); } - if (!db->get_tx_blob(tx_id, bd)) + if (!db->get_pruned_tx_blob(tx_id, bd)) { throw std::runtime_error("Aborting: tx not found"); } transaction tx; - if (!parse_and_validate_tx_from_blob(bd, tx)) + if (!parse_and_validate_tx_base_from_blob(bd, tx)) { LOG_PRINT_L0("Bad txn from db"); return 1; From 7ba31191f35a3e9826c57360ecd418f5d87878d6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 4 Nov 2019 15:49:21 +0000 Subject: [PATCH 050/469] daemon: add +meta print_tx parameter prints size, weight and (if mined) height --- src/daemon/command_parser_executor.cpp | 9 ++++++--- src/daemon/rpc_command_executor.cpp | 24 ++++++++++++++++++++++++ src/daemon/rpc_command_executor.h | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index b827221f6..2c472f1c7 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -244,12 +244,15 @@ bool t_command_parser_executor::print_block(const std::vector& args bool t_command_parser_executor::print_transaction(const std::vector& args) { + bool include_metadata = false; bool include_hex = false; bool include_json = false; // Assumes that optional flags come after mandatory argument for (unsigned int i = 1; i < args.size(); ++i) { - if (args[i] == "+hex") + if (args[i] == "+meta") + include_metadata = true; + else if (args[i] == "+hex") include_hex = true; else if (args[i] == "+json") include_json = true; @@ -261,7 +264,7 @@ bool t_command_parser_executor::print_transaction(const std::vector } if (args.empty()) { - std::cout << "expected: print_tx [+hex] [+json]" << std::endl; + std::cout << "expected: print_tx [+meta] [+hex] [+json]" << std::endl; return true; } @@ -269,7 +272,7 @@ bool t_command_parser_executor::print_transaction(const std::vector crypto::hash tx_hash; if (parse_hash256(str_hash, tx_hash)) { - m_executor.print_transaction(tx_hash, include_hex, include_json); + m_executor.print_transaction(tx_hash, include_metadata, include_hex, include_json); } return true; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..d4f4d4c7c 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -939,6 +939,7 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height, bool include } bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash, + bool include_metadata, bool include_hex, bool include_json) { cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; @@ -981,6 +982,29 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash, const std::string &as_hex = (1 == res.txs.size()) ? res.txs.front().as_hex : res.txs_as_hex.front(); const std::string &pruned_as_hex = (1 == res.txs.size()) ? res.txs.front().pruned_as_hex : ""; const std::string &prunable_as_hex = (1 == res.txs.size()) ? res.txs.front().prunable_as_hex : ""; + // Print metadata if requested + if (include_metadata) + { + if (!res.txs.front().in_pool) + { + tools::msg_writer() << "Block timestamp: " << res.txs.front().block_timestamp << " (" << tools::get_human_readable_timestamp(res.txs.front().block_timestamp) << ")"; + } + cryptonote::blobdata blob; + if (epee::string_tools::parse_hexstr_to_binbuff(pruned_as_hex + prunable_as_hex, blob)) + { + cryptonote::transaction tx; + if (cryptonote::parse_and_validate_tx_from_blob(blob, tx)) + { + tools::msg_writer() << "Size: " << blob.size(); + tools::msg_writer() << "Weight: " << cryptonote::get_transaction_weight(tx); + } + else + tools::fail_msg_writer() << "Error parsing transaction blob"; + } + else + tools::fail_msg_writer() << "Error parsing transaction from hex"; + } + // Print raw hex if requested if (include_hex) { diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index e8b12cb9b..39e1fe122 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -97,7 +97,7 @@ public: bool print_block_by_height(uint64_t height, bool include_hex); - bool print_transaction(crypto::hash transaction_hash, bool include_hex, bool include_json); + bool print_transaction(crypto::hash transaction_hash, bool include_metadata, bool include_hex, bool include_json); bool is_key_image_spent(const crypto::key_image &ki); From a633f85da905f737e024b29f0f3d72a2d68ae1f4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 4 Nov 2019 17:36:46 +0000 Subject: [PATCH 051/469] daemon: allow printing N blocks from the end of the chain It's a very common usage (for my anyway) and avoids the need to get the current height, paste, subtract one, etc --- src/daemon/command_parser_executor.cpp | 10 ++++++++ src/daemon/rpc_command_executor.cpp | 35 +++++++++++++++++++++++--- src/daemon/rpc_command_executor.h | 2 +- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index b827221f6..967bd7278 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -154,6 +154,16 @@ bool t_command_parser_executor::print_blockchain_info(const std::vectorrpc_request(ireq, ires, "/getinfo", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, ires.status); + return true; + } + } + if (start_block_index < 0 && (uint64_t)-start_block_index >= ires.height) + { + tools::fail_msg_writer() << "start offset is larger than blockchain height"; + return true; + } + start_block_index = ires.height + start_block_index; + end_block_index = start_block_index + end_block_index - 1; + } req.start_height = start_block_index; req.end_height = end_block_index; req.fill_pow_hash = false; - std::string fail_message = "Unsuccessful"; - + fail_message = "Failed calling getblockheadersrange"; if (m_is_rpc) { if (!m_rpc_client->json_rpc_request(req, res, "getblockheadersrange", fail_message.c_str())) diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index e8b12cb9b..99899c3cc 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -85,7 +85,7 @@ public: bool print_connections(); - bool print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index); + bool print_blockchain_info(int64_t start_block_index, uint64_t end_block_index); bool set_log_level(int8_t level); From 27457a2268220c11977a0772a0d50dc7a8edd998 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 4 Nov 2019 19:32:48 +0000 Subject: [PATCH 052/469] wallet2: fix pool txes not being flushed when mined --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7f8b48b8d..e60c6b7e1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3277,7 +3277,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // leak allowing a passive adversary with traffic analysis capability to // infer when we get an incoming output std::vector> process_pool_txs; - update_pool_state(process_pool_txs, refreshed); + update_pool_state(process_pool_txs, true); bool first = true, last = false; while(m_run.load(std::memory_order_relaxed)) From 4d804443cdb4502a2577f01251ad82d46f72ccb1 Mon Sep 17 00:00:00 2001 From: TheGoose <18522748+theg00s3@users.noreply.github.com> Date: Mon, 4 Nov 2019 13:36:04 +1100 Subject: [PATCH 053/469] Fixes a minor formatting error Fixes a minor formatting error --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b6b252e0..a102f5243 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,8 @@ Dates are provided in the format YYYY-MM-DD. | XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX | X's indicate that these details have not been determined as of commit date. -* indicates estimate as of commit date + +\* indicates estimate as of commit date ## Release staging schedule and protocol From b328de6b7ae7c1229c392210f3f15c1b1fc12460 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 5 Nov 2019 14:22:58 +0000 Subject: [PATCH 054/469] wallet_rpc_server: add tx weight in transfer commands responses --- src/wallet/wallet_rpc_server.cpp | 13 +++++++------ src/wallet/wallet_rpc_server.h | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 10 ++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..001cee15c 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -843,7 +843,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ template bool wallet_rpc_server::fill_response(std::vector &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, + bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er) { for (const auto & ptx : ptx_vector) @@ -858,6 +858,7 @@ namespace tools // Compute amount leaving wallet in tx. By convention dests does not include change outputs fill(amount, total_amount(ptx)); fill(fee, ptx.fee); + fill(weight, cryptonote::get_transaction_weight(ptx.tx)); } if (m_wallet->multisig()) @@ -943,7 +944,7 @@ namespace tools return false; } - return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); } catch (const std::exception& e) @@ -989,7 +990,7 @@ namespace tools return false; } - return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1353,7 +1354,7 @@ namespace tools { std::vector ptx_vector = m_wallet->create_unmixable_sweep_transactions(); - return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1400,7 +1401,7 @@ namespace tools uint32_t priority = m_wallet->adjust_priority(req.priority); std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); - return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1475,7 +1476,7 @@ namespace tools return false; } - return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); } catch (const std::exception& e) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b2b5e7116..8dc363728 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -255,7 +255,7 @@ namespace tools template bool fill_response(std::vector &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, + bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er); bool validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..a1876b3b8 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -484,6 +484,7 @@ namespace wallet_rpc std::string tx_key; uint64_t amount; uint64_t fee; + uint64_t weight; std::string tx_blob; std::string tx_metadata; std::string multisig_txset; @@ -494,6 +495,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) KV_SERIALIZE(fee) + KV_SERIALIZE(weight) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) @@ -550,6 +552,7 @@ namespace wallet_rpc std::list tx_key_list; std::list amount_list; std::list fee_list; + std::list weight_list; std::list tx_blob_list; std::list tx_metadata_list; std::string multisig_txset; @@ -560,6 +563,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(weight_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) @@ -723,6 +727,7 @@ namespace wallet_rpc std::list tx_key_list; std::list amount_list; std::list fee_list; + std::list weight_list; std::list tx_blob_list; std::list tx_metadata_list; std::string multisig_txset; @@ -733,6 +738,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(weight_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) @@ -793,6 +799,7 @@ namespace wallet_rpc std::list tx_key_list; std::list amount_list; std::list fee_list; + std::list weight_list; std::list tx_blob_list; std::list tx_metadata_list; std::string multisig_txset; @@ -803,6 +810,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(weight_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) @@ -850,6 +858,7 @@ namespace wallet_rpc std::string tx_key; uint64_t amount; uint64_t fee; + uint64_t weight; std::string tx_blob; std::string tx_metadata; std::string multisig_txset; @@ -860,6 +869,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) KV_SERIALIZE(fee) + KV_SERIALIZE(weight) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) From 4b38400336c2bd5b0d011da980ca9e46862a50b9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 5 Nov 2019 15:11:37 +0000 Subject: [PATCH 055/469] wallet2: don't try to lock an empty filename --- src/wallet/wallet2.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7f8b48b8d..6e4a14f49 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7555,6 +7555,8 @@ bool wallet2::is_output_blackballed(const std::pair &output) bool wallet2::lock_keys_file() { + if (m_wallet_file.empty()) + return true; if (m_keys_file_locker) { MDEBUG(m_keys_file << " is already locked."); @@ -7566,6 +7568,8 @@ bool wallet2::lock_keys_file() bool wallet2::unlock_keys_file() { + if (m_wallet_file.empty()) + return true; if (!m_keys_file_locker) { MDEBUG(m_keys_file << " is already unlocked."); @@ -7577,6 +7581,8 @@ bool wallet2::unlock_keys_file() bool wallet2::is_keys_file_locked() const { + if (m_wallet_file.empty()) + return false; return m_keys_file_locker->locked(); } From 0de8a0d37d80295ced83a2686b046de52fcbc4e3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 6 Nov 2019 14:18:54 +0000 Subject: [PATCH 056/469] wallet_rpc_server: new estimate_tx_size_and_weight RPC --- src/wallet/wallet2.cpp | 18 ++++++++++++ src/wallet/wallet2.h | 2 ++ src/wallet/wallet_rpc_server.cpp | 19 ++++++++++++ src/wallet/wallet_rpc_server.h | 2 ++ src/wallet/wallet_rpc_server_commands_defs.h | 31 ++++++++++++++++++++ utils/python-rpc/framework/wallet.py | 14 +++++++++ 6 files changed, 86 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7f8b48b8d..171b0147d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -13601,4 +13601,22 @@ std::vector wallet2::get_public_nodes(bool white_only) std::copy(res.gray.begin(), res.gray.end(), std::back_inserter(nodes)); return nodes; } +//---------------------------------------------------------------------------------------------------- +std::pair wallet2::estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size) +{ + THROW_WALLET_EXCEPTION_IF(n_inputs <= 0, tools::error::wallet_internal_error, "Invalid n_inputs"); + THROW_WALLET_EXCEPTION_IF(n_outputs < 0, tools::error::wallet_internal_error, "Invalid n_outputs"); + THROW_WALLET_EXCEPTION_IF(ring_size < 0, tools::error::wallet_internal_error, "Invalid ring size"); + + if (ring_size == 0) + ring_size = get_min_ring_size(); + if (n_outputs == 1) + n_outputs = 2; // extra dummy output + + const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); + size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof); + uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof); + return std::make_pair(size, weight); +} +//---------------------------------------------------------------------------------------------------- } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c86315f7c..07fc8fa6e 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1252,6 +1252,8 @@ private: bool is_unattended() const { return m_unattended; } + std::pair estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); + bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie); bool daemon_requires_payment(); bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..e7875021f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -4291,6 +4291,25 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + size_t extra_size = 34 /* pubkey */ + 10 /* encrypted payment id */; // typical makeup + const std::pair sw = m_wallet->estimate_tx_size_and_weight(req.rct, req.n_inputs, req.ring_size, req.n_outputs, extra_size); + res.size = sw.first; + res.weight = sw.second; + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to determine size and weight"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx) { res.version = WALLET_RPC_VERSION; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b2b5e7116..41f6879ef 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -154,6 +154,7 @@ namespace tools MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON) MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL) MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) + MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) END_JSON_RPC_MAP() END_URI_MAP2() @@ -240,6 +241,7 @@ namespace tools bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); //json rpc v2 diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..1720bc904 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -2580,5 +2580,36 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init response; }; + struct COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT + { + struct request_t + { + uint32_t n_inputs; + uint32_t n_outputs; + uint32_t ring_size; + bool rct; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(n_inputs) + KV_SERIALIZE(n_outputs) + KV_SERIALIZE_OPT(ring_size, 0u) + KV_SERIALIZE_OPT(rct, true) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + uint64_t size; + uint64_t weight; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(size) + KV_SERIALIZE(weight) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + } } diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index 6a3fabdc9..b74709114 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -1062,6 +1062,20 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(stop_mining) + def estimate_tx_size_and_weight(self, n_inputs, n_outputs, ring_size = 0, rct = True): + estimate_tx_size_and_weight = { + 'method': 'estimate_tx_size_and_weight', + 'jsonrpc': '2.0', + 'params': { + 'n_inputs': n_inputs, + 'n_outputs': n_outputs, + 'ring_size': ring_size, + 'rct': rct, + }, + 'id': '0' + } + return self.rpc.send_json_rpc_request(estimate_tx_size_and_weight) + def get_version(self): get_version = { 'method': 'get_version', From 7c1d31b0a1eb41e1e05951ec730fca77a9075736 Mon Sep 17 00:00:00 2001 From: xiphon Date: Sat, 9 Nov 2019 12:44:18 +0000 Subject: [PATCH 057/469] build: fix IOS, build blockchain_db and rpc, skip wallet_rpc_server --- src/CMakeLists.txt | 6 ++-- src/wallet/CMakeLists.txt | 65 ++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f332af3d3..d45363e24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,12 +113,10 @@ add_subdirectory(lmdb) add_subdirectory(multisig) add_subdirectory(net) add_subdirectory(hardforks) -if(NOT IOS) - add_subdirectory(blockchain_db) -endif() +add_subdirectory(blockchain_db) add_subdirectory(mnemonics) +add_subdirectory(rpc) if(NOT IOS) - add_subdirectory(rpc) add_subdirectory(serialization) endif() add_subdirectory(wallet) diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 3be1a6f6b..a0a166a93 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -77,42 +77,43 @@ target_link_libraries(wallet PRIVATE ${EXTRA_LIBRARIES}) -set(wallet_rpc_sources - wallet_rpc_server.cpp) +if(NOT IOS) + set(wallet_rpc_sources + wallet_rpc_server.cpp) -set(wallet_rpc_headers) + set(wallet_rpc_headers) -set(wallet_rpc_private_headers - wallet_rpc_server.h) + set(wallet_rpc_private_headers + wallet_rpc_server.h) -monero_private_headers(wallet_rpc_server - ${wallet_rpc_private_headers}) -monero_add_executable(wallet_rpc_server - ${wallet_rpc_sources} - ${wallet_rpc_headers} - ${wallet_rpc_private_headers}) - -target_link_libraries(wallet_rpc_server - PRIVATE - wallet - rpc_base - cryptonote_core - cncrypto - common - version - daemonizer - ${EPEE_READLINE} - ${Boost_CHRONO_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_THREAD_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) -set_property(TARGET wallet_rpc_server - PROPERTY - OUTPUT_NAME "monero-wallet-rpc") -install(TARGETS wallet_rpc_server DESTINATION bin) + monero_private_headers(wallet_rpc_server + ${wallet_rpc_private_headers}) + monero_add_executable(wallet_rpc_server + ${wallet_rpc_sources} + ${wallet_rpc_headers} + ${wallet_rpc_private_headers}) + target_link_libraries(wallet_rpc_server + PRIVATE + wallet + rpc_base + cryptonote_core + cncrypto + common + version + daemonizer + ${EPEE_READLINE} + ${Boost_CHRONO_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + set_property(TARGET wallet_rpc_server + PROPERTY + OUTPUT_NAME "monero-wallet-rpc") + install(TARGETS wallet_rpc_server DESTINATION bin) +endif() # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) From 27522aaa124a4a84091c3afaa85898dea2251c10 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 10 Nov 2019 12:58:19 +0000 Subject: [PATCH 058/469] core_tests: reset thread pool between tests Avoids a DB error (leading to an assert) where a thread uses a read txn previously created with an environment that was since closed and reopened. While this usually works since BlockchainLMDB renews txns if it detects the environment has changed, this will not work if objects end up being allocated at the same address as the previous instance, leading to stale data usage. Thanks hyc for the LMDB debugging. --- src/common/threadpool.cpp | 29 ++++++++++++++++++++++------- src/common/threadpool.h | 5 +++++ tests/core_tests/chaingen.h | 2 ++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 2748c798c..6eb527cdc 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -37,16 +37,14 @@ static __thread bool is_leaf = false; namespace tools { threadpool::threadpool(unsigned int max_threads) : running(true), active(0) { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - max = max_threads ? max_threads : tools::get_max_concurrency(); - size_t i = max ? max - 1 : 0; - while(i--) { - threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false))); - } + create(max_threads); } threadpool::~threadpool() { + destroy(); +} + +void threadpool::destroy() { try { const boost::unique_lock lock(mutex); @@ -64,6 +62,23 @@ threadpool::~threadpool() { try { threads[i].join(); } catch (...) { /* ignore */ } } + threads.clear(); +} + +void threadpool::recycle() { + destroy(); + create(max); +} + +void threadpool::create(unsigned int max_threads) { + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + max = max_threads ? max_threads : tools::get_max_concurrency(); + size_t i = max ? max - 1 : 0; + running = true; + while(i--) { + threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false))); + } } void threadpool::submit(waiter *obj, std::function f, bool leaf) { diff --git a/src/common/threadpool.h b/src/common/threadpool.h index 5e490ee7d..a49d0e14f 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -69,12 +69,17 @@ public: // task to finish. void submit(waiter *waiter, std::function f, bool leaf = false); + // destroy and recreate threads + void recycle(); + unsigned int get_max_concurrency() const; ~threadpool(); private: threadpool(unsigned int max_threads = 0); + void destroy(); + void create(unsigned int max_threads); typedef struct entry { waiter *wo; std::function f; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index b78640dc9..fa27deee4 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -47,6 +47,7 @@ #include "include_base_utils.h" #include "common/boost_serialization_helper.h" #include "common/command_line.h" +#include "common/threadpool.h" #include "cryptonote_basic/account_boost_serialization.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -758,6 +759,7 @@ inline bool do_replay_events_get_core(std::vector& events, cry t_test_class validator; bool ret = replay_events_through_core(c, events, validator); + tools::threadpool::getInstance().recycle(); // c.deinit(); return ret; } From 0f7c9f4f3162026d01ea653bdd5d5699fdc0a83b Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Sun, 10 Nov 2019 18:07:43 +0100 Subject: [PATCH 059/469] Gitian Readme: adding android signing & fix v0.15 checksum. --- contrib/gitian/README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 4fa393283..7e3502bcf 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -167,13 +167,12 @@ If all went well, this produces a number of (uncommitted) `.assert` files in the Checking your work ------------------ -Take a look in the assert files and note the SHA256 checksums listed there. eg for `v0.15.0.0` you should get this checksum: +Take a look in the assert files and note the SHA256 checksums listed there. -``` -2b95118f53d98d542a85f8732b84ba13b3cd20517ccb40332b0edd0ddf4f8c62 monero-x86_64-linux-gnu.tar.gz -``` +You should verify that the checksum that is listed matches each of the binaries you actually built. +This may be done on Linux using the `sha256sum` command or on MacOS using `shasum --algorithm 256` for example. -You should verify that this is really the checksum you get on that file you built. You can also look in the gitian.sigs repo and / or [getmonero.org release checksums](https://web.getmonero.org/downloads/hashes.txt) to see if others got the same checksum for the same version tag. If there is ever a mismatch -- **STOP! Something is wrong**. Contact others on IRC / github to figure out what is going on. +You can also look in the [gitian.sigs](https://github.com/monero-project/gitian.sigs/) repo and / or [getmonero.org release checksums](https://web.getmonero.org/downloads/hashes.txt) to see if others got the same checksum for the same version tag. If there is ever a mismatch -- **STOP! Something is wrong**. Contact others on IRC / github to figure out what is going on. Signing assert files @@ -188,8 +187,8 @@ VERSION=v0.15.0.0 gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert gpg --detach-sign ${VERSION}-osx/${GH_USER}/monero-osx-*-build.assert +gpg --detach-sign ${VERSION}-android/${GH_USER}/monero-android-*-build.assert ``` - This will create a `.sig` file for each `.assert` file above (2 files for each platform). From 75c5a04ca5025629c4db6fdefa844a6addf1d5e1 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 10 Nov 2019 09:19:52 +0000 Subject: [PATCH 060/469] gitian: Update to latest gitian-builder --- contrib/gitian/gitian-build.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index b60cc9309..28f2d9d4a 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -22,11 +22,7 @@ def setup(): if not os.path.isdir('builder'): subprocess.check_call(['git', 'clone', gbrepo, 'builder']) os.chdir('builder') - subprocess.check_call(['git', 'config', 'user.email', 'gitianuser@localhost']) - subprocess.check_call(['git', 'config', 'user.name', 'gitianuser']) - subprocess.check_call(['git', 'checkout', '963322de8420c50502c4cc33d4d7c0d84437b576']) - subprocess.check_call(['git', 'fetch', 'origin', '72c51f0bd2adec4eedab4dbd06c9229b9c4eb0e3']) - subprocess.check_call(['git', 'cherry-pick', '72c51f0bd2adec4eedab4dbd06c9229b9c4eb0e3']) + subprocess.check_call(['git', 'checkout', 'c0f77ca018cb5332bfd595e0aff0468f77542c23']) os.makedirs('inputs', exist_ok=True) os.chdir('inputs') if not os.path.isdir('monero'): From 6f45cfa5c5798520249880d247ec81bfc2297dae Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 11 Nov 2019 12:43:46 +0000 Subject: [PATCH 061/469] p2p: zero last seen timestamp when inserting a new peer --- src/p2p/net_node.inl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f8094bfa8..117468f9b 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1882,6 +1882,7 @@ namespace nodetool --i; continue; } + local_peerlist[i].last_seen = 0; #ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED be.pruning_seed = tools::make_pruning_seed(1 + (be.adr.as().ip()) % (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES), CRYPTONOTE_PRUNING_LOG_STRIPES); From 3d649d528a9aea786ddfcce08e0691c251933c39 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 11 Nov 2019 20:05:00 +0000 Subject: [PATCH 062/469] epee: close connection when the peer has done so This fixes rapid reconnections failing as the peer hasn't yet worked out the other side is gone, and will reject "duplicate" connections until a timeout. --- contrib/epee/include/net/abstract_tcp_server2.inl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 5d12f9466..128ff10aa 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -410,7 +410,12 @@ PRAGMA_WARNING_DISABLE_VS(4355) else { _dbg3("[sock " << socket().native_handle() << "] peer closed connection"); - if (m_ready_to_close) + bool do_shutdown = false; + CRITICAL_REGION_BEGIN(m_send_que_lock); + if(!m_send_que.size()) + do_shutdown = true; + CRITICAL_REGION_END(); + if (m_ready_to_close || do_shutdown) shutdown(); } m_ready_to_close = true; @@ -470,6 +475,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) { MERROR("SSL handshake failed"); boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); + m_ready_to_close = true; bool do_shutdown = false; CRITICAL_REGION_BEGIN(m_send_que_lock); if(!m_send_que.size()) From 21d4c216198bcd04b0d824bb2863fc5198e9166b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 12 Nov 2019 12:42:24 +0000 Subject: [PATCH 063/469] blockchain: error out if the builtin hashes data size is wrong --- src/cryptonote_core/blockchain.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d22158dfc..49415c6c6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -5074,7 +5074,12 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get return; } const size_t size_needed = 4 + nblocks * (sizeof(crypto::hash) * 2); - if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) + if(checkpoints.size() != size_needed) + { + MERROR("Failed to load hashes - unexpected data size"); + return; + } + else if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); From 584d057f744fee39560e35a037fec339bee2bf26 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 12 Nov 2019 16:05:17 +0000 Subject: [PATCH 064/469] epee: fix console_handlers_binder race, wait for thread to finish --- contrib/epee/include/console_handler.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index 13747b0c8..1b716fca4 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -605,10 +605,17 @@ eof: std::unique_ptr m_console_thread; async_console_handler m_console_handler; public: + ~console_handlers_binder() { + stop_handling(); + if (m_console_thread.get() != nullptr) + { + m_console_thread->join(); + } + } + bool start_handling(std::function prompt, const std::string& usage_string = "", std::function exit_handler = NULL) { m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, prompt, usage_string, exit_handler))); - m_console_thread->detach(); return true; } bool start_handling(const std::string &prompt, const std::string& usage_string = "", std::function exit_handler = NULL) From 32937809928ae2204f9eaf1f4995fb1681bd52fd Mon Sep 17 00:00:00 2001 From: Nathan Dorfman Date: Thu, 24 Oct 2019 15:53:12 -0600 Subject: [PATCH 065/469] daemon: Use rpc for "version" command --- src/daemon/command_parser_executor.cpp | 4 +-- src/daemon/rpc_command_executor.cpp | 35 ++++++++++++++++++++++++++ src/daemon/rpc_command_executor.h | 2 ++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index b827221f6..99a460c78 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -28,7 +28,6 @@ #include "common/dns_utils.h" #include "common/command_line.h" -#include "version.h" #include "daemon/command_parser_executor.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -803,8 +802,7 @@ bool t_command_parser_executor::rpc_payments(const std::vector& arg bool t_command_parser_executor::version(const std::vector& args) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; - return true; + return m_executor.version(); } bool t_command_parser_executor::prune_blockchain(const std::vector& args) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..f4a43d3d6 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -2442,4 +2442,39 @@ bool t_rpc_command_executor::rpc_payments() return true; } +bool t_rpc_command_executor::version() +{ + cryptonote::COMMAND_RPC_GET_INFO::request req; + cryptonote::COMMAND_RPC_GET_INFO::response res; + + const char *fail_message = "Problem fetching info"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/getinfo", fail_message)) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_info(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + if (res.version.empty()) + { + tools::fail_msg_writer() << "The daemon software version is not available."; + } + else + { + tools::success_msg_writer() << res.version; + } + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index e8b12cb9b..af55f0e22 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -163,6 +163,8 @@ public: bool print_net_stats(); + bool version(); + bool set_bootstrap_daemon( const std::string &address, const std::string &username, From dce6f055f90d076bc7afc0bc97110d049eb036df Mon Sep 17 00:00:00 2001 From: Nathan Dorfman Date: Tue, 12 Nov 2019 15:41:05 -0700 Subject: [PATCH 066/469] rpc: Only show version string if it matches expected pattern --- src/daemon/rpc_command_executor.cpp | 3 +- src/rpc/CMakeLists.txt | 2 + src/rpc/rpc_version_str.cpp | 55 ++++++++++++++++++++++++++++ src/rpc/rpc_version_str.h | 43 ++++++++++++++++++++++ tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/rpc_version_str.cpp | 49 +++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/rpc/rpc_version_str.cpp create mode 100644 src/rpc/rpc_version_str.h create mode 100644 tests/unit_tests/rpc_version_str.cpp diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index f4a43d3d6..eb9dfdf1e 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -38,6 +38,7 @@ #include "cryptonote_basic/difficulty.h" #include "cryptonote_basic/hardfork.h" #include "rpc/rpc_payment_signature.h" +#include "rpc/rpc_version_str.h" #include #include #include @@ -2465,7 +2466,7 @@ bool t_rpc_command_executor::version() } } - if (res.version.empty()) + if (res.version.empty() || !cryptonote::rpc::is_version_string_valid(res.version)) { tools::fail_msg_writer() << "The daemon software version is not available."; } diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index ebb1e767f..65d88b57e 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -37,6 +37,7 @@ set(rpc_sources bootstrap_daemon.cpp core_rpc_server.cpp rpc_payment.cpp + rpc_version_str.cpp instanciations) set(daemon_messages_sources @@ -54,6 +55,7 @@ set(rpc_base_headers rpc_handler.h) set(rpc_headers + rpc_version_str.h rpc_handler.h) set(daemon_rpc_server_headers) diff --git a/src/rpc/rpc_version_str.cpp b/src/rpc/rpc_version_str.cpp new file mode 100644 index 000000000..c60cf4891 --- /dev/null +++ b/src/rpc/rpc_version_str.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#include "rpc_version_str.h" +#include "version.h" +#include + +namespace cryptonote +{ + +namespace rpc +{ + +// Expected format of Monero software version string: +// 1) Four numbers, one to two digits each, separated by periods +// 2) Optionally, one of the following suffixes: +// a) -release +// b) - where is exactly nine lowercase hex digits + +bool is_version_string_valid(const std::string& str) +{ + return std::regex_match(str, std::regex( + "^\\d{1,2}(\\.\\d{1,2}){3}(-(release|[0-9a-f]{9}))?$", + std::regex_constants::nosubs + )); +} + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/rpc_version_str.h b/src/rpc/rpc_version_str.h new file mode 100644 index 000000000..930c807d2 --- /dev/null +++ b/src/rpc/rpc_version_str.h @@ -0,0 +1,43 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. +// +#pragma once + +#include + +namespace cryptonote +{ + +namespace rpc +{ + +bool is_version_string_valid(const std::string& str); + +} // namespace rpc + +} // namespace cryptonote diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 96825f54f..cda25dfc9 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -92,7 +92,8 @@ set(unit_tests_sources ringdb.cpp wipeable_string.cpp is_hdd.cpp - aligned.cpp) + aligned.cpp + rpc_version_str.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/rpc_version_str.cpp b/tests/unit_tests/rpc_version_str.cpp new file mode 100644 index 000000000..5dce60465 --- /dev/null +++ b/tests/unit_tests/rpc_version_str.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#include "gtest/gtest.h" + +#include "rpc/rpc_version_str.h" +#include "version.h" + +TEST(rpc, is_version_string_valid) +{ + using namespace cryptonote::rpc; + ASSERT_TRUE(is_version_string_valid(MONERO_VERSION)); + ASSERT_TRUE(is_version_string_valid("0.14.1.2")); + ASSERT_TRUE(is_version_string_valid("0.15.0.0-release")); + ASSERT_TRUE(is_version_string_valid("0.15.0.0-fe3f6a3e6")); + + ASSERT_FALSE(is_version_string_valid("")); + ASSERT_FALSE(is_version_string_valid("invalid")); + ASSERT_FALSE(is_version_string_valid("0.15.0.0-invalid")); + ASSERT_FALSE(is_version_string_valid("0.15.0.0-release0")); + ASSERT_FALSE(is_version_string_valid("0.15.0.0-release ")); + ASSERT_FALSE(is_version_string_valid("0.15.0.0-fe3f6a3e60")); + ASSERT_FALSE(is_version_string_valid("0.15.0.0-fe3f6a3e6 ")); +} From 67b4a19edf7b9d6a407a79eca5c57819863bfd71 Mon Sep 17 00:00:00 2001 From: wowario Date: Wed, 13 Nov 2019 10:17:45 +0300 Subject: [PATCH 067/469] simplewallet: noob-friendly help menu --- src/cryptonote_core/cryptonote_core.cpp | 5 +- .../cryptonote_protocol_handler.inl | 3 +- src/simplewallet/simplewallet.cpp | 53 +++++++++++++++---- src/simplewallet/simplewallet.h | 1 + 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 02620996e..826f2fc52 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1639,8 +1639,9 @@ namespace cryptonote << "You can set the level of process detailization through \"set_log \" command," << ENDL << "where is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL << ENDL - << "Use the \"help\" command to see the list of available commands." << ENDL - << "Use \"help \" to see a command's documentation." << ENDL + << "Use the \"help\" command to see a simplified list of available commands." << ENDL + << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL + << "Use \"help_advanced \" to see a command's documentation." << ENDL << "**********************************************************************" << ENDL); m_starter_message_showed = true; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 74ceeb41d..24d221c86 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2181,7 +2181,8 @@ skip: MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL << ENDL - << "Use the \"help\" command to see the list of available commands." << ENDL + << "Use the \"help\" command to see a simplified list of available commands." << ENDL + << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL << "**********************************************************************"); m_sync_timer.pause(); if (ELPP->vRegistry()->allowed(el::Level::Info, "sync-info")) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..ddbdc3e15 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -121,7 +121,7 @@ typedef cryptonote::simple_wallet sw; #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) -#define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help; +#define PRINT_USAGE(usage_help_advanced) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help_advanced; #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ do { \ @@ -268,7 +268,8 @@ namespace const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc"); const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc"); const char* USAGE_VERSION("version"); - const char* USAGE_HELP("help []"); + const char* USAGE_HELP_ADVANCED("help_advanced []"); + const char* USAGE_HELP("help"); std::string input_line(const std::string& prompt, bool yesno = false) { @@ -2315,7 +2316,7 @@ bool simple_wallet::on_unknown_command(const std::vector &args) { if (args[0] == "exit" || args[0] == "q") // backward compat return false; - fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help'")) % args.front(); + fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help_advanced'")) % args.front(); return true; } @@ -3043,6 +3044,30 @@ bool simple_wallet::set_export_format(const std::vector &args/* = s } bool simple_wallet::help(const std::vector &args/* = std::vector()*/) +{ + message_writer() << ""; + message_writer() << tr("Commands:"); + message_writer() << ""; + message_writer() << tr("\"welcome\" - Read welcome message."); + message_writer() << tr("\"donate \" - Donate XMR to the development team."); + message_writer() << tr("\"balance\" - Show balance."); + message_writer() << tr("\"address new\" - Create new subaddress."); + message_writer() << tr("\"address all\" - Show all addresses."); + message_writer() << tr("\"transfer
\" - Send XMR to an address."); + message_writer() << tr("\"show_transfers [in|out|pending|failed|pool]\" - Show transactions."); + message_writer() << tr("\"sweep_all
\" - Send whole balance to another wallet."); + message_writer() << tr("\"seed\" - Show secret 25 words that can be used to recover this wallet."); + message_writer() << tr("\"refresh\" - Synchronize wallet with the Monero network."); + message_writer() << tr("\"status\" - Check current status of wallet."); + message_writer() << tr("\"version\" - Check software version."); + message_writer() << tr("\"help_advanced\" - Show list with more available commands."); + message_writer() << tr("\"save\" - Save wallet."); + message_writer() << tr("\"exit\" - Exit wallet."); + message_writer() << ""; + return true; +} + +bool simple_wallet::help_advanced(const std::vector &args/* = std::vector()*/) { if(args.empty()) { @@ -3050,7 +3075,7 @@ bool simple_wallet::help(const std::vector &args/* = std::vector" + // Little hack to be able to do "help_advanced mms " std::vector mms_args(1, args.front() + " " + args.back()); success_msg_writer() << get_command_usage(mms_args); } @@ -3439,7 +3464,7 @@ simple_wallet::simple_wallet() " is one of:\n" " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" " send_signer_config, start_auto_config, stop_auto_config, auto_config\n" - "Get help about a subcommand with: help mms , or mms help ")); + "Get help about a subcommand with: help_advanced mms ")); m_cmd_binder.set_handler("mms init", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_INIT), @@ -3589,10 +3614,14 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::stop_mining_for_rpc, this, _1), tr(USAGE_STOP_MINING_FOR_RPC), tr("Stop mining to pay for RPC access")); + m_cmd_binder.set_handler("help_advanced", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::help_advanced, _1), + tr(USAGE_HELP_ADVANCED), + tr("Show the help section or the documentation about a .")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::on_command, this, &simple_wallet::help, _1), tr(USAGE_HELP), - tr("Show the help section or the documentation about a .")); + tr("Show simplified list of available commands.")); m_cmd_binder.set_unknown_command_handler(boost::bind(&simple_wallet::on_command, this, &simple_wallet::on_unknown_command, _1)); m_cmd_binder.set_empty_command_handler(boost::bind(&simple_wallet::on_empty_command, this)); m_cmd_binder.set_cancel_handler(boost::bind(&simple_wallet::on_cancelled_command, this)); @@ -4744,8 +4773,9 @@ boost::optional simple_wallet::new_wallet(const boost::pr "**********************************************************************\n" << tr("Your wallet has been generated!\n" "To start synchronizing with the daemon, use the \"refresh\" command.\n" - "Use the \"help\" command to see the list of available commands.\n" - "Use \"help \" to see a command's documentation.\n" + "Use the \"help\" command to see a simplified list of available commands.\n" + "Use the \"help_advanced\" command to see an advanced list of available commands.\n" + "Use \"help_advanced \" to see a command's documentation.\n" "Always use the \"exit\" command when closing monero-wallet-cli to save \n" "your current session's state. Otherwise, you might need to synchronize \n" "your wallet again (your wallet keys are NOT at risk in any case).\n") @@ -5004,8 +5034,9 @@ boost::optional simple_wallet::open_wallet(const boost::p } success_msg_writer() << "**********************************************************************\n" << - tr("Use the \"help\" command to see the list of available commands.\n") << - tr("Use \"help \" to see a command's documentation.\n") << + tr("Use the \"help\" command to see a simplified list of available commands.\n") << + tr("Use the \"help_advanced\" command to see an advanced list of available commands.\n") << + tr("Use \"help_advanced \" to see a command's documentation.\n") << "**********************************************************************"; return password; } @@ -11016,7 +11047,7 @@ void simple_wallet::mms_help(const std::vector &args) { if (args.size() > 1) { - fail_msg_writer() << tr("Usage: mms help []"); + fail_msg_writer() << tr("Usage: help_advanced mms []"); return; } std::vector help_args; diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 75bd893d5..e401f5fda 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -154,6 +154,7 @@ namespace cryptonote bool set_persistent_rpc_client_id(const std::vector &args = std::vector()); bool set_auto_mine_for_rpc_payment_threshold(const std::vector &args = std::vector()); bool set_credits_target(const std::vector &args = std::vector()); + bool help_advanced(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); From da6c807f8b0e1aa99e4aa85444e3c309ace43d67 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 8 Nov 2019 16:33:16 +0100 Subject: [PATCH 068/469] tests: fix HF12 chaingen - construct bc object from events --- tests/core_tests/chaingen.cpp | 188 +++++++++++++++++++++++++++--- tests/core_tests/chaingen.h | 11 +- tests/core_tests/wallet_tools.cpp | 3 - 3 files changed, 180 insertions(+), 22 deletions(-) diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 35e449e10..c38ea614c 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -47,6 +47,12 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/miner.h" +#include "blockchain_db/blockchain_db.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/testdb.h" + #include "chaingen.h" #include "device/device.hpp" using namespace std; @@ -55,6 +61,126 @@ using namespace epee; using namespace crypto; using namespace cryptonote; +namespace +{ + /** + * Dummy TestDB to store height -> (block, hash) information + * for the use only in the test_generator::fill_nonce() function, + * which requires blockchain object to correctly compute PoW on HF12+ blocks + * as the mining function requires it to obtain a valid seedhash. + */ + class TestDB: public cryptonote::BaseTestDB + { + private: + struct block_t + { + cryptonote::block bl; + crypto::hash hash; + }; + + public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override + { + blocks.push_back({blk, blk_hash}); + } + + virtual uint64_t height() const override { return blocks.empty() ? 0 : blocks.size() - 1; } + + // Required for randomx + virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const override + { + if (height < blocks.size()) + { + MDEBUG("Get hash for block height: " << height << " hash: " << blocks[height].hash); + return blocks[height].hash; + } + + MDEBUG("Get hash for block height: " << height << " zero-hash"); + crypto::hash hash = crypto::null_hash; + *(uint64_t*)&hash = height; + return hash; + } + + virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override + { + const uint64_t h = height(); + if (block_height != nullptr) + { + *block_height = h; + } + + return get_block_hash_from_height(h); + } + + virtual cryptonote::block get_top_block() const override + { + if (blocks.empty()) + { + cryptonote::block b; + return b; + } + + return blocks[blocks.size()-1].bl; + } + + virtual void pop_block(cryptonote::block &blk, std::vector &txs) override { if (!blocks.empty()) blocks.pop_back(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) override { if (height >= hf.size()) hf.resize(height + 1); hf[height] = version; } + virtual uint8_t get_hard_fork_version(uint64_t height) const override { if (height >= hf.size()) return 255; return hf[height]; } + + private: + std::vector blocks; + std::vector hf; + }; + +} + +static std::unique_ptr init_blockchain(const std::vector & events, cryptonote::network_type nettype) +{ + std::unique_ptr bc; + v_hardforks_t hardforks; + cryptonote::test_options test_options_tmp{nullptr, 0}; + const cryptonote::test_options * test_options = &test_options_tmp; + if (!extract_hard_forks(events, hardforks)) + { + MDEBUG("Extracting hard-forks from blocks"); + extract_hard_forks_from_blocks(events, hardforks); + } + + hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator + test_options_tmp.hard_forks = hardforks.data(); + test_options = &test_options_tmp; + + cryptonote::tx_memory_pool txpool(*bc); + bc.reset(new cryptonote::Blockchain(txpool)); + + cryptonote::Blockchain *blockchain = bc.get(); + auto bdb = new TestDB(); + + BOOST_FOREACH(const test_event_entry &ev, events) + { + if (typeid(block) != ev.type()) + { + continue; + } + + const block *blk = &boost::get(ev); + auto blk_hash = get_block_hash(*blk); + bdb->add_block(*blk, 1, 1, 1, 0, 0, blk_hash); + } + + bool r = blockchain->init(bdb, nettype, true, test_options, 2, nullptr); + CHECK_AND_ASSERT_THROW_MES(r, "could not init blockchain from events"); + return bc; +} void test_generator::get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const { @@ -184,13 +310,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co //blk.tree_root_hash = get_tx_tree_hash(blk); - // Nonce search... - blk.nonce = 0; - while (!miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ - return cryptonote::get_block_longhash(NULL, b, hash, height, threads); - }, blk, get_test_difficulty(hf_ver), height)) - blk.timestamp++; - + fill_nonce(blk, get_test_difficulty(hf_ver), height); add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1); return true; @@ -268,6 +388,26 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight); } +void test_generator::fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) +{ + const cryptonote::Blockchain *blockchain = nullptr; + std::unique_ptr bc; + + if (blk.major_version >= RX_BLOCK_VERSION) + { + CHECK_AND_ASSERT_THROW_MES(m_events != nullptr, "events not set, cannot compute valid RandomX PoW"); + bc = init_blockchain(*m_events, m_nettype); + blockchain = bc.get(); + } + + blk.nonce = 0; + while (!miner::find_nonce_for_given_block([blockchain](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ + return cryptonote::get_block_longhash(blockchain, b, hash, height, threads); + }, blk, diffic, height)) { + blk.timestamp++; + } +} + namespace { uint64_t get_inputs_amount(const vector &s) @@ -796,15 +936,6 @@ void fill_tx_sources_and_destinations(const std::vector& event fill_tx_sources_and_destinations(events, blk_head, from, to.get_keys().m_account_address, amount, fee, nmix, sources, destinations); } -void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) -{ - blk.nonce = 0; - while (!miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){ - return cryptonote::get_block_longhash(NULL, b, hash, height, threads); - }, blk, diffic, height)) - blk.timestamp++; -} - cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr, uint64_t amount) { tx_destination_entry de; @@ -983,6 +1114,31 @@ bool extract_hard_forks(const std::vector& events, v_hardforks return !hard_forks.empty(); } +bool extract_hard_forks_from_blocks(const std::vector& events, v_hardforks_t& hard_forks) +{ + int hf = -1; + int64_t height = 0; + + for(auto & ev : events) + { + if (typeid(block) != ev.type()) + { + continue; + } + + const block *blk = &boost::get(ev); + if (blk->major_version != hf) + { + hf = blk->major_version; + hard_forks.push_back(std::make_pair(blk->major_version, (uint64_t)height)); + } + + height += 1; + } + + return !hard_forks.empty(); +} + void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs) { std::unordered_set confirmed_hashes; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index b78640dc9..993af9960 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -227,8 +227,8 @@ public: bf_hf_version= 1 << 8 }; - test_generator() {} - test_generator(const test_generator &other): m_blocks_info(other.m_blocks_info) {} + test_generator(): m_events(nullptr) {} + test_generator(const test_generator &other): m_blocks_info(other.m_blocks_info), m_events(other.m_events), m_nettype(other.m_nettype) {} void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; void get_last_n_block_weights(std::vector& block_weights, const crypto::hash& head, size_t n) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; @@ -253,9 +253,14 @@ public: uint8_t hf_version = 1); bool construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block, const cryptonote::account_base& miner_acc, const std::vector& tx_hashes, size_t txs_size); + void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height); + void set_events(const std::vector * events) { m_events = events; } + void set_network_type(const cryptonote::network_type nettype) { m_nettype = nettype; } private: std::unordered_map m_blocks_info; + const std::vector * m_events; + cryptonote::network_type m_nettype; friend class boost::serialization::access; @@ -407,7 +412,6 @@ cryptonote::account_public_address get_address(const cryptonote::tx_destination_ inline cryptonote::difficulty_type get_test_difficulty(const boost::optional& hf_ver=boost::none) {return !hf_ver || hf_ver.get() <= 1 ? 1 : 2;} inline uint64_t current_difficulty_window(const boost::optional& hf_ver=boost::none){ return !hf_ver || hf_ver.get() <= 1 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; } -void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height); cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr=false, uint64_t amount=0); std::vector build_dsts(const var_addr_t& to1, bool sub1=false, uint64_t am1=0); @@ -490,6 +494,7 @@ void fill_tx_sources_and_destinations(const std::vector& event uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx); bool extract_hard_forks(const std::vector& events, v_hardforks_t& hard_forks); +bool extract_hard_forks_from_blocks(const std::vector& events, v_hardforks_t& hard_forks); /************************************************************************/ /* */ diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp index 21a9455c0..fdc4753f9 100644 --- a/tests/core_tests/wallet_tools.cpp +++ b/tests/core_tests/wallet_tools.cpp @@ -10,9 +10,6 @@ using namespace epee; using namespace crypto; using namespace cryptonote; -// Shared random generator -static std::default_random_engine RND(crypto::rand()); - void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::account_base& account) { wallet->clear(); From 9fe8a76c59a766dabe5023f8b0bc0341b8e51891 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 12 Nov 2019 18:39:03 +0000 Subject: [PATCH 069/469] perf_timer: fix pause/resume macros dereferencing too much --- src/common/perf_timer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index ea2237348..29a37e655 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -84,7 +84,7 @@ void set_performance_timer_log_level(el::Level level); #define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr PERF_TIMER_NAME(name)(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info)) #define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000000) #define PERF_TIMER_STOP(name) do { PERF_TIMER_NAME(name).reset(NULL); } while(0) -#define PERF_TIMER_PAUSE(name) PERF_TIMER_NAME(name)->pause() -#define PERF_TIMER_RESUME(name) PERF_TIMER_NAME(name)->resume() +#define PERF_TIMER_PAUSE(name) PERF_TIMER_NAME(name).pause() +#define PERF_TIMER_RESUME(name) PERF_TIMER_NAME(name).resume() } From b2ad757f487ceb41da917871e2330d00b1b4669e Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 13 Nov 2019 18:00:50 +0100 Subject: [PATCH 070/469] Replace memset with memwipe. --- contrib/epee/include/md5_l.h | 2 +- src/crypto/blake256.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/md5_l.h b/contrib/epee/include/md5_l.h index a45d91bc8..bc7122650 100644 --- a/contrib/epee/include/md5_l.h +++ b/contrib/epee/include/md5_l.h @@ -85,7 +85,7 @@ namespace md5 MD5Update( &ctx, input, ilen ); MD5Final( output, &ctx); - memset( &ctx, 0, sizeof( MD5_CTX) ); + memwipe( &ctx, sizeof( MD5_CTX )); return true; } diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index 1e305b3a6..bb2c5fb40 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "blake256.h" #define U8TO32(p) \ @@ -277,7 +278,7 @@ void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { } blake256_update(&S->outer, pad, 512); - memset(keyhash, 0, 32); + memwipe(keyhash, sizeof(keyhash)); } // keylen = number of bytes @@ -307,7 +308,7 @@ void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { } blake224_update(&S->outer, pad, 512); - memset(keyhash, 0, 32); + memwipe(keyhash, sizeof(keyhash)); } // datalen = number of bits @@ -327,7 +328,7 @@ void hmac_blake256_final(hmac_state *S, uint8_t *digest) { blake256_final(&S->inner, ihash); blake256_update(&S->outer, ihash, 256); blake256_final(&S->outer, digest); - memset(ihash, 0, 32); + memwipe(ihash, sizeof(ihash)); } void hmac_blake224_final(hmac_state *S, uint8_t *digest) { @@ -335,7 +336,7 @@ void hmac_blake224_final(hmac_state *S, uint8_t *digest) { blake224_final(&S->inner, ihash); blake224_update(&S->outer, ihash, 224); blake224_final(&S->outer, digest); - memset(ihash, 0, 32); + memwipe(ihash, sizeof(ihash)); } // keylen = number of bytes; inlen = number of bytes From 6dec001359df2e8e0a7d91aa1eda36a50e26476d Mon Sep 17 00:00:00 2001 From: selsta Date: Thu, 14 Nov 2019 01:37:03 +0100 Subject: [PATCH 071/469] simplewallet: fix restore height warning --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..ad8247728 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4356,7 +4356,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (std::cin.eof() || !command_line::is_yes(confirm)) CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted")); - m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height()); + m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height() > 0 ? m_wallet->estimate_blockchain_height() - 1 : 0); m_wallet->explicit_refresh_from_block_height(true); m_restore_height = m_wallet->get_refresh_from_block_height(); } From 607c01aa563dfb13f941a6ebf8fcd1e00fac398c Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 10 Nov 2019 12:25:13 +0000 Subject: [PATCH 072/469] gitian: Parametrize target platforms --- contrib/gitian/gitian-build.py | 49 +++++++++++++++------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 28f2d9d4a..f934ffa17 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -48,29 +48,28 @@ def rebuild(): print('\nBuilding Dependencies\n') os.makedirs('../out/' + args.version, exist_ok=True) - if args.linux: - print('\nCompiling ' + args.version + ' Linux') - subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, 'inputs/monero/contrib/gitian/gitian-linux.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../sigs/', 'inputs/monero/contrib/gitian/gitian-linux.yml']) - subprocess.check_call('mv build/out/monero-*.tar.bz2 ../out/'+args.version, shell=True) + platforms = {'l': ['Linux', 'linux', 'tar.bz2'], + 'a': ['Android', 'android', 'tar.bz2'], + 'w': ['Windows', 'win', 'zip'], + 'm': ['MacOS', 'osx', 'tar.bz2'] } - if args.android: - print('\nCompiling ' + args.version + ' Android') - subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, 'inputs/monero/contrib/gitian/gitian-android.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-android', '--destination', '../sigs/', 'inputs/monero/contrib/gitian/gitian-android.yml']) - subprocess.check_call('mv build/out/monero-*.tar.bz2 ../out/'+args.version, shell=True) + for i in args.os: + if i is 'm' and args.nomac: + continue - if args.windows: - print('\nCompiling ' + args.version + ' Windows') - subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, 'inputs/monero/contrib/gitian/gitian-win.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win', '--destination', '../sigs/', 'inputs/monero/contrib/gitian/gitian-win.yml']) - subprocess.check_call('mv build/out/monero*.zip ../out/'+args.version, shell=True) + os_name = platforms[i][0] + tag_name = platforms[i][1] + suffix = platforms[i][2] - if args.macos: - print('\nCompiling ' + args.version + ' MacOS') - subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero'+args.url, 'inputs/monero/contrib/gitian/gitian-osx.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx', '--destination', '../sigs/', 'inputs/monero/contrib/gitian/gitian-osx.yml']) - subprocess.check_call('mv build/out/monero*.tar.bz2 ../out/'+args.version, shell=True) + print('\nCompiling ' + args.version + ' ' + os_name) + infile = 'inputs/monero/contrib/gitian/gitian-' + tag_name + '.yml' + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, infile]) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../sigs/', infile]) + subprocess.check_call('mv build/out/monero-*.' + suffix + ' ../out/'+args.version, shell=True) + print('Moving var/install.log to var/install-' + tag_name + '.log') + subprocess.check_call('mv var/install.log var/install-' + tag_name + '.log', shell=True) + print('Moving var/build.log to var/build-' + tag_name + '.log') + subprocess.check_call('mv var/build.log var/build-' + tag_name + '.log', shell=True) os.chdir(workdir) @@ -142,11 +141,6 @@ def main(): args = parser.parse_args() workdir = os.getcwd() - args.linux = 'l' in args.os - args.android = 'a' in args.os - args.windows = 'w' in args.os - args.macos = 'm' in args.os - args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs']) if args.buildsign: @@ -173,10 +167,11 @@ def main(): os.environ['LXC_GUEST_IP'] = '10.0.3.5' # Disable MacOS build if no SDK found - if args.macos and not os.path.isfile('builder/inputs/MacOSX10.11.sdk.tar.gz'): - args.macos = False + args.nomac = False + if 'm' in args.os and not os.path.isfile('builder/inputs/MacOSX10.11.sdk.tar.gz'): if args.build: print('Cannot build for MacOS, SDK does not exist. Will build for other OSes') + args.nomac = True script_name = os.path.basename(sys.argv[0]) # Signer and version shouldn't be empty From cca6e5c645b51d4dfed7021be12d993d17fab0d6 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Mon, 11 Nov 2019 08:34:47 +0000 Subject: [PATCH 073/469] depends: Add FreeBSD support --- contrib/depends/Makefile | 4 ++++ contrib/depends/hosts/freebsd.mk | 17 ++++++++++++++++ contrib/depends/packages/boost.mk | 1 + contrib/depends/packages/freebsd_base.mk | 23 +++++++++++++++++++++ contrib/depends/packages/libiconv.mk | 1 + contrib/depends/packages/openssl.mk | 2 ++ contrib/depends/packages/packages.mk | 26 +++++++++++++----------- contrib/depends/packages/zeromq.mk | 1 + contrib/depends/toolchain.cmake.in | 7 +++++-- 9 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 contrib/depends/hosts/freebsd.mk create mode 100644 contrib/depends/packages/freebsd_base.mk diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index 7eedee15e..28ec972e4 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -58,6 +58,7 @@ ifeq ($(host_os),) host_os:=$(findstring linux,$(full_host_os)) endif host_os+=$(findstring darwin,$(full_host_os)) +host_os+=$(findstring freebsd,$(full_host_os)) host_os+=$(findstring mingw32,$(full_host_os)) host_os:=$(strip $(host_os)) ifeq ($(host_os),) @@ -74,6 +75,9 @@ endif ifeq ($(host_os),linux) host_cmake=Linux endif +ifeq ($(host_os),freebsd) +host_cmake=FreeBSD +endif ifeq ($(host_os),darwin) host_cmake=Darwin endif diff --git a/contrib/depends/hosts/freebsd.mk b/contrib/depends/hosts/freebsd.mk new file mode 100644 index 000000000..6a7a2fe20 --- /dev/null +++ b/contrib/depends/hosts/freebsd.mk @@ -0,0 +1,17 @@ +freebsd_CC=clang-8 +freebsd_CXX=clang++-8 +freebsd_AR=ar +freebsd_RANLIB=ranlib +freebsd_NM=nm + +freebsd_CFLAGS=-pipe +freebsd_CXXFLAGS=$(freebsd_CFLAGS) + +freebsd_release_CFLAGS=-O2 +freebsd_release_CXXFLAGS=$(freebsd_release_CFLAGS) + +freebsd_debug_CFLAGS=-g -O0 +freebsd_debug_CXXFLAGS=$(freebsd_debug_CFLAGS) + +freebsd_native_toolchain=freebsd_base + diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index 3231df925..43216f47a 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -24,6 +24,7 @@ $(package)_archiver_darwin=$($(package)_libtool) $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale $(package)_cxxflags=-std=c++11 $(package)_cxxflags_linux=-fPIC +$(package)_cxxflags_freebsd=-fPIC endef define $(package)_preprocess_cmds diff --git a/contrib/depends/packages/freebsd_base.mk b/contrib/depends/packages/freebsd_base.mk new file mode 100644 index 000000000..c6a209dcd --- /dev/null +++ b/contrib/depends/packages/freebsd_base.mk @@ -0,0 +1,23 @@ +package=freebsd_base +$(package)_version=11.3 +$(package)_download_path=https://download.freebsd.org/ftp/releases/amd64/$($(package)_version)-RELEASE/ +$(package)_download_file=base.txz +$(package)_file_name=freebsd-base-$($(package)_version).txz +$(package)_sha256_hash=4599023ac136325b86f2fddeec64c1624daa83657e40b00b2ef944c81463a4ff + +define $(package)_extract_cmds + echo $($(package)_sha256_hash) $($(1)_source_dir)/$($(package)_file_name) | sha256sum -c &&\ + tar xf $($(1)_source_dir)/$($(package)_file_name) ./lib/ ./usr/lib/ ./usr/include/ +endef + +define $(package)_build_cmds + mkdir bin &&\ + echo "exec /usr/bin/clang-8 -target x86_64-unknown-freebsd$($(package)_version) --sysroot=$(host_prefix)/native $$$$""@" > bin/clang-8 &&\ + echo "exec /usr/bin/clang++-8 -target x86_64-unknown-freebsd$($(package)_version) --sysroot=$(host_prefix)/native $$$$""@" > bin/clang++-8 &&\ + chmod 755 bin/* +endef + +define $(package)_stage_cmds + mkdir $($(package)_staging_dir)/$(host_prefix)/native &&\ + mv bin lib usr $($(package)_staging_dir)/$(host_prefix)/native +endef diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk index d4995c1b7..698aa8bd3 100644 --- a/contrib/depends/packages/libiconv.mk +++ b/contrib/depends/packages/libiconv.mk @@ -10,6 +10,7 @@ define $(package)_set_vars $(package)_config_opts=--enable-static $(package)_config_opts=--disable-shared $(package)_config_opts_linux=--with-pic + $(package)_config_opts_freebsd=--with-pic endef define $(package)_preprocess_cmds diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk index f3b781f28..e59a6de1c 100644 --- a/contrib/depends/packages/openssl.mk +++ b/contrib/depends/packages/openssl.mk @@ -36,6 +36,7 @@ $(package)_config_opts+=no-zlib $(package)_config_opts+=no-zlib-dynamic $(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) $(package)_config_opts_linux=-fPIC -Wa,--noexecstack +$(package)_config_opts_freebsd=-fPIC -Wa,--noexecstack $(package)_config_opts_x86_64_linux=linux-x86_64 $(package)_config_opts_i686_linux=linux-generic32 $(package)_config_opts_arm_linux=linux-generic32 @@ -49,6 +50,7 @@ $(package)_config_opts_powerpc_linux=linux-generic32 $(package)_config_opts_x86_64_darwin=darwin64-x86_64-cc $(package)_config_opts_x86_64_mingw32=mingw64 $(package)_config_opts_i686_mingw32=mingw +$(package)_config_opts_x86_64_freebsd=BSD-x86_64 endef define $(package)_preprocess_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 9302d5b4d..76c30066c 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,18 +1,22 @@ -ifeq ($(host_os),android) packages:=boost openssl zeromq libiconv -else -packages:=boost openssl zeromq expat ldns libiconv hidapi protobuf libusb -endif -native_packages := native_ccache native_protobuf +native_packages := native_ccache + +hardware_packages := hidapi protobuf libusb +hardware_native_packages := native_protobuf android_native_packages = android_ndk android_packages = ncurses readline sodium -darwin_native_packages = native_biplist native_ds_store native_mac_alias -darwin_packages = sodium-darwin ncurses readline +darwin_native_packages = native_biplist native_ds_store native_mac_alias $(hardware_native_packages) +darwin_packages = sodium-darwin ncurses readline $(hardware_packages) -linux_packages = eudev ncurses readline sodium +# not really native... +freebsd_native_packages = freebsd_base +freebsd_packages = ncurses readline sodium + +linux_packages = eudev ncurses readline sodium $(hardware_packages) +linux_native_packages = $(hardware_native_packages) qt_packages = qt ifeq ($(build_tests),ON) @@ -23,10 +27,8 @@ ifneq ($(host_arch),riscv64) linux_packages += unwind endif -ifeq ($(host_os),mingw32) -packages += icu4c -packages += sodium -endif +mingw32_packages = icu4c sodium $(hardware_packages) +mingw32_native_packages = $(hardware_native_packages) ifneq ($(build_os),darwin) darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus diff --git a/contrib/depends/packages/zeromq.mk b/contrib/depends/packages/zeromq.mk index 46caf14cb..d55f38c33 100644 --- a/contrib/depends/packages/zeromq.mk +++ b/contrib/depends/packages/zeromq.mk @@ -8,6 +8,7 @@ $(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b10 define $(package)_set_vars $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve $(package)_config_opts_linux=--with-pic + $(package)_config_opts_freebsd=--with-pic $(package)_cxxflags=-std=c++11 endef diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 13018ea26..c56f3eb2e 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -1,4 +1,4 @@ -# Set the system name to one of Android, Darwin, Linux, or Windows +# Set the system name to one of Android, Darwin, FreeBSD, Linux, or Windows SET(CMAKE_SYSTEM_NAME @depends@) SET(CMAKE_BUILD_TYPE @release_type@) @@ -23,13 +23,14 @@ SET(Readline_INCLUDE_DIR @prefix@/include) SET(Readline_LIBRARY @prefix@/lib/libreadline.a) SET(Terminfo_LIBRARY @prefix@/lib/libtinfo.a) -if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") SET(LIBUSB-1.0_LIBRARY @prefix@/lib/libusb-1.0.a) SET(LIBUDEV_LIBRARY @prefix@/lib/libudev.a) @@ -40,6 +41,8 @@ SET(Protobuf_INCLUDE_DIRS @prefix@/include CACHE PATH "Protobuf include dir") SET(Protobuf_LIBRARY @prefix@/lib/libprotobuf.a CACHE FILEPATH "Protobuf library") endif() +endif() + SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) From 35b5e917aa2eaa0bf85ed2fdeaf3a6a6103a7413 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Mon, 11 Nov 2019 09:13:47 +0000 Subject: [PATCH 074/469] gitian: add FreeBSD --- contrib/gitian/gitian-build.py | 29 +++---- contrib/gitian/gitian-freebsd.yml | 133 ++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 contrib/gitian/gitian-freebsd.yml diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index f934ffa17..64eb218bb 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -8,6 +8,12 @@ import sys gsigs = 'https://github.com/monero-project/gitian.sigs.git' gbrepo = 'https://github.com/devrandom/gitian-builder.git' +platforms = {'l': ['Linux', 'linux', 'tar.bz2'], + 'a': ['Android', 'android', 'tar.bz2'], + 'f': ['FreeBSD', 'freebsd', 'tar.bz2'], + 'w': ['Windows', 'win', 'zip'], + 'm': ['MacOS', 'osx', 'tar.bz2'] } + def setup(): global args, workdir programs = ['apt-cacher-ng', 'ruby', 'git', 'make', 'wget'] @@ -48,10 +54,6 @@ def rebuild(): print('\nBuilding Dependencies\n') os.makedirs('../out/' + args.version, exist_ok=True) - platforms = {'l': ['Linux', 'linux', 'tar.bz2'], - 'a': ['Android', 'android', 'tar.bz2'], - 'w': ['Windows', 'win', 'zip'], - 'm': ['MacOS', 'osx', 'tar.bz2'] } for i in args.os: if i is 'm' and args.nomac: @@ -76,10 +78,8 @@ def rebuild(): if args.commit_files: print('\nCommitting '+args.version+' Unsigned Sigs\n') os.chdir('sigs') - subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer]) - subprocess.check_call(['git', 'add', args.version+'-android/'+args.signer]) - subprocess.check_call(['git', 'add', args.version+'-win/'+args.signer]) - subprocess.check_call(['git', 'add', args.version+'-osx/'+args.signer]) + for i, v in platforms: + subprocess.check_call(['git', 'add', args.version+'-'+v[1]+'/'+args.signer]) subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) os.chdir(workdir) @@ -104,14 +104,9 @@ def verify(): global args, workdir os.chdir('builder') - print('\nVerifying v'+args.version+' Linux\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-linux', 'inputs/monero/contrib/gitian/gitian-linux.yml']) - print('\nVerifying v'+args.version+' Android\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-android', 'inputs/monero/contrib/gitian/gitian-android.yml']) - print('\nVerifying v'+args.version+' Windows\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-win', 'inputs/monero/contrib/gitian/gitian-win.yml']) - print('\nVerifying v'+args.version+' MacOS\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-osx', 'inputs/monero/contrib/gitian/gitian-osx.yml']) + for i, v in platforms: + print('\nVerifying v'+args.version+' '+v[0]+'\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../sigs/', '-r', args.version+'-'+v[1], 'inputs/monero/contrib/gitian/gitian-'+v[1]+'.yml']) os.chdir(workdir) def main(): @@ -124,7 +119,7 @@ def main(): parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') - parser.add_argument('-o', '--os', dest='os', default='lawm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, a for Android, w for Windows, m for MacOS') + parser.add_argument('-o', '--os', dest='os', default='lafwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, a for Android, f for FreeBSD, w for Windows, m for MacOS') parser.add_argument('-r', '--rebuild', action='store_true', dest='rebuild', help='Redo a Gitian build') parser.add_argument('-R', '--rebuildsign', action='store_true', dest='rebuildsign', help='Redo and sign a Gitian build') parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') diff --git a/contrib/gitian/gitian-freebsd.yml b/contrib/gitian/gitian-freebsd.yml new file mode 100644 index 000000000..3402a2b6c --- /dev/null +++ b/contrib/gitian/gitian-freebsd.yml @@ -0,0 +1,133 @@ +--- +name: "monero-freebsd-0.15" +enable_cache: true +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "curl" +- "clang-8" +- "llvm-8" +- "gperf" +- "gcc-7" +- "g++-7" +- "gcc" +- "g++" +- "binutils-gold" +- "git" +- "pkg-config" +- "build-essential" +- "autoconf" +- "libtool" +- "automake" +- "faketime" +- "bsdmainutils" +- "ca-certificates" +- "python" +- "cmake" +- "ccache" +- "protobuf-compiler" +- "libdbus-1-dev" +- "libharfbuzz-dev" +- "libprotobuf-dev" +- "python3-zmq" +remotes: +- "url": "https://github.com/monero-project/monero.git" + "dir": "monero" +files: [] +script: | + + WRAP_DIR=$HOME/wrapped + HOSTS="x86_64-unknown-freebsd" + FAKETIME_HOST_PROGS="" + FAKETIME_PROGS="clang-8 clang++-8 llvm-ar-8 llvm-ranlib-8 date" + HOST_CFLAGS="-O2 -g" + HOST_CXXFLAGS="-O2 -g" + HOST_LDFLAGS=-static-libstdc++ + + export GZIP="-9n" + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export TZ="UTC" + export BUILD_DIR=`pwd` + mkdir -p ${WRAP_DIR} + if test -n "$GBUILD_CACHE_ENABLED"; then + export SOURCES_PATH=${GBUILD_COMMON_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + fi + + export ZERO_AR_DATE=1 + + function create_global_faketime_wrappers { + for prog in ${FAKETIME_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} + echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${prog} + chmod +x ${WRAP_DIR}/${prog} + done + } + + function create_per-host_faketime_wrappers { + for i in $HOSTS; do + for prog in ${FAKETIME_HOST_PROGS}; do + WRAPPER=${WRAP_DIR}/${i}-${prog} + echo '#!/usr/bin/env bash' > ${WRAPPER} + echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAPPER} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAPPER} + echo "export FAKETIME=\"$1\"" >> ${WRAPPER} + echo "$NDKDIR/${ABI}-$prog \$@" >> ${WRAPPER} + chmod +x ${WRAPPER} + done + done + } + + # Faketime for depends so intermediate results are comparable + DUMMYTIME="2000-01-01 12:00:00" + export PATH_orig=${PATH} + create_global_faketime_wrappers "$DUMMYTIME" + create_per-host_faketime_wrappers "$DUMMYTIME" + export PATH=${WRAP_DIR}:${PATH} + + # gcc 7+ honors SOURCE_DATE_EPOCH, no faketime needed + export SOURCE_DATE_EPOCH=`date -d "$DUMMYTIME" +%s` + + git config --global core.abbrev 9 + cd monero + # Set the version string that gets added to the tar archive name + version="`git describe`" + if [[ $version == *"-"*"-"* ]]; then + version="`git rev-parse --short=9 HEAD`" + version="`echo $version | head -c 9`" + fi + + BASEPREFIX=`pwd`/contrib/depends + # Build dependencies for each host + for i in $HOSTS; do + make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" + done + + # Faketime for binaries + export PATH=${PATH_orig} + create_global_faketime_wrappers "${REFERENCE_DATETIME}" + create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" + + ORIGPATH="$PATH" + # Build in a new dir for each host + for i in ${HOSTS}; do + export PATH=${WRAP_DIR}:${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + mkdir build && cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DCMAKE_BUILD_TYPE=Release + make ${MAKEOPTS} + chmod 755 bin/* + cp ../LICENSE bin + chmod 644 bin/LICENSE + DISTNAME=monero-${i}-${version} + mv bin ${DISTNAME} + find ${DISTNAME}/ | sort | tar --no-recursion --owner=0 --group=0 -c -T - | bzip2 -9 > ${OUTDIR}/${DISTNAME}.tar.bz2 + cd .. + rm -rf build + done + From a7a40e285c7d006b6144de7e66c7cda3f630c25f Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Thu, 14 Nov 2019 10:36:49 +0100 Subject: [PATCH 075/469] Actually concatenate error strings. --- src/rpc/daemon_handler.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index d7e081af3..2dada5521 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -311,42 +311,42 @@ namespace rpc if (tvc.m_double_spend) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "double spend"; + res.error_details += "double spend"; } if (tvc.m_invalid_input) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "invalid input"; + res.error_details += "invalid input"; } if (tvc.m_invalid_output) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "invalid output"; + res.error_details += "invalid output"; } if (tvc.m_too_big) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "too big"; + res.error_details += "too big"; } if (tvc.m_overspend) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "overspend"; + res.error_details += "overspend"; } if (tvc.m_fee_too_low) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "fee too low"; + res.error_details += "fee too low"; } if (tvc.m_not_rct) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "tx is not ringct"; + res.error_details += "tx is not ringct"; } if (tvc.m_too_few_outputs) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "too few outputs"; + res.error_details += "too few outputs"; } if (res.error_details.empty()) { From feef1c6aace7c320502feb179b64a33101a62f67 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 11 Nov 2019 12:13:21 +0000 Subject: [PATCH 076/469] epee: fix peer ids being truncated on display --- contrib/epee/include/string_tools.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 1be5eb5e1..319c0121b 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -188,8 +188,10 @@ POP_WARNINGS return boost::lexical_cast(val); } //---------------------------------------------------------------------------- - inline std::string to_string_hex(uint32_t val) + template + inline std::string to_string_hex(const T &val) { + static_assert(std::is_arithmetic::value, "only arithmetic types"); std::stringstream ss; ss << std::hex << val; std::string s; From 09f59eccad787b98006490dcd6da7e6c29487dac Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 14 Nov 2019 19:49:57 +0000 Subject: [PATCH 077/469] wallet: set non-empty error string on connection failure --- src/wallet/node_rpc_proxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 15ea26044..005b0bafa 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -36,7 +36,7 @@ do { \ CHECK_AND_ASSERT_MES(error.code == 0, error.message, error.message); \ handle_payment_changes(res, std::integral_constant::Has>()); \ - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); \ + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); \ /* empty string -> not connection */ \ CHECK_AND_ASSERT_MES(!res.status.empty(), res.status, "No connection to daemon"); \ CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Daemon busy"); \ From be3a432653fbb8e7efa938724aa97bb745798bd8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 15 Nov 2019 13:21:22 +0000 Subject: [PATCH 078/469] translations: find lrelease disguised as lrelease-qt5 --- utils/translations/build-translations.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/translations/build-translations.sh b/utils/translations/build-translations.sh index 1217dca0a..1a2aa4492 100755 --- a/utils/translations/build-translations.sh +++ b/utils/translations/build-translations.sh @@ -6,6 +6,10 @@ then lrelease=`which lrelease-qt4 2> /dev/null` fi if test -z "$lrelease" +then + lrelease=`which lrelease-qt5 2> /dev/null` +fi +if test -z "$lrelease" then echo "lrelease not found" exit 1 From 56dced2d6d36569de870498004ca883602115412 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 15 Nov 2019 13:21:46 +0000 Subject: [PATCH 079/469] translations: fix "monero_" prefix when taking languages from file --- utils/translations/build-translations.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/translations/build-translations.sh b/utils/translations/build-translations.sh index 1a2aa4492..c868a691f 100755 --- a/utils/translations/build-translations.sh +++ b/utils/translations/build-translations.sh @@ -21,7 +21,7 @@ then languages="" for language in $(cat translations/ready) do - languages="$languages translations/$language.ts" + languages="$languages translations/monero_$language.ts" done else languages="translations/*.ts" From 08635a08755956be12c68ba40bcc96b8828ff2a9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 12 Nov 2019 18:40:37 +0000 Subject: [PATCH 080/469] blockchain: speedup fetching pruned contiguous tx blobs About twice as fast, very roughly --- src/blockchain_db/blockchain_db.h | 16 ++++++++++++ src/blockchain_db/lmdb/db_lmdb.cpp | 42 ++++++++++++++++++++++++++++++ src/blockchain_db/lmdb/db_lmdb.h | 1 + src/blockchain_db/testdb.h | 1 + src/cryptonote_core/blockchain.cpp | 13 ++++++--- 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index d1e4919be..21700d5d3 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1252,6 +1252,22 @@ public: */ virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0; + /** + * @brief fetches a number of pruned transaction blob from the given hash, in canonical blockchain order + * + * The subclass should return the pruned transactions stored from the one with the given + * hash. + * + * If the first transaction does not exist, the subclass should return false. + * If the first transaction exists, but there are fewer transactions starting with it + * than requested, the subclass should return false. + * + * @param h the hash to look for + * + * @return true iff the transactions were found + */ + virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const = 0; + /** * @brief fetches the prunable transaction blob with the given hash * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index f978ef307..495d70e7d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3033,6 +3033,48 @@ bool BlockchainLMDB::get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobd return true; } +bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + if (!count) + return true; + + TXN_PREFIX_RDONLY(); + RCURSOR(tx_indices); + RCURSOR(txs_pruned); + + bd.reserve(bd.size() + count); + + MDB_val_set(v, h); + MDB_val result; + int res = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + if (res == MDB_NOTFOUND) + return false; + if (res) + throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", res).c_str())); + + const txindex *tip = (const txindex *)v.mv_data; + const uint64_t id = tip->data.tx_id; + MDB_val_set(val_tx_id, id); + MDB_cursor_op op = MDB_SET; + while (count--) + { + res = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result, op); + op = MDB_NEXT; + if (res == MDB_NOTFOUND) + return false; + if (res) + throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx blob", res).c_str())); + bd.emplace_back(reinterpret_cast(result.mv_data), result.mv_size); + } + + TXN_POSTFIX_RDONLY(); + + return true; +} + bool BlockchainLMDB::get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 61a551476..5dd44ec67 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -254,6 +254,7 @@ public: virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; + virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const; virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index ac19fae25..4176e087c 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -69,6 +69,7 @@ public: virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const override { return cryptonote::blobdata(); } virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } + virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const { return false; } virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; } virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d22158dfc..a943a7093 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2498,10 +2498,17 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons block b; CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first.first, b), false, "internal error, invalid block"); blocks.back().first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash; - std::vector mis; std::vector txs; - get_transactions_blobs(b.tx_hashes, txs, mis, pruned); - CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); + if (pruned) + { + CHECK_AND_ASSERT_MES(m_db->get_pruned_tx_blobs_from(b.tx_hashes.front(), b.tx_hashes.size(), txs), false, "Failed to retrieve all transactions needed"); + } + else + { + std::vector mis; + get_transactions_blobs(b.tx_hashes, txs, mis, pruned); + CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); + } size += blocks.back().first.first.size(); for (const auto &t: txs) size += t.size(); From b9b5c473d165c4ed59d012a9cd8e4c96dc546b74 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 12 Nov 2019 18:38:38 +0000 Subject: [PATCH 081/469] threadpool: use std::move when taking an element off the queue It has a std::function, which can have a capture context, and the function runtime might be small --- src/common/threadpool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 2748c798c..38f52c45b 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -145,7 +145,7 @@ void threadpool::run(bool flush) { if (!running) break; active++; - e = queue.front(); + e = std::move(queue.front()); queue.pop_front(); lock.unlock(); ++depth; From 6efeefbca20d573b76513869b688f1ea1daae709 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 15 Nov 2019 19:38:41 +0000 Subject: [PATCH 082/469] epee: set application/json MIME type on json errors --- contrib/epee/include/net/http_server_handlers_map2.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 07ed8157b..0c0653cd6 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -120,6 +120,7 @@ #define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \ { \ uint64_t ticks = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ epee::serialization::portable_storage ps; \ if(!ps.load_from_json(query_info.m_body)) \ { \ @@ -148,6 +149,7 @@ #define PREPARE_OBJECTS_FROM_JSON(command_type) \ handled = true; \ + response_info.m_mime_tipe = "application/json"; \ boost::value_initialized > req_; \ epee::json_rpc::request& req = static_cast&>(req_);\ if(!req.load(ps)) \ From 1b2953f03d4a3e393d19f779f4e82c8d9a38e664 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Fri, 15 Nov 2019 23:39:10 +0000 Subject: [PATCH 083/469] Add __pycache__ directory to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 0ece7cb75..08c310e66 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ nbproject .idea/ /testnet + +__pycache__/ From 024a1c7ddf0f510f149edb7f64aa54e100fd196f Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Wed, 13 Nov 2019 13:50:17 +0000 Subject: [PATCH 084/469] Add ARFLAGS Needed to invoke deterministic mode on some archivers --- contrib/depends/builders/default.mk | 2 +- contrib/depends/funcs.mk | 9 ++++++ contrib/depends/hosts/android.mk | 2 ++ contrib/depends/hosts/darwin.mk | 1 + contrib/depends/hosts/default.mk | 2 +- contrib/depends/hosts/freebsd.mk | 1 + contrib/depends/hosts/linux.mk | 1 + contrib/depends/hosts/mingw32.mk | 1 + contrib/depends/packages/boost.mk | 4 ++- contrib/depends/packages/eudev.mk | 2 +- contrib/depends/packages/hidapi.mk | 2 +- contrib/depends/packages/libiconv.mk | 2 +- contrib/depends/packages/libusb.mk | 4 +-- contrib/depends/packages/ncurses.mk | 2 +- contrib/depends/packages/openssl.mk | 6 ++-- contrib/depends/packages/protobuf.mk | 2 +- contrib/depends/packages/readline.mk | 2 +- contrib/depends/packages/sodium.mk | 2 +- contrib/depends/packages/unwind.mk | 2 +- contrib/depends/packages/zeromq.mk | 2 +- .../depends/patches/boost/fix_aroptions.patch | 28 +++++++++++++++++++ .../depends/patches/openssl/fix_arflags.patch | 24 ++++++++++++++++ contrib/gitian/gitian-android.yml | 6 ++-- 23 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 contrib/depends/patches/boost/fix_aroptions.patch create mode 100644 contrib/depends/patches/openssl/fix_arflags.patch diff --git a/contrib/depends/builders/default.mk b/contrib/depends/builders/default.mk index f097db65d..c4191435d 100644 --- a/contrib/depends/builders/default.mk +++ b/contrib/depends/builders/default.mk @@ -17,4 +17,4 @@ define add_build_flags_func build_$(build_arch)_$(build_os)_$1 += $(build_$(build_os)_$1) build_$1=$$(build_$(build_arch)_$(build_os)_$1) endef -$(foreach flags, CFLAGS CXXFLAGS LDFLAGS, $(eval $(call add_build_flags_func,$(flags)))) +$(foreach flags, CFLAGS CXXFLAGS ARFLAGS LDFLAGS, $(eval $(call add_build_flags_func,$(flags)))) diff --git a/contrib/depends/funcs.mk b/contrib/depends/funcs.mk index 469144361..355ae07eb 100644 --- a/contrib/depends/funcs.mk +++ b/contrib/depends/funcs.mk @@ -10,6 +10,7 @@ $(1)_libtool=$($($(1)_type)_LIBTOOL) $(1)_nm=$($($(1)_type)_NM) $(1)_cflags=$($($(1)_type)_CFLAGS) $($($(1)_type)_$(release_type)_CFLAGS) $(1)_cxxflags=$($($(1)_type)_CXXFLAGS) $($($(1)_type)_$(release_type)_CXXFLAGS) +$(1)_arflags=$($($(1)_type)_ARFLAGS) $($($(1)_type)_$(release_type)_ARFLAGS) $(1)_ldflags=$($($(1)_type)_LDFLAGS) $($($(1)_type)_$(release_type)_LDFLAGS) -L$($($(1)_type)_prefix)/lib $(1)_cppflags=$($($(1)_type)_CPPFLAGS) $($($(1)_type)_$(release_type)_CPPFLAGS) -I$($($(1)_type)_prefix)/include $(1)_recipe_hash:= @@ -102,6 +103,11 @@ $(1)_cxxflags+=$($(1)_cxxflags_$(host_arch)) $($(1)_cxxflags_$(host_arch)_$(rele $(1)_cxxflags+=$($(1)_cxxflags_$(host_os)) $($(1)_cxxflags_$(host_os)_$(release_type)) $(1)_cxxflags+=$($(1)_cxxflags_$(host_arch)_$(host_os)) $($(1)_cxxflags_$(host_arch)_$(host_os)_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(host_arch)) $($(1)_arflags_$(host_arch)_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(host_os)) $($(1)_arflags_$(host_os)_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(host_arch)_$(host_os)) $($(1)_arflags_$(host_arch)_$(host_os)_$(release_type)) + $(1)_cppflags+=$($(1)_cppflags_$(release_type)) $(1)_cppflags+=$($(1)_cppflags_$(host_arch)) $($(1)_cppflags_$(host_arch)_$(release_type)) $(1)_cppflags+=$($(1)_cppflags_$(host_os)) $($(1)_cppflags_$(host_os)_$(release_type)) @@ -143,6 +149,9 @@ endif ifneq ($($(1)_ar),) $(1)_autoconf += AR="$$($(1)_ar)" endif +ifneq ($($(1)_arflags),) +$(1)_autoconf += ARFLAGS="$$($(1)_arflags)" +endif ifneq ($($(1)_cflags),) $(1)_autoconf += CFLAGS="$$($(1)_cflags)" endif diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk index 2a24435d4..d6f8b99dd 100644 --- a/contrib/depends/hosts/android.mk +++ b/contrib/depends/hosts/android.mk @@ -6,9 +6,11 @@ endif android_CC=$(host_toolchain)clang android_CXX=$(host_toolchain)clang++ +android_RANLIB=: android_CFLAGS=-pipe android_CXXFLAGS=$(android_CFLAGS) +android_ARFLAGS=crsD android_release_CFLAGS=-O2 android_release_CXXFLAGS=$(android_release_CFLAGS) diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk index 6f8f2ffe2..7b5c8b051 100644 --- a/contrib/depends/hosts/darwin.mk +++ b/contrib/depends/hosts/darwin.mk @@ -7,6 +7,7 @@ darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sys darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) +darwin_ARFLAGS=cr darwin_release_CFLAGS=-O1 darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) diff --git a/contrib/depends/hosts/default.mk b/contrib/depends/hosts/default.mk index 6f60d6b3f..2e7f9fa23 100644 --- a/contrib/depends/hosts/default.mk +++ b/contrib/depends/hosts/default.mk @@ -23,4 +23,4 @@ host_$(release_type)_$1 = $$($(host_arch)_$(host_os)_$(release_type)_$1) endef $(foreach tool,CC CXX AR RANLIB STRIP NM LIBTOOL OTOOL INSTALL_NAME_TOOL,$(eval $(call add_host_tool_func,$(tool)))) -$(foreach flags,CFLAGS CXXFLAGS CPPFLAGS LDFLAGS, $(eval $(call add_host_flags_func,$(flags)))) +$(foreach flags,CFLAGS CXXFLAGS ARFLAGS CPPFLAGS LDFLAGS, $(eval $(call add_host_flags_func,$(flags)))) diff --git a/contrib/depends/hosts/freebsd.mk b/contrib/depends/hosts/freebsd.mk index 6a7a2fe20..2e3b5933e 100644 --- a/contrib/depends/hosts/freebsd.mk +++ b/contrib/depends/hosts/freebsd.mk @@ -6,6 +6,7 @@ freebsd_NM=nm freebsd_CFLAGS=-pipe freebsd_CXXFLAGS=$(freebsd_CFLAGS) +freebsd_ARFLAGS=cr freebsd_release_CFLAGS=-O2 freebsd_release_CXXFLAGS=$(freebsd_release_CFLAGS) diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk index b13a0f1ad..912fdb03c 100644 --- a/contrib/depends/hosts/linux.mk +++ b/contrib/depends/hosts/linux.mk @@ -1,5 +1,6 @@ linux_CFLAGS=-pipe linux_CXXFLAGS=$(linux_CFLAGS) +linux_ARFLAGS=cr linux_release_CFLAGS=-O2 linux_release_CXXFLAGS=$(linux_release_CFLAGS) diff --git a/contrib/depends/hosts/mingw32.mk b/contrib/depends/hosts/mingw32.mk index dbfb62fdc..ccc4c5082 100644 --- a/contrib/depends/hosts/mingw32.mk +++ b/contrib/depends/hosts/mingw32.mk @@ -1,5 +1,6 @@ mingw32_CFLAGS=-pipe mingw32_CXXFLAGS=$(mingw32_CFLAGS) +mingw32_ARFLAGS=cr mingw32_release_CFLAGS=-O2 mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index 43216f47a..0d241928e 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -4,6 +4,7 @@ $(package)_download_path=https://dl.bintray.com/boostorg/release/1.64.0/source/ $(package)_file_name=$(package)_$($(package)_version).tar.bz2 $(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 $(package)_dependencies=libiconv +$(package)_patches=fix_aroptions.patch define $(package)_set_vars $(package)_config_opts_release=variant=release @@ -28,7 +29,8 @@ $(package)_cxxflags_freebsd=-fPIC endef define $(package)_preprocess_cmds - echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STR IP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam + patch -p1 < $($(package)_patch_dir)/fix_aroptions.patch &&\ + echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$($(package)_arflags)\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef define $(package)_config_cmds diff --git a/contrib/depends/packages/eudev.mk b/contrib/depends/packages/eudev.mk index a7795b777..0e930df93 100644 --- a/contrib/depends/packages/eudev.mk +++ b/contrib/depends/packages/eudev.mk @@ -9,7 +9,7 @@ define $(package)_set_vars endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmd diff --git a/contrib/depends/packages/hidapi.mk b/contrib/depends/packages/hidapi.mk index a27df04fa..b76ef1548 100644 --- a/contrib/depends/packages/hidapi.mk +++ b/contrib/depends/packages/hidapi.mk @@ -18,7 +18,7 @@ endef define $(package)_config_cmds ./bootstrap &&\ - $($(package)_autoconf) $($(package)_config_opts) + $($(package)_autoconf) $($(package)_config_opts) AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk index 698aa8bd3..eac8b4331 100644 --- a/contrib/depends/packages/libiconv.mk +++ b/contrib/depends/packages/libiconv.mk @@ -19,7 +19,7 @@ define $(package)_preprocess_cmds endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk index d865d2a17..348c410a7 100644 --- a/contrib/depends/packages/libusb.mk +++ b/contrib/depends/packages/libusb.mk @@ -19,11 +19,11 @@ ifneq ($(host_os),darwin) define $(package)_config_cmds cp -f $(BASEDIR)/config.guess config.guess &&\ cp -f $(BASEDIR)/config.sub config.sub &&\ - $($(package)_autoconf) + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef else define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef endif diff --git a/contrib/depends/packages/ncurses.mk b/contrib/depends/packages/ncurses.mk index 1acf1faca..d8fdf351c 100644 --- a/contrib/depends/packages/ncurses.mk +++ b/contrib/depends/packages/ncurses.mk @@ -7,7 +7,7 @@ $(package)_patches=fallback.c define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" - $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" ARFLAGS=$($(package)_arflags) cf_cv_ar_flags="" $(package)_config_env_darwin=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" $(package)_config_opts=--prefix=$(host_prefix) $(package)_config_opts+=--disable-shared diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk index e59a6de1c..4b07d08ba 100644 --- a/contrib/depends/packages/openssl.mk +++ b/contrib/depends/packages/openssl.mk @@ -3,9 +3,10 @@ $(package)_version=1.0.2r $(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=ae51d08bba8a83958e894946f15303ff894d75c2b8bbd44a852b64e3fe11d0d6 +$(package)_patches=fix_arflags.patch define $(package)_set_vars -$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" +$(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" $(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl $(package)_config_opts+=no-capieng $(package)_config_opts+=no-dso @@ -56,7 +57,8 @@ endef define $(package)_preprocess_cmds sed -i.old "/define DATE/d" util/mkbuildinf.pl && \ sed -i.old "s|engines apps test|engines|" Makefile.org && \ - sed -i -e "s/-mandroid //" Configure + sed -i -e "s/-mandroid //" Configure && \ + patch < $($(package)_patch_dir)/fix_arflags.patch endef define $(package)_config_cmds diff --git a/contrib/depends/packages/protobuf.mk b/contrib/depends/packages/protobuf.mk index 81fa78a3f..ad1098975 100644 --- a/contrib/depends/packages/protobuf.mk +++ b/contrib/depends/packages/protobuf.mk @@ -12,7 +12,7 @@ define $(package)_set_vars endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/readline.mk b/contrib/depends/packages/readline.mk index 29ae783a2..b6e6a451a 100644 --- a/contrib/depends/packages/readline.mk +++ b/contrib/depends/packages/readline.mk @@ -7,7 +7,7 @@ $(package)_dependencies=ncurses define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" - $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" LDFLAGS="-L$(host_prefix)/lib" + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" LDFLAGS="-L$(host_prefix)/lib" ARFLAGS=$($(package)_arflags) $(package)_config_env_darwin=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" $(package)_config_opts+=--prefix=$(host_prefix) $(package)_config_opts+=--exec-prefix=$(host_prefix) diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index cdc690e69..ff4c0f289 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -14,7 +14,7 @@ endef define $(package)_config_cmds ./autogen.sh &&\ patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch &&\ - $($(package)_autoconf) $($(package)_config_opts) + $($(package)_autoconf) $($(package)_config_opts) AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk index fddbd0561..8b8903a9a 100644 --- a/contrib/depends/packages/unwind.mk +++ b/contrib/depends/packages/unwind.mk @@ -7,7 +7,7 @@ $(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65 define $(package)_config_cmds cp -f $(BASEDIR)/config.guess config/config.guess &&\ cp -f $(BASEDIR)/config.sub config/config.sub &&\ - $($(package)_autoconf) --disable-shared --enable-static + $($(package)_autoconf) --disable-shared --enable-static AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/zeromq.mk b/contrib/depends/packages/zeromq.mk index d55f38c33..55941e67d 100644 --- a/contrib/depends/packages/zeromq.mk +++ b/contrib/depends/packages/zeromq.mk @@ -20,7 +20,7 @@ define $(package)_preprocess_cmds endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) endef define $(package)_build_cmds diff --git a/contrib/depends/patches/boost/fix_aroptions.patch b/contrib/depends/patches/boost/fix_aroptions.patch new file mode 100644 index 000000000..5b2ec1006 --- /dev/null +++ b/contrib/depends/patches/boost/fix_aroptions.patch @@ -0,0 +1,28 @@ +--- boost_1_64_0/tools/build/src/tools/gcc.jam.O 2017-04-17 03:22:26.000000000 +0100 ++++ boost_1_64_0/tools/build/src/tools/gcc.jam 2019-11-15 15:46:16.957937137 +0000 +@@ -243,6 +243,8 @@ + { + ECHO notice: using gcc archiver :: $(condition) :: $(archiver[1]) ; + } ++ local arflags = [ feature.get-values : $(options) ] ; ++ toolset.flags gcc.archive .ARFLAGS $(condition) : $(arflags) ; + + # - Ranlib. + local ranlib = [ common.get-invocation-command gcc +@@ -970,6 +972,7 @@ + # logic in intel-linux, but that is hardly worth the trouble as on Linux, 'ar' + # is always available. + .AR = ar ; ++.ARFLAGS = rc ; + .RANLIB = ranlib ; + + toolset.flags gcc.archive AROPTIONS ; +@@ -1011,7 +1014,7 @@ + # + actions piecemeal archive + { +- "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" ++ "$(.AR)" $(AROPTIONS) $(.ARFLAGS) "$(<)" "$(>)" + "$(.RANLIB)" "$(<)" + } + diff --git a/contrib/depends/patches/openssl/fix_arflags.patch b/contrib/depends/patches/openssl/fix_arflags.patch new file mode 100644 index 000000000..2d2900d80 --- /dev/null +++ b/contrib/depends/patches/openssl/fix_arflags.patch @@ -0,0 +1,24 @@ +--- Makefile.org.O 2019-02-26 14:20:20.000000000 +0000 ++++ Makefile.org 2019-11-15 13:05:54.370086856 +0000 +@@ -63,8 +63,8 @@ + PEX_LIBS= + EX_LIBS= + EXE_EXT= +-ARFLAGS= +-AR=ar $(ARFLAGS) r ++ARFLAGS= r ++AR=ar $(ARFLAGS) + RANLIB= ranlib + RC= windres + NM= nm +--- Configure.O 2019-02-26 14:20:20.000000000 +0000 ++++ Configure 2019-11-16 07:43:14.933990774 +0000 +@@ -1251,7 +1251,7 @@ + my $shared_extension = $fields[$idx_shared_extension]; + my $ranlib = $ENV{'RANLIB'} || $fields[$idx_ranlib]; + my $ar = $ENV{'AR'} || "ar"; +-my $arflags = $fields[$idx_arflags]; ++my $arflags = $ENV{'ARFLAGS'} || $fields[$idx_arflags]; + my $windres = $ENV{'RC'} || $ENV{'WINDRES'} || "windres"; + my $multilib = $fields[$idx_multilib]; + diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml index ce213e056..bade0475f 100644 --- a/contrib/gitian/gitian-android.yml +++ b/contrib/gitian/gitian-android.yml @@ -39,7 +39,7 @@ script: | WRAP_DIR=$HOME/wrapped HOSTS="arm-linux-android aarch64-linux-android" - FAKETIME_HOST_PROGS="clang clang++ ar ranlib nm" + FAKETIME_HOST_PROGS="clang clang++ ar nm" FAKETIME_PROGS="date" HOST_CFLAGS="-O2 -g" HOST_CXXFLAGS="-O2 -g" @@ -117,12 +117,10 @@ script: | export PATH=${PATH_orig} create_global_faketime_wrappers "${REFERENCE_DATETIME}" create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" - export PATH=${WRAP_DIR}:${PATH} - ORIGPATH="$PATH" # Build in a new dir for each host for i in ${HOSTS}; do - export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + export PATH=${WRAP_DIR}:${BASEPREFIX}/${i}/native/bin:${PATH_orig} mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DCMAKE_BUILD_TYPE=Release make ${MAKEOPTS} From b14d9abca90490d0137048d71305a3a42f14fc34 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Wed, 13 Nov 2019 14:23:31 +0000 Subject: [PATCH 085/469] Use standard time for depends caches streamline, remove obsolete wrappers --- contrib/gitian/gitian-android.yml | 10 +++--- contrib/gitian/gitian-freebsd.yml | 14 ++++----- contrib/gitian/gitian-linux.yml | 51 +++++++++++++++---------------- contrib/gitian/gitian-osx.yml | 3 +- contrib/gitian/gitian-win.yml | 47 ++++++++++++---------------- 5 files changed, 59 insertions(+), 66 deletions(-) diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml index bade0475f..02614b1a5 100644 --- a/contrib/gitian/gitian-android.yml +++ b/contrib/gitian/gitian-android.yml @@ -46,7 +46,6 @@ script: | HOST_LDFLAGS=-static-libstdc++ export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" export TZ="UTC" export BUILD_DIR=`pwd` mkdir -p ${WRAP_DIR} @@ -89,14 +88,13 @@ script: | } # Faketime for depends so intermediate results are comparable - DUMMYTIME="2000-01-01 12:00:00" export PATH_orig=${PATH} - create_global_faketime_wrappers "$DUMMYTIME" - create_per-host_faketime_wrappers "$DUMMYTIME" + create_global_faketime_wrappers "2000-01-01 12:00:00" + create_per-host_faketime_wrappers "2000-01-01 12:00:00" export PATH=${WRAP_DIR}:${PATH} # gcc 7+ honors SOURCE_DATE_EPOCH, no faketime needed - export SOURCE_DATE_EPOCH=`date -d "$DUMMYTIME" +%s` + export SOURCE_DATE_EPOCH=`date -d 2000-01-01T12:00:00 +%s` git config --global core.abbrev 9 cd monero @@ -109,6 +107,7 @@ script: | BASEPREFIX=`pwd`/contrib/depends # Build dependencies for each host + export TAR_OPTIONS=--mtime=2000-01-01T12:00:00 for i in $HOSTS; do make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" done @@ -119,6 +118,7 @@ script: | create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" # Build in a new dir for each host + export TAR_OPTIONS=--mtime=${REFERENCE_DATE}T${REFERENCE_TIME} for i in ${HOSTS}; do export PATH=${WRAP_DIR}:${BASEPREFIX}/${i}/native/bin:${PATH_orig} mkdir build && cd build diff --git a/contrib/gitian/gitian-freebsd.yml b/contrib/gitian/gitian-freebsd.yml index 3402a2b6c..0220b82a5 100644 --- a/contrib/gitian/gitian-freebsd.yml +++ b/contrib/gitian/gitian-freebsd.yml @@ -8,7 +8,6 @@ architectures: packages: - "curl" - "clang-8" -- "llvm-8" - "gperf" - "gcc-7" - "g++-7" @@ -41,13 +40,12 @@ script: | WRAP_DIR=$HOME/wrapped HOSTS="x86_64-unknown-freebsd" FAKETIME_HOST_PROGS="" - FAKETIME_PROGS="clang-8 clang++-8 llvm-ar-8 llvm-ranlib-8 date" + FAKETIME_PROGS="clang-8 clang++-8 date" HOST_CFLAGS="-O2 -g" HOST_CXXFLAGS="-O2 -g" HOST_LDFLAGS=-static-libstdc++ export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" export TZ="UTC" export BUILD_DIR=`pwd` mkdir -p ${WRAP_DIR} @@ -85,14 +83,13 @@ script: | } # Faketime for depends so intermediate results are comparable - DUMMYTIME="2000-01-01 12:00:00" export PATH_orig=${PATH} - create_global_faketime_wrappers "$DUMMYTIME" - create_per-host_faketime_wrappers "$DUMMYTIME" + create_global_faketime_wrappers "2000-01-01 12:00:00" + create_per-host_faketime_wrappers "2000-01-01 12:00:00" export PATH=${WRAP_DIR}:${PATH} # gcc 7+ honors SOURCE_DATE_EPOCH, no faketime needed - export SOURCE_DATE_EPOCH=`date -d "$DUMMYTIME" +%s` + export SOURCE_DATE_EPOCH=`date -d 2000-01-01T12:00:00 +%s` git config --global core.abbrev 9 cd monero @@ -105,6 +102,7 @@ script: | BASEPREFIX=`pwd`/contrib/depends # Build dependencies for each host + export TAR_OPTIONS=--mtime=2000-01-01T12:00:00 for i in $HOSTS; do make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" done @@ -116,6 +114,8 @@ script: | ORIGPATH="$PATH" # Build in a new dir for each host + export SOURCE_DATE_EPOCH=`date -d ${REFERENCE_DATE}T${REFERENCE_TIME} +%s` + export TAR_OPTIONS=--mtime=${REFERENCE_DATE}T${REFERENCE_TIME} for i in ${HOSTS}; do export PATH=${WRAP_DIR}:${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index d2124e45c..9c2ebac9b 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -50,14 +50,13 @@ script: | WRAP_DIR=$HOME/wrapped HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu" - FAKETIME_HOST_PROGS="gcc g++" - FAKETIME_PROGS="date ar ranlib nm" + FAKETIME_HOST_PROGS="" + FAKETIME_PROGS="date" HOST_CFLAGS="-O2 -g" HOST_CXXFLAGS="-O2 -g" HOST_LDFLAGS=-static-libstdc++ export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" export TZ="UTC" export BUILD_DIR=`pwd` mkdir -p ${WRAP_DIR} @@ -105,29 +104,15 @@ script: | # x86 needs /usr/include/i386-linux-gnu/asm pointed to /usr/include/x86_64-linux-gnu/asm, # but we can't write there. Instead, create a link here and force it to be included in the - # search paths by wrapping gcc/g++. + # search paths. + # This problem goes away if linux-libc-dev:i386 pkg exists, but it's not in bionic. - mkdir -p $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu - rm -f $WRAP_DIR/extra_includes/i686-pc-linux-gnu/asm - ln -s /usr/include/x86_64-linux-gnu/asm $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu/asm + mkdir -p $EXTRA_INCLUDES_BASE/i686-linux-gnu + rm -f $WRAP_DIR/extra_includes/i686-linux-gnu/asm + ln -s /usr/include/x86_64-linux-gnu/asm $EXTRA_INCLUDES_BASE/i686-linux-gnu/asm - for prog in gcc g++; do - rm -f ${WRAP_DIR}/${prog} - cat << EOF > ${WRAP_DIR}/${prog} - #!/usr/bin/env bash - REAL="`which -a ${prog}-7 | grep -v ${WRAP_DIR}/${prog} | head -1`" - for var in "\$@" - do - if [ "\$var" = "-m32" ]; then - export C_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu" - export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu" - break - fi - done - \$REAL \$@ - EOF - chmod +x ${WRAP_DIR}/${prog} - done + # gcc 7+ honors SOURCE_DATE_EPOCH, no faketime needed + export SOURCE_DATE_EPOCH=`date -d 2000-01-01T12:00:00 +%s` git config --global core.abbrev 9 cd monero @@ -140,13 +125,17 @@ script: | BASEPREFIX=`pwd`/contrib/depends # Build dependencies for each host + export TAR_OPTIONS=--mtime=2000-01-01T12:00:00 for i in $HOSTS; do EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" if [ -d "$EXTRA_INCLUDES" ]; then - export HOST_ID_SALT="$EXTRA_INCLUDES" + export C_INCLUDE_PATH="$EXTRA_INCLUDES" + export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES" + else + unset C_INCLUDE_PATH + unset CPLUS_INCLUDE_PATH fi make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" V=1 - unset HOST_ID_SALT done # Faketime for binaries @@ -157,9 +146,19 @@ script: | ORIGPATH="$PATH" # Build in a new dir for each host + export SOURCE_DATE_EPOCH=`date -d ${REFERENCE_DATE}T${REFERENCE_TIME} +%s` + export TAR_OPTIONS=--mtime=${REFERENCE_DATE}T${REFERENCE_TIME} for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build + EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" + if [ -d "$EXTRA_INCLUDES" ]; then + export C_INCLUDE_PATH="$EXTRA_INCLUDES" + export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES" + else + unset C_INCLUDE_PATH + unset CPLUS_INCLUDE_PATH + fi cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON make ${MAKEOPTS} chmod 755 bin/* diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index 1f9928bb5..62913cb3e 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -35,7 +35,6 @@ script: | FAKETIME_PROGS="ar ranlib date dmg genisoimage" export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" export TZ="UTC" export BUILD_DIR=`pwd` mkdir -p ${WRAP_DIR} @@ -92,6 +91,7 @@ script: | tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/MacOSX10.11.sdk.tar.gz # Build dependencies for each host + export TAR_OPTIONS=--mtime=2000-01-01T12:00:00 for i in $HOSTS; do make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" done @@ -104,6 +104,7 @@ script: | ORIGPATH="$PATH" # Build in a new dir for each host + export TAR_OPTIONS=--mtime=${REFERENCE_DATE}T${REFERENCE_TIME} for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index 7bfb14a95..bd85695c1 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -22,6 +22,19 @@ packages: - "python" - "rename" - "cmake" +alternatives: +- + package: "i686-w64-mingw32-g++" + path: "/usr/bin/i686-w64-mingw32-g++-posix" +- + package: "i686-w64-mingw32-gcc" + path: "/usr/bin/i686-w64-mingw32-gcc-posix" +- + package: "x86_64-w64-mingw32-g++" + path: "/usr/bin/x86_64-w64-mingw32-g++-posix" +- + package: "x86_64-w64-mingw32-gcc" + path: "/usr/bin/x86_64-w64-mingw32-gcc-posix" remotes: - "url": "https://github.com/monero-project/monero.git" "dir": "monero" @@ -29,13 +42,12 @@ files: [] script: | WRAP_DIR=$HOME/wrapped HOSTS="i686-w64-mingw32 x86_64-w64-mingw32" - FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy" + FAKETIME_HOST_PROGS="windres objcopy" FAKETIME_PROGS="date zip" HOST_CFLAGS="-O2 -g" HOST_CXXFLAGS="-O2 -g" export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" export TZ="UTC" export BUILD_DIR=`pwd` mkdir -p ${WRAP_DIR} @@ -69,37 +81,15 @@ script: | done } - function create_per-host_linker_wrapper { - # This is only needed for trusty, as the mingw linker leaks a few bytes of - # heap, causing non-determinism. See discussion in https://github.com/bitcoin/bitcoin/pull/6900 - for i in $HOSTS; do - mkdir -p ${WRAP_DIR}/${i} - for prog in collect2; do - echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}/${prog} - REAL=$(${i}-gcc -print-prog-name=${prog}) - echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog} - echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog} - chmod +x ${WRAP_DIR}/${i}/${prog} - done - for prog in gcc g++; do - echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} - echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} - chmod +x ${WRAP_DIR}/${i}-${prog} - done - done - } - # Faketime for depends so intermediate results are comparable export PATH_orig=${PATH} create_global_faketime_wrappers "2000-01-01 12:00:00" create_per-host_faketime_wrappers "2000-01-01 12:00:00" - create_per-host_linker_wrapper "2000-01-01 12:00:00" export PATH=${WRAP_DIR}:${PATH} + # gcc 7+ honors SOURCE_DATE_EPOCH, no faketime needed + export SOURCE_DATE_EPOCH=`date -d 2000-01-01T12:00:00 +%s` + git config --global core.abbrev 9 cd monero # Set the version string that gets added to the tar archive name @@ -111,6 +101,7 @@ script: | BASEPREFIX=`pwd`/contrib/depends # Build dependencies for each host + export TAR_OPTIONS=--mtime=2000-01-01T12:00:00 for i in $HOSTS; do EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" if [ -d "$EXTRA_INCLUDES" ]; then @@ -129,6 +120,8 @@ script: | ORIGPATH="$PATH" # Run cmake and make, for each create a new build/ directory, # compile from there, archive, export and delete the archive again + export SOURCE_DATE_EPOCH=`date -d ${REFERENCE_DATE}T${REFERENCE_TIME} +%s` + export TAR_OPTIONS=--mtime=${REFERENCE_DATE}T${REFERENCE_TIME} for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build From 5eea312f6da20cdfefabdbec4210fd1ff988f11e Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 14 Nov 2019 01:45:59 +0000 Subject: [PATCH 086/469] Fixup sodium darwin get rid of sodium-darwin.mk special case, unify back with sodium.mk --- contrib/depends/packages/packages.mk | 2 +- contrib/depends/packages/sodium-darwin.mk | 30 ----------------------- contrib/depends/packages/sodium.mk | 1 + 3 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 contrib/depends/packages/sodium-darwin.mk diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 76c30066c..95b23a37e 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -9,7 +9,7 @@ android_native_packages = android_ndk android_packages = ncurses readline sodium darwin_native_packages = native_biplist native_ds_store native_mac_alias $(hardware_native_packages) -darwin_packages = sodium-darwin ncurses readline $(hardware_packages) +darwin_packages = sodium ncurses readline $(hardware_packages) # not really native... freebsd_native_packages = freebsd_base diff --git a/contrib/depends/packages/sodium-darwin.mk b/contrib/depends/packages/sodium-darwin.mk deleted file mode 100644 index 9f11a9426..000000000 --- a/contrib/depends/packages/sodium-darwin.mk +++ /dev/null @@ -1,30 +0,0 @@ -package=sodium-darwin -$(package)_version=1.0.16 -$(package)_download_path=https://download.libsodium.org/libsodium/releases/ -$(package)_file_name=libsodium-$($(package)_version).tar.gz -$(package)_sha256_hash=eeadc7e1e1bcef09680fb4837d448fbdf57224978f865ac1c16745868fbd0533 - -define $(package)_set_vars -$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" -$(package)_config_opts=--enable-static --disable-shared -$(package)_config_opts+=--prefix=$(host_prefix) -endef - -define $(package)_config_cmds - ./autogen.sh &&\ - $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" -endef - -define $(package)_build_cmds - echo "path is problematic here" &&\ - make -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - -define $(package)_postprocess_cmds - rm lib/*.la -endef - diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index ff4c0f289..dbf86fc5a 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -9,6 +9,7 @@ define $(package)_set_vars $(package)_config_opts=--enable-static --disable-shared $(package)_config_opts+=--prefix=$(host_prefix) $(package)_config_opts_android=RANLIB=$($(package)_ranlib) AR=$($(package)_ar) CC=$($(package)_cc) +$(package)_config_opts_darwin=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" endef define $(package)_config_cmds From 9cbba5a8ec4f1634c8588051e0117a60cb6d55b3 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sat, 16 Nov 2019 16:13:06 +0000 Subject: [PATCH 087/469] Fix unwind library ordering Sort objects being archived --- contrib/depends/packages/unwind.mk | 5 +++++ contrib/depends/patches/unwind/fix_obj_order.patch | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 contrib/depends/patches/unwind/fix_obj_order.patch diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk index 8b8903a9a..826a820c4 100644 --- a/contrib/depends/packages/unwind.mk +++ b/contrib/depends/packages/unwind.mk @@ -3,6 +3,11 @@ $(package)_version=1.2 $(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind $(package)_file_name=lib$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65532b992 +$(package)_patches=fix_obj_order.patch + +define $(package)_preprocess_cmds + patch -p0 < $($(package)_patch_dir)/fix_obj_order.patch +endef define $(package)_config_cmds cp -f $(BASEDIR)/config.guess config/config.guess &&\ diff --git a/contrib/depends/patches/unwind/fix_obj_order.patch b/contrib/depends/patches/unwind/fix_obj_order.patch new file mode 100644 index 000000000..374a9f04a --- /dev/null +++ b/contrib/depends/patches/unwind/fix_obj_order.patch @@ -0,0 +1,11 @@ +--- config/ltmain.sh.O 2017-01-13 16:00:54.000000000 +0000 ++++ config/ltmain.sh 2019-11-17 06:46:51.994402494 +0000 +@@ -7957,6 +7957,8 @@ + esac + done + fi ++ oldobjs=`for obj in $oldobjs; do echo $obj; done | sort` ++ oldobjs=" `echo $oldobjs`" + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" From 2e435dfe764b8341dc01fd766bf7b72bbb87e909 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 17 Nov 2019 11:14:31 +0000 Subject: [PATCH 088/469] Fixup compiled python in OSX --- contrib/gitian/gitian-osx.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index 62913cb3e..ecc7d4b59 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -32,7 +32,7 @@ script: | WRAP_DIR=$HOME/wrapped HOSTS="x86_64-apple-darwin11" FAKETIME_HOST_PROGS="" - FAKETIME_PROGS="ar ranlib date dmg genisoimage" + FAKETIME_PROGS="ar ranlib date dmg genisoimage python" export GZIP="-9n" export TZ="UTC" From 47d094c0c40605572f2fe3a9d62cb5c96ef37de8 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 17 Nov 2019 11:41:09 +0000 Subject: [PATCH 089/469] Fix #6147 DB corruption from child process overwrite Don't leave stdout/stderr dangling on a fork. --- src/daemonizer/posix_fork.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index 5af4e1a4a..16758215d 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -127,13 +127,18 @@ void fork(const std::string & pidfile) { quit("Unable to open output file: " + output); } +#else + if (open("/dev/null", O_WRONLY) < 0) + { + quit("Unable to open /dev/null"); + } +#endif // Also send standard error to the same log file. if (dup(1) < 0) { quit("Unable to dup output descriptor"); } -#endif } } // namespace posix From abeed9a767ae68389fb1fae9f0bd4affc886e610 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Mon, 18 Nov 2019 07:58:22 +0000 Subject: [PATCH 090/469] "Fix" non-determinism in native_cctools build By omitting the otool binary which is built non-deterministically. We don't use it anyway. --- contrib/depends/packages/native_cctools.mk | 4 ++++ .../depends/patches/native_cctools/skip_otool.patch | 12 ++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 contrib/depends/patches/native_cctools/skip_otool.patch diff --git a/contrib/depends/packages/native_cctools.mk b/contrib/depends/packages/native_cctools.mk index bcfe1af6b..8bf71254f 100644 --- a/contrib/depends/packages/native_cctools.mk +++ b/contrib/depends/packages/native_cctools.mk @@ -10,6 +10,7 @@ $(package)_clang_download_file=clang+llvm-$($(package)_clang_version)-x86_64-lin $(package)_clang_file_name=clang-llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz $(package)_clang_sha256_hash=99b28a6b48e793705228a390471991386daa33a9717cd9ca007fcdde69608fd9 $(package)_extra_sources=$($(package)_clang_file_name) +$(package)_patches=skip_otool.patch define $(package)_fetch_cmds $(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ @@ -37,7 +38,10 @@ $(package)_cc=$($(package)_extract_dir)/toolchain/bin/clang $(package)_cxx=$($(package)_extract_dir)/toolchain/bin/clang++ endef +# If clang gets updated to a version with a fix for https://reviews.llvm.org/D50559 +# then the patch that skips otool can be removed. define $(package)_preprocess_cmds + patch -p0 < $($(package)_patch_dir)/skip_otool.patch && \ cd $($(package)_build_subdir); ./autogen.sh && \ sed -i.old "/define HAVE_PTHREADS/d" ld64/src/ld/InputFiles.h endef diff --git a/contrib/depends/patches/native_cctools/skip_otool.patch b/contrib/depends/patches/native_cctools/skip_otool.patch new file mode 100644 index 000000000..30c4ee524 --- /dev/null +++ b/contrib/depends/patches/native_cctools/skip_otool.patch @@ -0,0 +1,12 @@ +--- cctools/Makefile.am.O 2016-06-09 15:06:16.000000000 +0100 ++++ cctools/Makefile.am 2019-11-18 08:59:20.078663220 +0000 +@@ -1,7 +1,7 @@ + if ISDARWIN +-SUBDIRS=libstuff ar as misc otool ld64 $(LD_CLASSIC) ++SUBDIRS=libstuff ar as misc ld64 $(LD_CLASSIC) + else +-SUBDIRS=libstuff ar as misc libobjc2 otool ld64 $(LD_CLASSIC) ++SUBDIRS=libstuff ar as misc ld64 $(LD_CLASSIC) + endif + + ACLOCAL_AMFLAGS = -I m4 From 68a6507c3f1e87955d9958afb68b173b51ae4389 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Fri, 28 Feb 2020 16:45:51 -0500 Subject: [PATCH 091/469] Fixed bug in ZMQ JSON-RPC method field --- src/rpc/message.cpp | 15 +++++++-- tests/unit_tests/CMakeLists.txt | 4 ++- tests/unit_tests/zmq_rpc.cpp | 55 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 tests/unit_tests/zmq_rpc.cpp diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index 158b58005..689eb5e33 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -52,6 +52,16 @@ constexpr const char id_field[] = "id"; constexpr const char method_field[] = "method"; constexpr const char params_field[] = "params"; constexpr const char result_field[] = "result"; + +const rapidjson::Value& get_method_field(const rapidjson::Value& src) +{ + const auto member = src.FindMember(method_field); + if (member == src.MemberEnd()) + throw cryptonote::json::MISSING_KEY{method_field}; + if (!member->value.IsString()) + throw cryptonote::json::WRONG_TYPE{"Expected string"}; + return member->value; +} } rapidjson::Value Message::toJson(rapidjson::Document& doc) const @@ -120,7 +130,7 @@ FullMessage::FullMessage(const std::string& json_string, bool request) if (request) { - OBJECT_HAS_MEMBER_OR_THROW(doc, method_field) + get_method_field(doc); // throws on errors OBJECT_HAS_MEMBER_OR_THROW(doc, params_field) } else @@ -151,8 +161,7 @@ std::string FullMessage::getJson() std::string FullMessage::getRequestType() const { - OBJECT_HAS_MEMBER_OR_THROW(doc, method_field) - return doc[method_field].GetString(); + return get_method_field(doc).GetString(); } rapidjson::Value& FullMessage::getMessage() diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cda25dfc9..a53b08844 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -93,7 +93,8 @@ set(unit_tests_sources wipeable_string.cpp is_hdd.cpp aligned.cpp - rpc_version_str.cpp) + rpc_version_str.cpp + zmq_rpc.cpp) set(unit_tests_headers unit_tests_utils.h) @@ -106,6 +107,7 @@ target_link_libraries(unit_tests ringct cryptonote_protocol cryptonote_core + daemon_messages blockchain_db lmdb_lib rpc diff --git a/tests/unit_tests/zmq_rpc.cpp b/tests/unit_tests/zmq_rpc.cpp new file mode 100644 index 000000000..af1f1608b --- /dev/null +++ b/tests/unit_tests/zmq_rpc.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 HOLDER 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. + +#include + +#include "rpc/message.h" +#include "serialization/json_object.h" + +TEST(ZmqFullMessage, InvalidRequest) +{ + EXPECT_THROW( + (cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"params\":[]}", true}), + cryptonote::json::MISSING_KEY + ); + EXPECT_THROW( + (cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":3,\"params\":[]}", true}), + cryptonote::json::WRONG_TYPE + ); +} + +TEST(ZmqFullMessage, Request) +{ + static constexpr const char request[] = "{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"foo\",\"params\":[]}"; + EXPECT_NO_THROW( + (cryptonote::rpc::FullMessage{request, true}) + ); + + cryptonote::rpc::FullMessage parsed{request, true}; + EXPECT_STREQ("foo", parsed.getRequestType().c_str()); +} From 24adee4a0886d66e6df31594c67f7204367a8e3f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 19 Nov 2019 01:54:49 +0000 Subject: [PATCH 092/469] p2p: don't request flags after closing connection --- src/p2p/net_node.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f8094bfa8..a1be1e323 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1089,7 +1089,7 @@ namespace nodetool LOG_WARNING_CC(context_, "COMMAND_HANDSHAKE Failed"); m_network_zones.at(context_.m_remote_address.get_zone()).m_net_server.get_config_object().close(context_.m_connection_id); } - else + else if (!just_take_peerlist) { try_get_support_flags(context_, [](p2p_connection_context& flags_context, const uint32_t& support_flags) { From 3b41933335c3f49d2783d60e78f97266474e1979 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 19 Nov 2019 03:18:06 +0000 Subject: [PATCH 093/469] wallet: don't issue node rpc proxy requests in '--offline' mode --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7f8b48b8d..b6ffc039c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5394,6 +5394,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) void wallet2::set_offline(bool offline) { m_offline = offline; + m_node_rpc_proxy.set_offline(offline); m_http_client.set_auto_connect(!offline); if (offline) { From d8fc8d8868568c3694b24e0a413a896569e41a79 Mon Sep 17 00:00:00 2001 From: Jethro Grassie Date: Fri, 8 Nov 2019 01:26:17 -0500 Subject: [PATCH 094/469] make d2h et al. constant-time --- src/ringct/rctTypes.cpp | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index 2c4e5fc3b..1763542db 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -31,6 +31,7 @@ #include "misc_log_ex.h" #include "cryptonote_config.h" #include "rctTypes.h" +#include "int-util.h" using namespace crypto; using namespace std; @@ -118,40 +119,22 @@ namespace rct { //uint long long to 32 byte key void d2h(key & amounth, const xmr_amount in) { sc_0(amounth.bytes); - xmr_amount val = in; - int i = 0; - while (val != 0) { - amounth[i] = (unsigned char)(val & 0xFF); - i++; - val /= (xmr_amount)256; - } + memcpy_swap64le(amounth.bytes, &in, 1); } //uint long long to 32 byte key key d2h(const xmr_amount in) { key amounth; - sc_0(amounth.bytes); - xmr_amount val = in; - int i = 0; - while (val != 0) { - amounth[i] = (unsigned char)(val & 0xFF); - i++; - val /= (xmr_amount)256; - } + d2h(amounth, in); return amounth; } //uint long long to int[64] void d2b(bits amountb, xmr_amount val) { int i = 0; - while (val != 0) { - amountb[i] = val & 1; - i++; - val >>= 1; - } while (i < 64) { - amountb[i] = 0; - i++; + amountb[i++] = val & 1; + val >>= 1; } } @@ -172,16 +155,11 @@ namespace rct { int val = 0, i = 0, j = 0; for (j = 0; j < 8; j++) { val = (unsigned char)test.bytes[j]; - i = 8 * j; - while (val != 0) { - amountb2[i] = val & 1; - i++; + i = 0; + while (i < 8) { + amountb2[j*8+i++] = val & 1; val >>= 1; } - while (i < 8 * (j + 1)) { - amountb2[i] = 0; - i++; - } } } From 402dd2078cc8ecb83564c7ee494dad0feb11b9df Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 18 Nov 2019 12:35:11 +0000 Subject: [PATCH 095/469] db_lmdb: guard against non NUL terminated keys --- src/blockchain_db/lmdb/db_lmdb.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index f978ef307..f46e53f3d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -163,7 +163,15 @@ int BlockchainLMDB::compare_string(const MDB_val *a, const MDB_val *b) { const char *va = (const char*) a->mv_data; const char *vb = (const char*) b->mv_data; - return strcmp(va, vb); + const size_t sz = std::min(a->mv_size, b->mv_size); + int ret = strncmp(va, vb, sz); + if (ret) + return ret; + if (a->mv_size < b->mv_size) + return -1; + if (a->mv_size > b->mv_size) + return 1; + return 0; } } From f3e155ea798f08adfa99aabe8f8c3777a496c9fb Mon Sep 17 00:00:00 2001 From: xiphon Date: Wed, 20 Nov 2019 00:23:40 +0000 Subject: [PATCH 096/469] add xiphon's GPG key --- utils/gpg_keys/xiphon.asc | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 utils/gpg_keys/xiphon.asc diff --git a/utils/gpg_keys/xiphon.asc b/utils/gpg_keys/xiphon.asc new file mode 100644 index 000000000..01ad7fb77 --- /dev/null +++ b/utils/gpg_keys/xiphon.asc @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +JezqaExCLJDmDVnOj/t8V7dUv4o84A== +=y+nZ +-----END PGP PUBLIC KEY BLOCK----- From bd6a577880804bb9d1778d11b1d1a4071d5b9940 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Wed, 20 Nov 2019 01:26:47 +0100 Subject: [PATCH 097/469] Gitian: Fix sigs generation --- contrib/gitian/gitian-build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 64eb218bb..0b36fb4a1 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -66,7 +66,7 @@ def rebuild(): print('\nCompiling ' + args.version + ' ' + os_name) infile = 'inputs/monero/contrib/gitian/gitian-' + tag_name + '.yml' subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, infile]) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../sigs/', infile]) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-'+tag_name, '--destination', '../sigs/', infile]) subprocess.check_call('mv build/out/monero-*.' + suffix + ' ../out/'+args.version, shell=True) print('Moving var/install.log to var/install-' + tag_name + '.log') subprocess.check_call('mv var/install.log var/install-' + tag_name + '.log', shell=True) From dc589ef07eb00a9d128001b2ee46362790486fff Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Thu, 21 Nov 2019 01:43:30 +0100 Subject: [PATCH 098/469] Add TheCharlatan pgp key --- utils/gpg_keys/thecharlatan.asc | 316 ++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 utils/gpg_keys/thecharlatan.asc diff --git a/utils/gpg_keys/thecharlatan.asc b/utils/gpg_keys/thecharlatan.asc new file mode 100644 index 000000000..a10a1fb54 --- /dev/null +++ b/utils/gpg_keys/thecharlatan.asc @@ -0,0 +1,316 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=abJr +-----END PGP PUBLIC KEY BLOCK----- From 0e0351c456ed8cd0c05138e15c641401588afb72 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Sat, 16 Nov 2019 13:13:58 +0000 Subject: [PATCH 099/469] wallet_rpc_server: add count parameter to create_address --- src/wallet/wallet_rpc_server.cpp | 26 +++++++++++++++++--- src/wallet/wallet_rpc_server_commands_defs.h | 14 ++++++++--- utils/python-rpc/framework/wallet.py | 7 +++--- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..328e944ed 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -550,9 +550,29 @@ namespace tools if (!m_wallet) return not_open(er); try { - m_wallet->add_subaddress(req.account_index, req.label); - res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1; - res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index}); + if (req.count < 1 || req.count > 64) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Count must be between 1 and 64."; + return false; + } + + std::vector addresses; + std::vector address_indices; + + addresses.reserve(req.count); + address_indices.reserve(req.count); + + for (uint32_t i = 0; i < req.count; i++) { + m_wallet->add_subaddress(req.account_index, req.label); + uint32_t new_address_index = m_wallet->get_num_subaddresses(req.account_index) - 1; + address_indices.push_back(new_address_index); + addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index})); + } + + res.address = addresses[0]; + res.address_index = address_indices[0]; + res.addresses = addresses; + res.address_indices = address_indices; } catch (const std::exception& e) { diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..3af782fbe 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 16 +#define WALLET_RPC_VERSION_MINOR 17 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -182,11 +182,13 @@ namespace wallet_rpc { struct request_t { - uint32_t account_index; + uint32_t account_index; + uint32_t count; std::string label; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) + KV_SERIALIZE_OPT(count, 1U) KV_SERIALIZE(label) END_KV_SERIALIZE_MAP() }; @@ -194,12 +196,16 @@ namespace wallet_rpc struct response_t { - std::string address; - uint32_t address_index; + std::string address; + uint32_t address_index; + std::vector addresses; + std::vector address_indices; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) KV_SERIALIZE(address_index) + KV_SERIALIZE(addresses) + KV_SERIALIZE(address_indices) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response; diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index 6a3fabdc9..426074511 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -237,14 +237,15 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(create_account) - def create_address(self, account_index = 0, label = ""): + def create_address(self, account_index = 0, label = "", count = 1): create_address = { 'method': 'create_address', 'params' : { 'account_index': account_index, - 'label': label + 'label': label, + 'count': count }, - 'jsonrpc': '2.0', + 'jsonrpc': '2.0', 'id': '0' } return self.rpc.send_json_rpc_request(create_address) From d7cf8727674bbb8a366e4ad400707671694e4036 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 22 Nov 2019 15:37:31 +0000 Subject: [PATCH 100/469] rpc: add received_timestamp for pool txes in gettransactions --- src/rpc/core_rpc_server.cpp | 3 +++ src/rpc/core_rpc_server_commands_defs.h | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..ed65dcf1e 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -953,18 +953,21 @@ namespace cryptonote { e.double_spend_seen = it->second.double_spend_seen; e.relayed = it->second.relayed; + e.received_timestamp = it->second.receive_time; } else { MERROR("Failed to determine pool info for " << tx_hash); e.double_spend_seen = false; e.relayed = false; + e.received_timestamp = 0; } } else { e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height); + e.received_timestamp = 0; e.double_spend_seen = false; e.relayed = false; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 855ea854c..c76880ab9 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 3 -#define CORE_RPC_VERSION_MINOR 0 +#define CORE_RPC_VERSION_MINOR 1 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -351,6 +351,7 @@ namespace cryptonote bool double_spend_seen; uint64_t block_height; uint64_t block_timestamp; + uint64_t received_timestamp; std::vector output_indices; bool relayed; @@ -372,6 +373,7 @@ namespace cryptonote else { KV_SERIALIZE(relayed) + KV_SERIALIZE(received_timestamp) } END_KV_SERIALIZE_MAP() }; From 81c2658989a303b678d688e33954e7e565981c58 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 22 Nov 2019 18:10:52 +0000 Subject: [PATCH 101/469] rpc: don't auto fail RPC needing payment in bootstrap mode --- src/rpc/core_rpc_server.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..cb526233a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1958,7 +1958,11 @@ namespace cryptonote m_was_bootstrap_ever_used = true; } - r = r && res.status == CORE_RPC_STATUS_OK; + if (r && res.status != CORE_RPC_STATUS_PAYMENT_REQUIRED && res.status != CORE_RPC_STATUS_OK) + { + MINFO("Failing RPC " << command_name << " due to peer return status " << res.status); + r = false; + } res.untrusted = true; return true; } From 9cb8d92202c7a2fb42640f38b53eca38458c5039 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Sat, 23 Nov 2019 16:59:42 +0100 Subject: [PATCH 102/469] ANONYMITY_NETWORKS.md : fix unintentional wrapping --- ANONYMITY_NETWORKS.md | 46 +++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/ANONYMITY_NETWORKS.md b/ANONYMITY_NETWORKS.md index cb0e378c3..3337b5fc3 100644 --- a/ANONYMITY_NETWORKS.md +++ b/ANONYMITY_NETWORKS.md @@ -46,8 +46,10 @@ Connecting to an anonymous address requires the command line option `--tx-proxy` which tells `monerod` the ip/port of a socks proxy provided by a separate process. On most systems the configuration will look like: -> `--tx-proxy tor,127.0.0.1:9050,10` -> `--tx-proxy i2p,127.0.0.1:9000` +``` +--tx-proxy tor,127.0.0.1:9050,10 +--tx-proxy i2p,127.0.0.1:9000 +``` which tells `monerod` that ".onion" p2p addresses can be forwarded to a socks proxy at IP 127.0.0.1 port 9050 with a max of 10 outgoing connections and @@ -55,12 +57,14 @@ proxy at IP 127.0.0.1 port 9050 with a max of 10 outgoing connections and 9000 with the default max outgoing connections. Since there are no seed nodes for anonymity connections, peers must be manually specified: -> `--add-exclusive-node rveahdfho7wo4b2m.onion:28083` -> `--add-peer rveahdfho7wo4b2m.onion:28083` +``` +--add-exclusive-node rveahdfho7wo4b2m.onion:28083 +--add-peer rveahdfho7wo4b2m.onion:28083 +``` Either option can be listed multiple times, and can specify any mix of Tor, I2P, and IPv4 addresses. Using `--add-exclusive-node` will prevent the usage of -seed nodes on ALL networks, which will typically be undesireable. +seed nodes on ALL networks, which will typically be undesirable. ### Inbound Connections @@ -68,8 +72,10 @@ Receiving anonymity connections is done through the option `--anonymous-inbound`. This option tells `monerod` the inbound address, network type, and max connections: -> `--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.1:28083,25` -> `--anonymous-inbound cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000,127.0.0.1:30000` +``` +--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.1:28083,25 +--anonymous-inbound cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000,127.0.0.1:30000 +``` which tells `monerod` that a max of 25 inbound Tor connections are being received at address "rveahdfho7wo4b2m.onion:28083" and forwarded to `monerod` @@ -87,12 +93,16 @@ P2P anonymity connections. The anonymity network (Tor/i2p) is [configured in the same manner](#configuration), except the localhost port must be the RPC port (typically 18081 for mainnet) instead of the p2p port: -> HiddenServiceDir /var/lib/tor/data/monero -> HiddenServicePort 18081 127.0.0.1:18081 +``` +HiddenServiceDir /var/lib/tor/data/monero +HiddenServicePort 18081 127.0.0.1:18081 +``` Then the wallet will be configured to use a Tor/i2p address: -> `--proxy 127.0.0.1:9050` -> `--daemon-address rveahdfho7wo4b2m.onion` +``` +--proxy 127.0.0.1:9050 +--daemon-address rveahdfho7wo4b2m.onion +``` The proxy must match the address type - a Tor proxy will not work properly with i2p addresses, etc. @@ -125,8 +135,10 @@ can distribute the address to its other peers. Tor must be configured for hidden services. An example configuration ("torrc") might look like: -> HiddenServiceDir /var/lib/tor/data/monero -> HiddenServicePort 28083 127.0.0.1:28083 +``` +HiddenServiceDir /var/lib/tor/data/monero +HiddenServicePort 28083 127.0.0.1:28083 +``` This will store key information in `/var/lib/tor/data/monero` and will forward "Tor port" 28083 to port 28083 of ip 127.0.0.1. The file @@ -170,7 +182,7 @@ be used by an ISP to link a user to a transaction. Run `monerod` as often as possible to conceal when transactions are being sent. Future versions will also have peers that first receive a transaction over an anonymity network delay the broadcast to public peers by a randomized amount. -This will not completetely mitigate a user who syncs up sends then quits, in +This will not completely mitigate a user who syncs up sends then quits, in part because this rule is not enforceable, so this mitigation strategy is simply a best effort attempt. @@ -183,9 +195,9 @@ the connections are not circuit based. #### Mitigation -The best mitigiation is to use I2P instead of Tor. However, I2P +The best mitigation is to use I2P instead of Tor. However, I2P has a smaller set of users (less cover traffic) and academic reviews, so there -is a tradeoff in potential isses. Also, anyone attempting this strategy really +is a trade off in potential issues. Also, anyone attempting this strategy really wants to uncover a user, it seems unlikely that this would be performed against every Tor/I2P user. @@ -213,7 +225,7 @@ key identity. @secparam (twitter) recommended changing circuits (Tor) as an additional precaution. This is likely not a good idea - forcibly requesting Tor to change circuits is observable by the ISP. Instead, `monerod` should likely disconnect -from peers ocassionally. Tor will rotate circuits every ~10 minutes, so +from peers occasionally. Tor will rotate circuits every ~10 minutes, so establishing new connections will use a new public key identity and make it more difficult for the hidden service to link information. This process will have to be done carefully because closing/reconnecting connections can also From 7ac7d5d32f2f5c23587135fc1dd7287b34fb2cd4 Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 25 Nov 2019 00:37:54 +0100 Subject: [PATCH 103/469] updates: fix source code URL on _WIN32 --- src/common/updates.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/updates.cpp b/src/common/updates.cpp index 0bc6ff63c..f620bb53a 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -101,7 +101,7 @@ namespace tools { const char *base = user ? "https://downloads.getmonero.org/" : "https://updates.getmonero.org/"; #ifdef _WIN32 - static const char *extension = strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe"; + static const char *extension = strncmp(buildtag.c_str(), "source", 6) ? (strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe") : ".tar.bz2"; #else static const char extension[] = ".tar.bz2"; #endif From f19c9f23076bade5a5933c8084b8779e065ee42e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Oct 2019 17:43:09 +0000 Subject: [PATCH 104/469] util: allow newlines in string to be split --- src/common/util.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 57e747837..747235646 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1239,7 +1239,7 @@ std::string get_nix_version_display_string() return get_string_prefix_by_width(s, 999999999).second; }; - std::vector> split_string_by_width(const std::string &s, size_t columns) + std::vector> split_line_by_width(const std::string &s, size_t columns) { std::vector words; std::vector> lines; @@ -1279,4 +1279,17 @@ std::string get_nix_version_display_string() return lines; } + std::vector> split_string_by_width(const std::string &s, size_t columns) + { + std::vector lines; + std::vector> all_lines; + boost::split(lines, s, boost::is_any_of("\n"), boost::token_compress_on); + for (const auto &e: lines) + { + std::vector> new_lines = split_line_by_width(e, columns); + for (auto &l: new_lines) + all_lines.push_back(std::move(l)); + } + return all_lines; + } } From 88b82befc7735125c13f04a1c48f7de5c76dba4d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Oct 2019 17:43:27 +0000 Subject: [PATCH 105/469] simplewallet: point to "set help" in the lock screen message --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..b96050cd8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6175,7 +6175,7 @@ void simple_wallet::check_for_inactivity_lock(bool user) m_in_command = true; if (!user) { - const std::string speech = tr("I locked your Monero wallet to protect you while you were away"); + const std::string speech = tr("I locked your Monero wallet to protect you while you were away\nsee \"help_advanced set\" to configure/disable"); std::vector> lines = tools::split_string_by_width(speech, 45); size_t max_len = 0; From e896cca86e20ed49abcb4d9cd4a966fd66f28fba Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 25 Nov 2019 19:13:05 +0000 Subject: [PATCH 106/469] epee: reorder a couple init list fields to match declaration This is a bug waiting to happen --- contrib/epee/include/net/net_helper.h | 2 +- contrib/epee/src/http_auth.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 81545e502..9446e3588 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -103,8 +103,8 @@ namespace net_utils blocked_mode_client() : m_io_service(), m_ctx(boost::asio::ssl::context::tlsv12), - m_connector(direct_connect{}), m_ssl_socket(new boost::asio::ssl::stream(m_io_service, m_ctx)), + m_connector(direct_connect{}), m_ssl_options(epee::net_utils::ssl_support_t::e_ssl_support_autodetect), m_initialized(true), m_connected(false), diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index 289069daa..5f4907cc2 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -584,8 +584,8 @@ namespace explicit server_parameters(const auth_message& request, const DigestIter& digest) : nonce(request.nonce) , opaque(request.opaque) - , stale(request.stale) , realm(request.realm) + , stale(request.stale) , value_generator() , index(boost::fusion::distance(boost::fusion::begin(digest_algorithms), digest)) { From 8231c7cd0406e8bdae58d10f3bb40546b113e170 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 22 Nov 2019 18:11:35 +0000 Subject: [PATCH 107/469] rpc: fix bootstrap RPC payment RPC being made in raw JSON, not JSON RPC --- src/rpc/core_rpc_server.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index cb526233a..bafdff0df 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2944,7 +2944,7 @@ namespace cryptonote RPC_TRACKER(rpc_access_info); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "rpc_access_info", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "rpc_access_info", req, res, r)) return r; // if RPC payment is not enabled @@ -3016,7 +3016,7 @@ namespace cryptonote { RPC_TRACKER(rpc_access_submit_nonce); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "rpc_access_submit_nonce", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "rpc_access_submit_nonce", req, res, r)) return r; // if RPC payment is not enabled @@ -3075,7 +3075,7 @@ namespace cryptonote RPC_TRACKER(rpc_access_pay); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "rpc_access_pay", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "rpc_access_pay", req, res, r)) return r; // if RPC payment is not enabled @@ -3134,7 +3134,7 @@ namespace cryptonote RPC_TRACKER(rpc_access_data); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "rpc_access_data", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "rpc_access_data", req, res, r)) return r; if (!m_rpc_payment) @@ -3162,7 +3162,7 @@ namespace cryptonote RPC_TRACKER(rpc_access_account); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "rpc_access_account", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "rpc_access_account", req, res, r)) return r; if (!m_rpc_payment) From 0bae2277b5f6bef07b5dec558c07632477af8a63 Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Tue, 26 Nov 2019 10:13:59 -0500 Subject: [PATCH 108/469] update readme to encourage joining #monero-dev replaces #6126 because I can't squash things with the github web GUI --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7b6b252e0..6afcbbd05 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers. - Mail: [dev@getmonero.org](mailto:dev@getmonero.org) - GitHub: [https://github.com/monero-project/monero](https://github.com/monero-project/monero) - IRC: [#monero-dev on Freenode](https://webchat.freenode.net/?randomnick=1&channels=%23monero-dev&prompt=1&uio=d4) +- It is HIGHLY recommended that you join the #monero-dev IRC channel if you are developing software that uses Monero. Due to the nature of this open source software project, joining this channel and idling is the best way to stay updated on best practices and new developments in the Monero ecosystem. All you need to do is join the IRC channel and idle to stay updated with the latest in Monero development. If you do not, you risk wasting resources on developing integrations that are not compatible with the Monero network. The Monero core team and community continuously make efforts to communicate updates, developments, and documentation via other platforms – but for the best information, you need to talk to other Monero developers, and they are on IRC. #monero-dev is about Monero development, not getting help about using Monero, or help about development of other software, including yours, unless it also pertains to Monero code itself. For these cases, checkout #monero. ## Vulnerability response From 3813a992e4536af1aa9eb966337b3e247dac6812 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 25 Nov 2019 21:49:36 +0000 Subject: [PATCH 109/469] download: catch exceptions checking for size Happens on at least one windows box --- src/common/download.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/common/download.cpp b/src/common/download.cpp index f07d6798d..2b6a3f9d3 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -107,13 +107,17 @@ namespace tools MINFO("Content-Length: " << length); content_length = length; boost::filesystem::path path(control->path); - boost::filesystem::space_info si = boost::filesystem::space(path); - if (si.available < (size_t)content_length) + try { - const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024; - MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)"); - return false; + boost::filesystem::space_info si = boost::filesystem::space(path); + if (si.available < (size_t)content_length) + { + const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024; + MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)"); + return false; + } } + catch (const std::exception &e) { MWARNING("Failed to check for free space: " << e.what()); } } if (offset > 0) { From 2d1afceb0d76ac57b6b40a8b1ac11e55b0d23344 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 25 Nov 2019 21:53:47 +0000 Subject: [PATCH 110/469] net_ssl: load default certificates in CA mode on Windows Because it always does things wonkily doens't it --- contrib/epee/src/net_ssl.cpp | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index 16454fce0..06997d3ba 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -43,6 +43,10 @@ // openssl req -new -key /tmp/KEY -out /tmp/REQ // openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT +#ifdef _WIN32 +static void add_windows_root_certs(SSL_CTX *ctx) noexcept; +#endif + namespace { struct openssl_bio_free @@ -324,7 +328,12 @@ boost::asio::ssl::context ssl_options_t::create_context() const switch (verification) { case ssl_verification_t::system_ca: +#ifdef _WIN32 + try { add_windows_root_certs(ssl_context.native_handle()); } + catch (const std::exception &e) { ssl_context.set_default_verify_paths(); } +#else ssl_context.set_default_verify_paths(); +#endif break; case ssl_verification_t::user_certificates: ssl_context.set_verify_depth(0); @@ -558,3 +567,36 @@ bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s) } // namespace } // namespace +#ifdef _WIN32 + +// https://stackoverflow.com/questions/40307541 +// Because Windows always has to do things wonkily +#include +static void add_windows_root_certs(SSL_CTX *ctx) noexcept +{ + HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT"); + if (hStore == NULL) { + return; + } + + X509_STORE *store = X509_STORE_new(); + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) { + // convert from DER to internal format + X509 *x509 = d2i_X509(NULL, + (const unsigned char **)&pContext->pbCertEncoded, + pContext->cbCertEncoded); + if(x509 != NULL) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + } + } + + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + // attach X509_STORE to boost ssl context + SSL_CTX_set_cert_store(ctx, store); +} +#endif + From 3c849188abcefff9c71c3c569353a965c4e466f2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 08:05:43 +0000 Subject: [PATCH 111/469] rpc: always set the update field in update on sucess Coverity 205416 --- src/rpc/core_rpc_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..dbacd489d 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2617,6 +2617,7 @@ namespace cryptonote { RPC_TRACKER(update); + res.update = false; if (m_core.offline()) { res.status = "Daemon is running offline"; From d56a483abea2e7803ef47cbcb48a86a45e605410 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 08:12:49 +0000 Subject: [PATCH 112/469] rpc: do not propagate exceptions out of a dtor Coverity 205415 --- src/rpc/core_rpc_server.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dbacd489d..858b35df2 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -86,10 +86,14 @@ namespace RPCTracker(const char *rpc, tools::LoggingPerformanceTimer &timer): rpc(rpc), timer(timer) { } ~RPCTracker() { - boost::unique_lock lock(mutex); - auto &e = tracker[rpc]; - ++e.count; - e.time += timer.value(); + try + { + boost::unique_lock lock(mutex); + auto &e = tracker[rpc]; + ++e.count; + e.time += timer.value(); + } + catch (...) { /* ignore */ } } void pay(uint64_t amount) { boost::unique_lock lock(mutex); From d0faae2a922effe64706aae95a095d39c9c3e3ee Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 08:16:08 +0000 Subject: [PATCH 113/469] rpc: init a few missing client_info members Coverity 205414 --- src/rpc/rpc_payment.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index 0637db728..b363c27b2 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -59,6 +59,10 @@ namespace cryptonote { rpc_payment::client_info::client_info(): + previous_seed_height(0), + seed_height(0), + previous_seed_hash(crypto::null_hash), + seed_hash(crypto::null_hash), cookie(0), top(crypto::null_hash), previous_top(crypto::null_hash), From 054b2621b1c8621afc3682b358bd02301d3aa2ea Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 08:19:04 +0000 Subject: [PATCH 114/469] node_rpc_proxy: init some new rpc payment fields in invalidate The cache time would take care of these, but it's cleaner that way Coverity 205412 --- src/wallet/node_rpc_proxy.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 005b0bafa..f3698b599 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -78,6 +78,10 @@ void NodeRPCProxy::invalidate() m_rpc_payment_seed_hash = crypto::null_hash; m_rpc_payment_next_seed_hash = crypto::null_hash; m_height_time = 0; + m_rpc_payment_diff = 0; + m_rpc_payment_credits_per_hash_found = 0; + m_rpc_payment_height = 0; + m_rpc_payment_cookie = 0; } boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) From fa16df9996700306d0976180156c320b8d529faf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 08:23:02 +0000 Subject: [PATCH 115/469] make_test_signature: exit nicely on top level exception Coverity 205411 --- tests/functional_tests/make_test_signature.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional_tests/make_test_signature.cc b/tests/functional_tests/make_test_signature.cc index 8c0333233..789523de5 100644 --- a/tests/functional_tests/make_test_signature.cc +++ b/tests/functional_tests/make_test_signature.cc @@ -32,6 +32,7 @@ int main(int argc, const char **argv) { + TRY_ENTRY(); if (argc > 2) { fprintf(stderr, "usage: %s \n", argv[0]); @@ -57,4 +58,5 @@ int main(int argc, const char **argv) std::string signature = cryptonote::make_rpc_payment_signature(skey); printf("%s\n", signature.c_str()); return 0; + CATCH_ENTRY_L0("main()", 1); } From b90c4bc39ee336bf0a0cb6e40876926baf06d1fe Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Oct 2019 08:24:50 +0000 Subject: [PATCH 116/469] rpc: error out from get_info if the proxied call errors out Coverity 205410 --- src/rpc/core_rpc_server.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 858b35df2..5a4ad0506 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2226,8 +2226,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) { - on_get_info(req, res, ctx); - if (res.status != CORE_RPC_STATUS_OK) + if (!on_get_info(req, res, ctx) || res.status != CORE_RPC_STATUS_OK) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = res.status; From 316ab7b56ed2b4c3f8eb114f6e291cf9e91f8c00 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Nov 2019 18:21:53 +0000 Subject: [PATCH 117/469] wallet2: better error when restoring a wallet with the wrong keys --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e60c6b7e1..8ecfdeca2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4236,7 +4236,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); if (r) setup_keys(password); From f8127831c7421c4c9255e2b96ff4b717142d35ab Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 29 Nov 2019 19:59:41 +0000 Subject: [PATCH 118/469] simplewallet: fix encrypted payment id note triggering on dummy ones --- src/simplewallet/simplewallet.cpp | 32 +++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..5cddf00a1 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5446,20 +5446,28 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, std::vector tx_extra_fields; parse_tx_extra(tx.extra, tx_extra_fields); // failure ok tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + tx_extra_pub_key extra_pub_key; + crypto::hash8 payment_id8 = crypto::null_hash8; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_pub_key)) { - crypto::hash payment_id = crypto::null_hash; - crypto::hash8 payment_id8 = crypto::null_hash8; - if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + const crypto::public_key &tx_pub_key = extra_pub_key.pub_key; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - if (payment_id8 != crypto::null_hash8) - message_writer() << - tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); - } - else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - message_writer(console_color_red, false) << - tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead."); - } + if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + m_wallet->get_account().get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_wallet->get_account().get_keys().m_view_secret_key); + } + } + } + + if (payment_id8 != crypto::null_hash8) + message_writer() << + tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); + + crypto::hash payment_id = crypto::null_hash; + if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + message_writer(console_color_red, false) << + tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead."); } if (unlock_time && !cryptonote::is_coinbase(tx)) message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid); From 5f2a32c15ab0eef7bf0e0a0f9cd1362a1c456a22 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 29 Nov 2019 20:05:26 +0000 Subject: [PATCH 119/469] daemon: run with -rpc-payment-address and --rpc-restricted-bind-port --- src/daemon/daemon.cpp | 9 +++++---- src/daemon/rpc.h | 3 ++- src/rpc/core_rpc_server.cpp | 3 ++- src/rpc/core_rpc_server.h | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index cb96b37b6..056f2f320 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -78,13 +78,14 @@ public: const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc); const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); - rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"}); + const auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; + const bool has_restricted_rpc_port_arg = !command_line::is_arg_defaulted(vm, restricted_rpc_port_arg); + rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core", !has_restricted_rpc_port_arg}); - auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; - if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg)) + if(has_restricted_rpc_port_arg) { auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg); - rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted", true}); } } }; diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 213593aa7..6f545a3b7 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -56,12 +56,13 @@ public: , const bool restricted , const std::string & port , const std::string & description + , bool allow_rpc_payment ) : m_server{core.get(), p2p.get()}, m_description{description} { MGINFO("Initializing " << m_description << " RPC server..."); - if (!m_server.init(vm, restricted, port)) + if (!m_server.init(vm, restricted, port, allow_rpc_payment)) { throw std::runtime_error("Failed to initialize " + m_description + " RPC server."); } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..ab7faf52b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -231,6 +231,7 @@ namespace cryptonote const boost::program_options::variables_map& vm , const bool restricted , const std::string& port + , bool allow_rpc_payment ) { m_restricted = restricted; @@ -242,7 +243,7 @@ namespace cryptonote return false; std::string address = command_line::get_arg(vm, arg_rpc_payment_address); - if (!address.empty()) + if (!address.empty() && allow_rpc_payment) { if (!m_restricted && nettype() != FAKECHAIN) { diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 23c611470..fbcffd120 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -88,7 +88,8 @@ namespace cryptonote bool init( const boost::program_options::variables_map& vm, const bool restricted, - const std::string& port + const std::string& port, + bool allow_rpc_payment ); network_type nettype() const { return m_core.get_nettype(); } From 02b8051339547a5ad33e173727ae6ea87fc70781 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 1 Dec 2019 15:38:42 +0000 Subject: [PATCH 120/469] unit_tests: remove invalid bulletproofs unit test It was intended to check a case which is actually valid (0 gamma), but was actually duplicating the bad amount test. Reported by WhatDo_ on IRC. --- tests/unit_tests/bulletproofs.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 760769551..a73b80848 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -179,15 +179,6 @@ TEST(bulletproofs, invalid_31) ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); } -TEST(bulletproofs, invalid_gamma_0) -{ - rct::key invalid_amount = rct::zero(); - invalid_amount[8] = 1; - rct::key gamma = rct::zero(); - rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); - ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); -} - static const char * const torsion_elements[] = { "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", From ef95a76d0d6f57d8f8ad43ecf93414331f228ecb Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 1 Dec 2019 13:07:26 -0500 Subject: [PATCH 121/469] remove unused variable 'ptx' from on_describe_transfer() --- src/wallet/wallet_rpc_server.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..6dd0b0b58 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1157,7 +1157,6 @@ namespace tools } } - std::vector ptx; try { // gather info to ask the user From 1d78db270997a60385da4a5baf2457911af9c406 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 2 Dec 2019 00:02:38 +0100 Subject: [PATCH 122/469] Add travis freebsd --- .travis.yml | 2 ++ README.md | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 516e4f8e5..ba2215905 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,8 @@ env: - HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev" # Cross-Mac - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11 +# x86_64 Freebsd + - HOST=x86_64-unknown-freebsd PACKAGES="clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev" before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") diff --git a/README.md b/README.md index 7b6b252e0..fd8016ef7 100644 --- a/README.md +++ b/README.md @@ -547,6 +547,11 @@ You can also cross-compile static binaries on Linux for Windows and macOS with t * Requires: `g++-aarch64-linux-gnu` * ```make depends target=riscv64-linux-gnu``` for RISC V 64 bit binaries. * Requires: `g++-riscv64-linux-gnu` +* ```make depends target=x86_64-unknown-freebsd``` for freebsd binaries. + * Requires: `clang-8` +* ```make depends target=arm-linux-android``` for 32bit android binaries +* ```make depends target=aarch64-linux-android``` for 64bit android binaries + The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. From 948534877ebc3aec2723e389cddadb9f9f629916 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 2 Dec 2019 15:08:17 +0000 Subject: [PATCH 123/469] easylogging++: add emscripten support This is upstream, but seems to have got lost in conflicts when merging support for BSDs or android. --- external/easylogging++/easylogging++.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 398afd20a..03d62bfb6 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -125,13 +125,16 @@ #else # define ELPP_OS_NETBSD 0 #endif +#if defined(__EMSCRIPTEN__) +# define ELPP_OS_EMSCRIPTEN 1 +#endif #if (defined(__DragonFly__)) # define ELPP_OS_DRAGONFLY 1 #else # define ELPP_OS_DRAGONFLY 0 #endif // Unix -#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS)) +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS)) # define ELPP_OS_UNIX 1 #else # define ELPP_OS_UNIX 0 @@ -216,7 +219,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # define ELPP_INTERNAL_INFO(lvl, msg) #endif // (defined(ELPP_DEBUG_INFO)) #if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) -# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD && !ELPP_OS_ANDROID) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN) # define ELPP_STACKTRACE 1 # else # define ELPP_STACKTRACE 0 From 5985c5afe8051ef9933212e62293ece3634a5024 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 2 Dec 2019 18:51:28 +0000 Subject: [PATCH 124/469] rpc: add bad-blocks to flush_cache RPC Flushes m_invalid_blocks in Blockchain. --- src/cryptonote_core/blockchain.cpp | 7 +++++++ src/cryptonote_core/blockchain.h | 5 +++++ src/cryptonote_core/cryptonote_core.cpp | 4 ++++ src/cryptonote_core/cryptonote_core.h | 5 +++++ src/daemon/command_parser_executor.cpp | 20 +++++++++++++++++--- src/daemon/command_server.cpp | 2 +- src/daemon/rpc_command_executor.cpp | 3 ++- src/daemon/rpc_command_executor.h | 2 +- src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 4 +++- 10 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d22158dfc..733ac5bcd 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2534,6 +2534,13 @@ bool Blockchain::add_block_as_invalid(const block_extended_info& bei, const cryp return true; } //------------------------------------------------------------------ +void Blockchain::flush_invalid_blocks() +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_invalid_blocks.clear(); +} +//------------------------------------------------------------------ bool Blockchain::have_block(const crypto::hash& id) const { LOG_PRINT_L3("Blockchain::" << __func__); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6467031c2..915c48f80 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1016,6 +1016,11 @@ namespace cryptonote */ bool has_block_weights(uint64_t height, uint64_t nblocks) const; + /** + * @brief flush the invalid blocks set + */ + void flush_invalid_blocks(); + #ifndef IN_UNIT_TESTS private: #endif diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 02620996e..c4ef8b460 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1921,6 +1921,10 @@ namespace cryptonote bad_semantics_txes_lock.unlock(); } //----------------------------------------------------------------------------------------------- + void core::flush_invalid_blocks() + { + m_blockchain_storage.flush_invalid_blocks(); + } bool core::update_blockchain_pruning() { return m_blockchain_storage.update_blockchain_pruning(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index f69ac3509..4d86a0fcf 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -844,6 +844,11 @@ namespace cryptonote */ void flush_bad_txs_cache(); + /** + * @brief flushes the invalid block cache + */ + void flush_invalid_blocks(); + private: /** diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index b827221f6..8f06b1f01 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -846,13 +846,27 @@ bool t_command_parser_executor::set_bootstrap_daemon(const std::vector& args) { + bool bad_txs = false, bad_blocks = false; + std::string arg; + if (args.empty()) goto show_list; - if (args[0] == "bad-txs") - return m_executor.flush_cache(true); + + for (size_t i = 0; i < args.size(); ++i) + { + arg = args[i]; + if (arg == "bad-txs") + bad_txs = true; + else if (arg == "bad-blocks") + bad_blocks = true; + else + goto show_list; + } + return m_executor.flush_cache(bad_txs, bad_blocks); show_list: - std::cout << "Cache type needed: bad-txs" << std::endl; + std::cout << "Invalid cache type: " << arg << std::endl; + std::cout << "Cache types: bad-txs bad-blocks" << std::endl; return true; } diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 8ec690631..7fae77c30 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -325,7 +325,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "flush_cache" , std::bind(&t_command_parser_executor::flush_cache, &m_parser, p::_1) - , "flush_cache bad-txs" + , "flush_cache [bad-txs] [bad-blocks]" , "Flush the specified cache(s)." ); } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..0fae9b99e 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -2373,7 +2373,7 @@ bool t_rpc_command_executor::set_bootstrap_daemon( return true; } -bool t_rpc_command_executor::flush_cache(bool bad_txs) +bool t_rpc_command_executor::flush_cache(bool bad_txs, bool bad_blocks) { cryptonote::COMMAND_RPC_FLUSH_CACHE::request req; cryptonote::COMMAND_RPC_FLUSH_CACHE::response res; @@ -2381,6 +2381,7 @@ bool t_rpc_command_executor::flush_cache(bool bad_txs) epee::json_rpc::error error_resp; req.bad_txs = bad_txs; + req.bad_blocks = bad_blocks; if (m_is_rpc) { diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index e8b12cb9b..6276d124d 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -170,7 +170,7 @@ public: bool rpc_payments(); - bool flush_cache(bool bad_txs); + bool flush_cache(bool bad_txs, bool invalid_blocks); }; } // namespace daemonize diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..2438b3045 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -3004,6 +3004,8 @@ namespace cryptonote RPC_TRACKER(flush_cache); if (req.bad_txs) m_core.flush_bad_txs_cache(); + if (req.bad_blocks) + m_core.flush_invalid_blocks(); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 855ea854c..c3ee866bb 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 3 -#define CORE_RPC_VERSION_MINOR 0 +#define CORE_RPC_VERSION_MINOR 1 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -2559,9 +2559,11 @@ namespace cryptonote struct request_t { bool bad_txs; + bool bad_blocks; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_OPT(bad_txs, false) + KV_SERIALIZE_OPT(bad_blocks, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; From f1d379d27d02e5da46b3d23e1335019b178c13c6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 2 Dec 2019 23:21:45 +0000 Subject: [PATCH 125/469] simplewallet: fix "outputs in same tx" detector It was comparing source txids, but txids were empty, so all checks triggered --- src/simplewallet/simplewallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..b753fa1ed 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6072,6 +6072,7 @@ bool simple_wallet::print_ring_members(const std::vectorget_rpc_client_secret_key()); bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res); err = interpret_rpc_response(r, res.status); From 94266eeb89cad13b8f76a0ca16c143baf27820ee Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 2 Dec 2019 23:21:57 +0000 Subject: [PATCH 126/469] simplewallet: fix output age display with duplicate heights The highlight check was based on height, so would highlight any output at that height, resulting in several matches if a fake out was picked at the same height as the real spend --- src/simplewallet/simplewallet.cpp | 23 +++++++++++++---------- src/simplewallet/simplewallet.h | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index b753fa1ed..e6391470e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5822,8 +5822,14 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args if (uses) { std::vector heights; - for (const auto &e: td.m_uses) heights.push_back(e.first); - const std::pair line = show_outputs_line(heights, blockchain_height, td.m_spent_height); + uint64_t idx = 0; + for (const auto &e: td.m_uses) + { + heights.push_back(e.first); + if (e.first < td.m_spent_height) + ++idx; + } + const std::pair line = show_outputs_line(heights, blockchain_height, idx); extra_string += std::string("\n ") + tr("Used at heights: ") + line.first + "\n " + line.second; } message_writer(td.m_spent ? console_color_magenta : console_color_green, false) << @@ -5992,7 +5998,7 @@ bool simple_wallet::rescan_spent(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- -std::pair simple_wallet::show_outputs_line(const std::vector &heights, uint64_t blockchain_height, uint64_t highlight_height) const +std::pair simple_wallet::show_outputs_line(const std::vector &heights, uint64_t blockchain_height, uint64_t highlight_idx) const { std::stringstream ostr; @@ -6000,7 +6006,7 @@ std::pair simple_wallet::show_outputs_line(const std:: blockchain_height = std::max(blockchain_height, h); for (size_t j = 0; j < heights.size(); ++j) - ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j]; + ostr << (j == highlight_idx ? " *" : " ") << heights[j]; // visualize the distribution, using the code by moneroexamples onion-monero-viewer const uint64_t resolution = 79; @@ -6010,9 +6016,9 @@ std::pair simple_wallet::show_outputs_line(const std:: uint64_t pos = (heights[j] * resolution) / blockchain_height; ring_str[pos] = 'o'; } - if (highlight_height < blockchain_height) + if (highlight_idx < heights.size() && heights[highlight_idx] < blockchain_height) { - uint64_t pos = (highlight_height * resolution) / blockchain_height; + uint64_t pos = (heights[highlight_idx] * resolution) / blockchain_height; ring_str[pos] = '*'; } @@ -6094,14 +6100,11 @@ bool simple_wallet::print_ring_members(const std::vector heights(absolute_offsets.size(), 0); - uint64_t highlight_height = std::numeric_limits::max(); for (size_t j = 0; j < absolute_offsets.size(); ++j) { heights[j] = res.outs[j].height; - if (j == source.real_output) - highlight_height = heights[j]; } - std::pair ring_str = show_outputs_line(heights, blockchain_height, highlight_height); + std::pair ring_str = show_outputs_line(heights, blockchain_height, source.real_output); ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); } // warn if rings contain keys originating from the same tx or temporally very close block heights diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 75bd893d5..2ee14eb8a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -274,7 +274,7 @@ namespace cryptonote bool print_seed(bool encrypted); void key_images_sync_intern(); void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); - std::pair show_outputs_line(const std::vector &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits::max()) const; + std::pair show_outputs_line(const std::vector &heights, uint64_t blockchain_height, uint64_t highlight_idx = std::numeric_limits::max()) const; bool freeze_thaw(const std::vector& args, bool freeze); bool prompt_if_old(const std::vector &ptx_vector); bool on_command(bool (simple_wallet::*cmd)(const std::vector&), const std::vector &args); From 021cf733c637927de4f2988f8cf3df84b2bca9d2 Mon Sep 17 00:00:00 2001 From: Bertrand Jacquin Date: Sun, 1 Dec 2019 19:54:36 +0000 Subject: [PATCH 127/469] ssl: server-side: allow multiple version of TLS boost::asio::ssl::context is created using specifically TLSv1.2, which blocks the ability to use superior version of TLS like TLSv1.3. Filtering is also made specially later in the code to remove unsafe version for TLS such SSLv2, SSLv3 etc.. This change is removing double filtering to allow TLSv1.2 and above to be used. testssl.sh 3.0rc5 now reports the following (please note monerod was built with USE_EXTRA_EC_CERT): $ ./testssl.sh --openssl=/usr/bin/openssl \ --each-cipher --cipher-per-proto \ --server-defaults --server-preference \ --vulnerable --heartbleed --ccs --ticketbleed \ --robot --renegotiation --compression --breach \ --poodle --tls-fallback --sweet32 --beast --lucky13 \ --freak --logjam --drown --pfs --rc4 --full \ --wide --hints 127.0.0.1:38081 Using "OpenSSL 1.1.1d 10 Sep 2019" [~80 ciphers] on ip-10-97-15-6:/usr/bin/openssl (built: "Dec 3 21:14:51 2019", platform: "linux-x86_64") Start 2019-12-03 21:51:25 -->> 127.0.0.1:38081 (127.0.0.1) <<-- rDNS (127.0.0.1): -- Service detected: HTTP Testing protocols via sockets except NPN+ALPN SSLv2 not offered (OK) SSLv3 not offered (OK) TLS 1 not offered TLS 1.1 not offered TLS 1.2 offered (OK) TLS 1.3 offered (OK): final NPN/SPDY not offered ALPN/HTTP2 not offered Testing for server implementation bugs No bugs found. Testing cipher categories NULL ciphers (no encryption) not offered (OK) Anonymous NULL Ciphers (no authentication) not offered (OK) Export ciphers (w/o ADH+NULL) not offered (OK) LOW: 64 Bit + DES, RC[2,4] (w/o export) not offered (OK) Triple DES Ciphers / IDEA not offered (OK) Average: SEED + 128+256 Bit CBC ciphers not offered Strong encryption (AEAD ciphers) offered (OK) Testing robust (perfect) forward secrecy, (P)FS -- omitting Null Authentication/Encryption, 3DES, RC4 PFS is offered (OK), ciphers follow (client/browser support is important here) Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC) ----------------------------------------------------------------------------------------------------------------------------- x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384 x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 Elliptic curves offered: prime256v1 secp384r1 secp521r1 X25519 X448 Testing server preferences Has server cipher order? yes (OK) Negotiated protocol TLSv1.3 Negotiated cipher TLS_AES_256_GCM_SHA384, 253 bit ECDH (X25519) Cipher order TLSv1.2: ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-GCM-SHA256 TLSv1.3: TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256 Testing server defaults (Server Hello) TLS extensions (standard) "renegotiation info/#65281" "EC point formats/#11" "supported versions/#43" "key share/#51" "max fragment length/#1" "extended master secret/#23" Session Ticket RFC 5077 hint no -- no lifetime advertised SSL Session ID support yes Session Resumption Tickets no, ID: no TLS clock skew Random values, no fingerprinting possible Server Certificate #1 (in response to request w/o SNI) Signature Algorithm SHA256 with RSA Server key size RSA 4096 bits Server key usage -- Server extended key usage -- Serial / Fingerprints 01 / SHA1 132E42981812F5575FA0AE64922B18A81B38C03F SHA256 EBA3CC4AA09DEF26706E64A70DB4BC8D723533BB67EAE12B503A845019FB61DC Common Name (CN) (no CN field in subject) subjectAltName (SAN) missing (NOT ok) -- Browsers are complaining Issuer Trust (hostname) certificate does not match supplied URI Chain of trust NOT ok (self signed) EV cert (experimental) no "eTLS" (visibility info) not present Certificate Validity (UTC) 181 >= 60 days (2019-12-03 21:51 --> 2020-06-02 21:51) # of certificates provided 1 Certificate Revocation List -- OCSP URI -- NOT ok -- neither CRL nor OCSP URI provided OCSP stapling not offered OCSP must staple extension -- DNS CAA RR (experimental) not offered Certificate Transparency -- Server Certificate #2 (in response to request w/o SNI) Signature Algorithm ECDSA with SHA256 Server key size EC 256 bits Server key usage -- Server extended key usage -- Serial / Fingerprints 01 / SHA1 E17B765DD8124525B1407E827B89A31FB167647D SHA256 AFB7F44B1C33831F521357E5AEEB813044CB02532143E92D35650A3FF792A7C3 Common Name (CN) (no CN field in subject) subjectAltName (SAN) missing (NOT ok) -- Browsers are complaining Issuer Trust (hostname) certificate does not match supplied URI Chain of trust NOT ok (self signed) EV cert (experimental) no "eTLS" (visibility info) not present Certificate Validity (UTC) 181 >= 60 days (2019-12-03 21:51 --> 2020-06-02 21:51) # of certificates provided 1 Certificate Revocation List -- OCSP URI -- NOT ok -- neither CRL nor OCSP URI provided OCSP stapling not offered OCSP must staple extension -- DNS CAA RR (experimental) not offered Certificate Transparency -- Testing HTTP header response @ "/" HTTP Status Code 404 Not found (Hint: supply a path which doesn't give a "404 Not found") HTTP clock skew Got no HTTP time, maybe try different URL? Strict Transport Security not offered Public Key Pinning -- Server banner Epee-based Application banner -- Cookie(s) (none issued at "/") -- maybe better try target URL of 30x Security headers -- Reverse Proxy banner -- Testing vulnerabilities Heartbleed (CVE-2014-0160) not vulnerable (OK), no heartbeat extension CCS (CVE-2014-0224) not vulnerable (OK) Ticketbleed (CVE-2016-9244), experiment. not vulnerable (OK), no session ticket extension ROBOT Server does not support any cipher suites that use RSA key transport Secure Renegotiation (CVE-2009-3555) not vulnerable (OK) Secure Client-Initiated Renegotiation not vulnerable (OK) CRIME, TLS (CVE-2012-4929) not vulnerable (OK) BREACH (CVE-2013-3587) no HTTP compression (OK) - only supplied "/" tested POODLE, SSL (CVE-2014-3566) not vulnerable (OK) TLS_FALLBACK_SCSV (RFC 7507) No fallback possible, no protocol below TLS 1.2 offered (OK) SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK) FREAK (CVE-2015-0204) not vulnerable (OK) DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this host and port (OK) make sure you don't use this certificate elsewhere with SSLv2 enabled services https://censys.io/ipv4?q=EBA3CC4AA09DEF26706E64A70DB4BC8D723533BB67EAE12B503A845019FB61DC could help you to find out LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2 BEAST (CVE-2011-3389) no SSL3 or TLS1 (OK) LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK) RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK) Testing ciphers per protocol via OpenSSL plus sockets against the server, ordered by encryption strength Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC) ----------------------------------------------------------------------------------------------------------------------------- SSLv2 SSLv3 TLS 1 TLS 1.1 TLS 1.2 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS 1.3 x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384 x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256 x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256 Running client simulations (HTTP) via sockets Browser Protocol Cipher Suite Name (OpenSSL) Forward Secrecy ------------------------------------------------------------------------------------------------ Android 4.2.2 No connection Android 4.4.2 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Android 5.0.0 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 256 bit ECDH (P-256) Android 6.0 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 256 bit ECDH (P-256) Android 7.0 TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 253 bit ECDH (X25519) Android 8.1 (native) No connection Android 9.0 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519) Chrome 65 Win 7 TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 253 bit ECDH (X25519) Chrome 74 (Win 10) No connection Firefox 62 Win 7 TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 253 bit ECDH (X25519) Firefox 66 (Win 8.1/10) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519) IE 6 XP No connection IE 7 Vista No connection IE 8 Win 7 No connection IE 8 XP No connection IE 11 Win 7 No connection IE 11 Win 8.1 No connection IE 11 Win Phone 8.1 No connection IE 11 Win 10 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Edge 15 Win 10 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519) Edge 17 (Win 10) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519) Opera 60 (Win 10) No connection Safari 9 iOS 9 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Safari 9 OS X 10.11 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Safari 10 OS X 10.12 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Apple ATS 9 iOS 9 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Tor 17.0.9 Win 7 No connection Java 6u45 No connection Java 7u25 No connection Java 8u161 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 256 bit ECDH (P-256) Java 9.0.4 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 256 bit ECDH (P-256) OpenSSL 1.0.1l TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 256 bit ECDH (P-256) OpenSSL 1.0.2e TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 256 bit ECDH (P-256) OpenSSL 1.1.0j (Debian) TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 253 bit ECDH (X25519) OpenSSL 1.1.1b (Debian) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519) Thunderbird (60.6) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519) --- contrib/epee/src/net_ssl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index 16454fce0..e664a4981 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -285,7 +285,7 @@ ssl_options_t::ssl_options_t(std::vector> fingerprints boost::asio::ssl::context ssl_options_t::create_context() const { - boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tlsv12}; + boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tls}; if (!bool(*this)) return ssl_context; From a2578892c06c570d9a45a136898dbd89bba9d37c Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Wed, 4 Dec 2019 15:21:39 +0300 Subject: [PATCH 128/469] --disable-ban-rpc option to prevent RPC users from banning --- src/rpc/core_rpc_server.cpp | 4 +++- src/rpc/core_rpc_server.h | 1 + src/rpc/rpc_args.cpp | 3 +++ src/rpc/rpc_args.h | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..c892f1048 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -152,6 +152,7 @@ namespace cryptonote : m_core(cr) , m_p2p(p2p) , m_was_bootstrap_ever_used(false) + , disable_rpc_ban(false) {} //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password) @@ -241,6 +242,7 @@ namespace cryptonote if (!rpc_config) return false; + disable_rpc_ban = rpc_config->disable_rpc_ban; std::string address = command_line::get_arg(vm, arg_rpc_payment_address); if (!address.empty()) { @@ -353,7 +355,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::add_host_fail(const connection_context *ctx, unsigned int score) { - if(!ctx || !ctx->m_remote_address.is_blockable()) + if(!ctx || !ctx->m_remote_address.is_blockable() || disable_rpc_ban) return false; CRITICAL_REGION_LOCAL(m_host_fails_score_lock); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 23c611470..54a4945e6 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -285,6 +285,7 @@ private: epee::critical_section m_host_fails_score_lock; std::map m_host_fails_score; std::unique_ptr m_rpc_payment; + bool disable_rpc_ban; }; } diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 0eaa0ef0e..dcb804d3e 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -103,6 +103,7 @@ namespace cryptonote , rpc_ssl_allowed_fingerprints({"rpc-ssl-allowed-fingerprints", rpc_args::tr("List of certificate fingerprints to allow")}) , rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false}) , rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false}) + , disable_rpc_ban({"disable-rpc-ban", rpc_args::tr("Do not ban hosts on RPC errors"), false, false}) {} const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); } @@ -123,6 +124,7 @@ namespace cryptonote command_line::add_arg(desc, arg.rpc_ssl_ca_certificates); command_line::add_arg(desc, arg.rpc_ssl_allowed_fingerprints); command_line::add_arg(desc, arg.rpc_ssl_allow_chained); + command_line::add_arg(desc, arg.disable_rpc_ban); if (any_cert_option) command_line::add_arg(desc, arg.rpc_ssl_allow_any_cert); } @@ -136,6 +138,7 @@ namespace cryptonote config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address); config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6); config.require_ipv4 = !command_line::get_arg(vm, arg.rpc_ignore_ipv4); + config.disable_rpc_ban = command_line::get_arg(vm, arg.disable_rpc_ban); if (!config.bind_ip.empty()) { // always parse IP here for error consistency diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index bdb9c70d5..ac6eb2744 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -65,6 +65,7 @@ namespace cryptonote const command_line::arg_descriptor> rpc_ssl_allowed_fingerprints; const command_line::arg_descriptor rpc_ssl_allow_chained; const command_line::arg_descriptor rpc_ssl_allow_any_cert; + const command_line::arg_descriptor disable_rpc_ban; }; // `allow_any_cert` bool toggles `--rpc-ssl-allow-any-cert` configuration @@ -85,5 +86,6 @@ namespace cryptonote std::vector access_control_origins; boost::optional login; // currently `boost::none` if unspecified by user epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + bool disable_rpc_ban = false; }; } From 72ca7e3b0faacfdabebc0a07c4fab4cdc17f7748 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 5 Dec 2019 21:02:18 -0400 Subject: [PATCH 129/469] Fix time comparison math Dividing `dt` here by 1e6 converts it to seconds, but that is clearly wrong since `REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY` is measured in microseconds. As a result, this if statement was effectively never used. --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 74ceeb41d..9b8bd55cd 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1673,9 +1673,9 @@ skip: const float max_multiplier = 10.f; const float min_multiplier = 1.25f; float multiplier = max_multiplier; - if (dt/1e6 >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) + if (dt >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) { - multiplier = max_multiplier - (dt/1e6-REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) * (max_multiplier - min_multiplier) / (REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD - REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY); + multiplier = max_multiplier - (dt-REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) * (max_multiplier - min_multiplier) / (REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD - REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY); multiplier = std::min(max_multiplier, std::max(min_multiplier, multiplier)); } if (dl_speed * .8f > ctx.m_current_speed_down * multiplier) From a6a2ad6cacb23c39dbaa6b02ef6b80cfe1ffa859 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 7 Dec 2019 13:56:53 +0000 Subject: [PATCH 130/469] simplewallet: set manual refresh mode in rescan_bc This ensures we get asked for the password if needed --- src/simplewallet/simplewallet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..3f5eec691 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8786,6 +8786,8 @@ bool simple_wallet::rescan_blockchain(const std::vector &args_) } } + m_in_manual_refresh.store(true, std::memory_order_relaxed); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); return refresh_main(start_height, reset_type, true); } //---------------------------------------------------------------------------------------------------- From d93e1dffa9c4f39888a4f766687f599855b86e8f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 9 Dec 2019 12:25:45 +0000 Subject: [PATCH 131/469] simplewallet: warn on refresh if refresh-from-block-height seems off --- src/simplewallet/simplewallet.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..0ae9f9a0f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5553,6 +5553,14 @@ boost::optional simple_wallet::on_device_passphrase_reque //---------------------------------------------------------------------------------------------------- void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money) { + const uint64_t rfbh = m_wallet->get_refresh_from_block_height(); + std::string err; + const uint64_t dh = m_wallet->get_daemon_blockchain_height(err); + if (err.empty() && rfbh > dh) + { + message_writer(console_color_yellow, false) << tr("The wallet's refresh-from-block-height setting is higher than the daemon's height: this may mean your wallet will skip over transactions"); + } + // Key image sync after the first refresh if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) { return; From c36130318498dfb66926daa383096d77710f162f Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 8 Dec 2019 02:45:49 +0000 Subject: [PATCH 132/469] Silence miner debugmsg spam Don't try to allocate the dataset repeatedly if it has already failed. --- src/crypto/rx-slow-hash.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index a7a459ad3..969bf8a0f 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -62,6 +62,7 @@ static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT; static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}}; static randomx_dataset *rx_dataset; +static int rx_dataset_nomem; static uint64_t rx_dataset_height; static THREADV randomx_vm *rx_vm = NULL; @@ -246,20 +247,25 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch } if (miners) { CTHR_MUTEX_LOCK(rx_dataset_mutex); - if (rx_dataset == NULL) { - rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); + if (!rx_dataset_nomem) { if (rx_dataset == NULL) { - mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset"); - rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); + rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); + if (rx_dataset == NULL) { + mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset"); + rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); + } + if (rx_dataset != NULL) + rx_initdata(rx_sp->rs_cache, miners, seedheight); } - if (rx_dataset != NULL) - rx_initdata(rx_sp->rs_cache, miners, seedheight); } if (rx_dataset != NULL) flags |= RANDOMX_FLAG_FULL_MEM; else { miners = 0; - mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner"); + if (!rx_dataset_nomem) { + rx_dataset_nomem = 1; + mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner"); + } } CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } @@ -309,5 +315,6 @@ void rx_stop_mining(void) { rx_dataset = NULL; randomx_release_dataset(rd); } + rx_dataset_nomem = 0; CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } From 987c3139dce921c98b4a24bab52354102f21fb1e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 9 Dec 2019 14:56:44 +0000 Subject: [PATCH 133/469] print_coinbase_tx_sum now supports 128 bits sums The tail emission will bring the total above 64 bits --- .../cryptonote_format_utils.cpp | 18 ++++++++++++++++-- src/cryptonote_basic/cryptonote_format_utils.h | 2 ++ src/cryptonote_core/cryptonote_core.cpp | 8 ++++---- src/cryptonote_core/cryptonote_core.h | 2 +- src/daemon/rpc_command_executor.cpp | 6 +++--- src/rpc/core_rpc_server.cpp | 17 +++++++++++------ src/rpc/core_rpc_server_commands_defs.h | 8 ++++++++ tests/functional_tests/blockchain.py | 5 +++++ 8 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 138cf49f4..651d61b06 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -996,17 +996,31 @@ namespace cryptonote } } //--------------------------------------------------------------- - std::string print_money(uint64_t amount, unsigned int decimal_point) + static void insert_money_decimal_point(std::string &s, unsigned int decimal_point) { if (decimal_point == (unsigned int)-1) decimal_point = default_decimal_point; - std::string s = std::to_string(amount); if(s.size() < decimal_point+1) { s.insert(0, decimal_point+1 - s.size(), '0'); } if (decimal_point > 0) s.insert(s.size() - decimal_point, "."); + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount, unsigned int decimal_point) + { + std::string s = std::to_string(amount); + insert_money_decimal_point(s, decimal_point); + return s; + } + //--------------------------------------------------------------- + std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point) + { + std::stringstream ss; + ss << amount; + std::string s = ss.str(); + insert_money_decimal_point(s, decimal_point); return s; } //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 29e4def64..8ed3b0b43 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -38,6 +38,7 @@ #include "crypto/crypto.h" #include "crypto/hash.h" #include +#include namespace epee { @@ -139,6 +140,7 @@ namespace cryptonote unsigned int get_default_decimal_point(); std::string get_unit(unsigned int decimal_point = -1); std::string print_money(uint64_t amount, unsigned int decimal_point = -1); + std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point = -1); //--------------------------------------------------------------- template bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 02620996e..460ff07d8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1178,10 +1178,10 @@ namespace cryptonote return m_mempool.check_for_key_images(key_im, spent); } //----------------------------------------------------------------------------------------------- - std::pair core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count) + std::pair core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count) { - uint64_t emission_amount = 0; - uint64_t total_fee_amount = 0; + boost::multiprecision::uint128_t emission_amount = 0; + boost::multiprecision::uint128_t total_fee_amount = 0; if (count) { const uint64_t end = start_offset + count - 1; @@ -1203,7 +1203,7 @@ namespace cryptonote }); } - return std::pair(emission_amount, total_fee_amount); + return std::pair(emission_amount, total_fee_amount); } //----------------------------------------------------------------------------------------------- bool core::check_tx_inputs_keyimages_diff(const transaction& tx) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index f69ac3509..01df86fd5 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -743,7 +743,7 @@ namespace cryptonote * * @return the number of blocks to sync in one go */ - std::pair get_coinbase_tx_sum(const uint64_t start_offset, const size_t count); + std::pair get_coinbase_tx_sum(const uint64_t start_offset, const size_t count); /** * @brief get the network type we're on diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..94fb304e5 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1851,9 +1851,9 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou tools::msg_writer() << "Sum of coinbase transactions between block heights [" << height << ", " << (height + count) << ") is " - << cryptonote::print_money(res.emission_amount + res.fee_amount) << " " - << "consisting of " << cryptonote::print_money(res.emission_amount) - << " in emissions, and " << cryptonote::print_money(res.fee_amount) << " in fees"; + << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_emission_amount) + boost::multiprecision::uint128_t(res.wide_fee_amount)) << " " + << "consisting of " << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_emission_amount)) + << " in emissions, and " << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_fee_amount)) << " in fees"; return true; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..0de667dd1 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -120,11 +120,16 @@ namespace return (value + quantum - 1) / quantum * quantum; } + void store_128(boost::multiprecision::uint128_t value, uint64_t &slow64, std::string &swide, uint64_t &stop64) + { + slow64 = (value & 0xffffffffffffffff).convert_to(); + swide = cryptonote::hex(value); + stop64 = ((value >> 64) & 0xffffffffffffffff).convert_to(); + } + void store_difficulty(cryptonote::difficulty_type difficulty, uint64_t &sdiff, std::string &swdiff, uint64_t &stop64) { - sdiff = (difficulty & 0xffffffffffffffff).convert_to(); - swdiff = cryptonote::hex(difficulty); - stop64 = ((difficulty >> 64) & 0xffffffffffffffff).convert_to(); + store_128(difficulty, sdiff, swdiff, stop64); } } @@ -2486,9 +2491,9 @@ namespace cryptonote return true; } CHECK_PAYMENT_MIN1(req, res, COST_PER_COINBASE_TX_SUM_BLOCK * req.count, false); - std::pair amounts = m_core.get_coinbase_tx_sum(req.height, req.count); - res.emission_amount = amounts.first; - res.fee_amount = amounts.second; + std::pair amounts = m_core.get_coinbase_tx_sum(req.height, req.count); + store_128(amounts.first, res.emission_amount, res.wide_emission_amount, res.emission_amount_top64); + store_128(amounts.second, res.fee_amount, res.wide_fee_amount, res.fee_amount_top64); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 855ea854c..5425e7f6c 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -2021,12 +2021,20 @@ namespace cryptonote struct response_t: public rpc_access_response_base { uint64_t emission_amount; + std::string wide_emission_amount; + uint64_t emission_amount_top64; uint64_t fee_amount; + std::string wide_fee_amount; + uint64_t fee_amount_top64; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(emission_amount) + KV_SERIALIZE(wide_emission_amount) + KV_SERIALIZE(emission_amount_top64) KV_SERIALIZE(fee_amount) + KV_SERIALIZE(wide_fee_amount) + KV_SERIALIZE(fee_amount_top64) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response; diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py index 78e0d8952..b8f8bac1a 100755 --- a/tests/functional_tests/blockchain.py +++ b/tests/functional_tests/blockchain.py @@ -203,10 +203,15 @@ class BlockchainTest(): res_sum = daemon.get_coinbase_tx_sum(i, 1) res_header = daemon.getblockheaderbyheight(i) assert res_sum.emission_amount == res_header.block_header.reward + assert res_sum.emission_amount_top64 == 0 + assert res_sum.emission_amount == int(res_sum.wide_emission_amount, 16) + assert res_sum.fee_amount == int(res_sum.wide_fee_amount, 16) res = daemon.get_coinbase_tx_sum(0, 1) assert res.emission_amount == 17592186044415 + assert res.emission_amount_top64 == 0 assert res.fee_amount == 0 + assert res.fee_amount_top64 == 0 sum_blocks = height + nblocks - 1 res = daemon.get_coinbase_tx_sum(0, sum_blocks) extrapolated = 17592186044415 + 17592186044415 * 2 * (sum_blocks - 1) From 8e338e555c353f14a10c7bb1747ef9a0038a4ff3 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Thu, 12 Dec 2019 23:39:37 +0100 Subject: [PATCH 134/469] depends: update libsodium to 1.0.18 --- contrib/depends/packages/sodium.mk | 4 ++-- contrib/depends/patches/sodium/fix-whitespace.patch | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index dbf86fc5a..462bd2415 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -1,8 +1,8 @@ package=sodium -$(package)_version=1.0.16 +$(package)_version=1.0.18 $(package)_download_path=https://download.libsodium.org/libsodium/releases/ $(package)_file_name=libsodium-$($(package)_version).tar.gz -$(package)_sha256_hash=eeadc7e1e1bcef09680fb4837d448fbdf57224978f865ac1c16745868fbd0533 +$(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1 $(package)_patches=fix-whitespace.patch define $(package)_set_vars diff --git a/contrib/depends/patches/sodium/fix-whitespace.patch b/contrib/depends/patches/sodium/fix-whitespace.patch index efbfe4e83..c3d3af0b4 100644 --- a/contrib/depends/patches/sodium/fix-whitespace.patch +++ b/contrib/depends/patches/sodium/fix-whitespace.patch @@ -5,8 +5,8 @@ index b29f769..ca008ae 100755 @@ -591,7 +591,7 @@ MAKEFLAGS= PACKAGE_NAME='libsodium' PACKAGE_TARNAME='libsodium' - PACKAGE_VERSION='1.0.16' --PACKAGE_STRING='libsodium 1.0.16' + PACKAGE_VERSION='1.0.18' +-PACKAGE_STRING='libsodium 1.0.18' +PACKAGE_STRING='libsodium' PACKAGE_BUGREPORT='https://github.com/jedisct1/libsodium/issues' PACKAGE_URL='https://github.com/jedisct1/libsodium' From aa9ba3064e4b59f7c378561f2716ca129bc7f846 Mon Sep 17 00:00:00 2001 From: binaryFate Date: Fri, 13 Dec 2019 21:03:56 +0100 Subject: [PATCH 135/469] add binaryFate's PGP key --- utils/gpg_keys/binaryfate.asc | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 utils/gpg_keys/binaryfate.asc diff --git a/utils/gpg_keys/binaryfate.asc b/utils/gpg_keys/binaryfate.asc new file mode 100644 index 000000000..55c5b161c --- /dev/null +++ b/utils/gpg_keys/binaryfate.asc @@ -0,0 +1,87 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBF3yDrwBEAC1UgCSLILsbdrSk5kfcEYKMvj7lJpIIj9D6LeeyIvovgO7beM0 +63cFCT0v+RH3CVKV8bCCJr8teR3Zgk+IeI6C0CQk+ocqlu0qBAALdZyGyZonozbc +lHGOfQ0rWEy01V/TB36bGhrsE/cM8nhICJ72Pkv3rrukZkprxvEJ+IYCk26Umiue +K1+Pm0sUMrxAQUYlvg8r1swOgLOuo7r8c1gZYvGixdb0t7mBzUgkSdmFUeAa/X8W +WzBPFluWMyetGUKzrV66W1ISHHi/2AyXim235Lqc4MbK2ObKfkZJCjC7y2afs5MS +t+uejz8bchLMM/LvV2TxKIbenho7ZxtGd8blNRAPe6FTOA+yOM50atJah1W3BmLx +sxk4gvII2/zlzaW4RNEy1Ma/47DINPwYB9BA7FqF7BTVt8WxpJ5Y2+8aR58ZtM+q +ObbO5s2O8kuDj94qQKAT8btetbb/pMKhF3XXSARkNZPzNFFtSy9xSin+hLAWpM0b +cftEtJrE7HY7DHOa9J4P/xFqLSQnZGpClg5lyRw34kU1l8sAzovFngN2Zn+RYkiW +y14I+uhVOFzroH2ymIYTwQ0crQJ4OYgtqzv0Rc+U2mTrTE+MRG28COsBqoMvWao9 +K4bhsP6qUmlTtMVFob4AP3eFqF3wg987zqeeCqaidAc7n5fuJHTDGNWFxQARAQAB +tCViaW5hcnlGYXRlIDxiaW5hcnlmYXRlQGdldG1vbmVyby5vcmc+iQJOBBMBCgA4 +FiEEgaxZH+nEtlxYBq/D8K9NRioL35IFAl3yDrwCGy8FCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AACgkQ8K9NRioL35IT2w/+JaVcSVo3vXTpC9TOSxnyjFP1QuPVxn+K +P4la6MzTQlaZOSaoGuXra+82ghWqJGbFhGYmsXWQZtUYVwTMq9N9IUJ55clfkiV3 +HQNIDh/6zz+ibBzxfNLAPTbz0OQ+26dWRmBKkjpIs8KWyVIR/Ma/ax1No20fPKoN +cgRvOct/BKNoX2+PlHh/0tTTIjc0NqNP79ptNyJUUTGL1uZOgfTGa7/FRxil1xiC +JZfbZ2mbdesqjTtF3CI/ahhPKUEKy5Tciy5pnErALPk4rsxdLNdl3NOVrb5tYqGv +GqoQT3KsHkYVHvLCx0Ji2peFa6lF0PyJpudUzlo7L3nn39b8v9ebKd/fk2gthnWr +eoFxEFa6dI0x+HA47FE00I7ze56oH0zFVUhx+3QOHFG3u2mHV+H+GfKaXq9Nd5nU +Wk8AGKJ/xYgX7G0TnUek9aOzSt3dRd+xtSjmgwA3T7vCi6F67SthRpq7F4zuecPl ++Ww1MQhXSdIi6P3ll09gTCOICaqOxuOkMeFFrs2w4XEU517QUzy1VTAcF3gSy9TL +Xpy5jqssjLjvDAHs/Jm+iryQtn/t9YHr7/DP7AAr4+e7aOY3659jP7UiMZ3RKqY5 +/15Ru/usXXFyC56Epsv1spBCthimiK/RdX8XW7mG+BIQJXN337WdicXGWrKVOixj +eJ7ghiSblIeJAjMEEAEKAB0WIQTsEs+GtgjVvRuRCZGpI7HrTHj9WAUCXfOqVwAK +CRCpI7HrTHj9WPSaD/wMa2PWbRj06DUHiKRMrrQF9gkzox2ZOd4om4YIa3opA+GJ +CRl6nMHO7MdGTiun0/jBoT4oU4rqvPYcQlKOXUFYaMz/3vPYyTotC8n1Rvf+DhQZ +CIubdrRW0B5pqa3MiyJ9S/oMf/5otto3eBZSnfegI0J/fNzyDAyS2WIj/iyoAXGQ +yy9lY+KQnjchIHItxFm6RWgZmbO6xbhRq+LUa90jEiSul1PoE+ldpH2wUlRvjP78 +lL4YqWC7U6puhgGPlQV2gvFG4zscXVf8XXV65KysqrB5RI8wx1JxC1n1ocOqwYbw +0P6WIUGSr98cBjzB1l9zKhHumV7AM9FNUf4GD+Mgl8I8i/vOSz8kYRaWCcoRZvWa +pgSEG4uA94MFZUP9b789quoMUv8kTboHU1XFDo/n7K83AVBThcj0BMt7ueZmwjtu +2YVc3vGX7GvsUr8XXYn82hqnHcBoVLI4awGMzzAiOI1KSPtksGr36mYgOuY1jYme +435YlVxH4A/NC1qvOZYLq/6u1pB/BouBtlVxCvyjhlM3/r0PgDB0KveiR72LzVXH +TB0CzmyizZnVYbNR1PvsxFvyPxRLIIfN1nzXFYxwILASlg0zhdkQgFWkZ+WJWA6g +6HrAmrrA8iV7x6ITR6dXrT2fSLdQ+nYTntaXunUw74gGWYaMPe/GTvgcguFoU4kC +MwQQAQoAHRYhBOyzccBByW6YhVJsYUZMOzgUXzoUBQJd8647AAoJEEZMOzgUXzoU +fvgP/jI1w6DxC4qRlEwsIpjvyKQbCWYr9w817gKRPOI0ZHbjoL2zBnBwAWvgEKXV +QTOkldwpWVHvt5qZweO89bJxQFShFdQklEbVXNlf9hMZqbWHt6gyS/65caEUT/nB +pugg6Ug3MIeszin7S2sKeKj3BT2ynJioMeNNUPntogUfWlOuhkVr3JezHV3TtxKf +yrs49AAmoAHK4DH34H7d8HuVjjr0v7gQc2Yu3MeWdR4drGmyU3uNY44fGqU9pP99 +HNW2Ec//HFIz+qgBZ63ps6qyWGOjXyrpn6k9L1lg8zilC1sJxRtYTwp1a/57Kpyu +NkR1265o2zu/cvb5oQxjCWmQPiZWKsoguHxiA93g14QdmnugHPM5L6XCqDl1fkQo +dnCy5uqlqs9Ucz6O4C1GV4Av3YsMepS2Bw5uZFyujqPUdw82oWWOwZfO2xw7kKzN +zd8OnQAKj3piMh1vh375HE+HwdN74txKDcD/HqoTMwuaqX145csUvORoABA/wU4w +yBJiOhJeqvUaWAsG0q6XQMIDYjGQ0rrtY6Ba9E/1UP3D2CpjH5fZChV1fxhCaQOi +nMSCwgA3HRiT4CnTYScsbnk7RcdOlc2Zff+cxyZfMvqcGM4R50NDYu8YnVjnyajn +No5WhvYYPQIoYaDM8b+RHGw8Zw4WpQqAqli0VoNJAux19os9uQINBF3yDrwBEADI +LMleZnQ87iFofqyMm6wd+146dPC0xINz3+ExkCcFJgwiHL4o1sF8LtXjBXVuoc33 +vK7mdU1Fm3N9D4W5tkxEW3NdHICc7r3IHqThFv6lOdckKg1t9HKWzEjWkB4/4Epr +sj7Xc1owuQkOVtkaWiXzv8e/pYM4j+21V71+8b6fuInvA1nNufawzN4m8RDDgbe9 +I9SdndJoO0vnhUrv55APVG6KJBRjT+SGrxkEa3cckhpGUsQkW8KEx1VH0Wc4NBdD +xSRzW+ZUDyMzoUD/b5kxNCqinaM3P95jmNJgqB9l/3ZEPZ0G/hqDg6EhQMe8HrYT +F90Q+QqsX35RbBrdXxjbod+GZPSTg0PExm/hBrt2tTE/t/yovn22PZeJm6Mg9trN +x08b/LLZ1L040s8WqQQUK0btG065PIVRpyIlsUEPt7W+Qdj+eJdZAjIDr04qBxUQ +bwHhnxCYH4PNQq75y/w684ZWoVW3UJE1P8AcXKx/0gp4jjrZCJTAkzfUBEc6vy6H +hzYLpAar52ALBmFGPpkV4PJDdlFX4uijsjVVf23Hi3AQnGWkUw34bftJuHYlluGg +aGXVzISn8MxqcaLUiapxMMg3pZ40m5gCHG7/Gm74xKZCQiNrxCLHn7/rgNV17U6m +aWCNGcaiCXfkbtqUPJrzK6ulDAhg3700Ok5RlVOtAwARAQABiQRsBBgBCgAgFiEE +gaxZH+nEtlxYBq/D8K9NRioL35IFAl3yDrwCGy4CQAkQ8K9NRioL35LBdCAEGQEK +AB0WIQStVkzajxZlrOeLXf0lk4OOq7H2VQUCXfIOvAAKCRAlk4OOq7H2VfMbD/oC +NxmlGhAJ2okXzcpFURNyIXTCt/Dlys+lwH2mwCjcniUcA5KsT1mV1Sb4SxsBsX18 +Fhol2wUUI/B3ELz15rOYTJq7u8TPpPnGsHtALgNwN1o90EYMRpMl7jiPJLKSWVA1 +spp7FBginaP/jkQwtcy+sHiJvmEiY2kmeB27aAA5bM8bafjzpqQ6Io/iguRVYex1 +ypH5D1Dq5bkrk8Fd+3bs2dcuB9jYy0lQlaYl8bJ4I4ZOgdO4nTKq0U0RTfYAR+wH +vpfzKrYQD7ZnX6mllXyvC+L4CujngND3GiavoOPX9JHJURrYC1WiMyKPb8nk1CrF +cIfLxFYqy535suen35T23G8B0ZY/i48ccRjI7ZrickIBSjJZmJLYevsejz+T1vqf +348WCAYisi44NL6vwAoBkvYLRw45YhzAZDZoKCv+ke7zjZAO3lnh+rR3j+QCe9gH +22gqJbhGpU54EpYYVx3GxIfaqQeHa5H02rSW8AUGyGjgdTo7x5NCAJjgtYyR08k1 +19aghaA8yi1wsbmiuz9viH2xJ1vPTgLLQE5qv0ed0uOGEeDtRZSfaWeYwk+qhier +bY3R3dzVOyan6W8uWaVgE8Ch9LttYAKa9qLoLDQE9tQLU6t1gklFfQiaLBzyPIgT +Ct6Aezwlcp2cDhxYazXjyxRTYtfshVPTXJMBF+0o+W78D/9c3f9Yv61Z1MktrHIt +5REIj5YGT3bAeDnUoGE+k+QzNx8856Hu5j4prQGav8F8+V69iqwVciK0sBoFUgI5 +JJizfVbEfmbZgl5c4S8OhApNv0Gbhj9U/XftM2Pz/m/QfsB5Az1jQ8jQGHULqEWB +d4Z+Cnt0LVmGxJkz0prlaUxrGp8NyO3A1RxWJbP6P9fPIJFFwDSer2xeq79dZGFQ +CLFxA3d8kjr9nioGZFaGK/LfbVcZBBM6VI0ziKRnWPeS3Zi50oy7+/gk7HgUvUmT +8uenHLE/kmfHg1lAycQ/TDY7/sP1Wtnbr72HEFOKDIvFIF8zaWwpaYSWrj6NRTJE +b9wa2Y5TervcvOcgzhleJP3PfDrahlfgUFtD/919cuQNG416tedumAaAlLpdBEUH +9F9FQ2Hzp8Pxm3N6LzbtgDoc6NXY2C08NPI+IwHysNIXzaH3jJ53TV8pCNvvd4Ok +Sk2fOVG/fDuZibwthAFjR3NIVJIP19xyxkalbeuLT+IUSzgN2ndojtK9eh7awMii +S/AkjI5G33OKYP0WZx+6o0i21rWXIepWgm74wMa9t9NCnNY50NIvesnG1AaXNKJ5 +D2o1uLXbwis6Fm2sMNutCkQFYsk/IFWC/Y8DsJzwkOty8gl3Q0NQi64IXpFGzUuG +lmE0kDaYQqoBKXNXcybElBID8Q== +=tkJZ +-----END PGP PUBLIC KEY BLOCK----- From 8a276451b252dba4067ecedd49d3abdbebb4cc86 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 14 Dec 2019 02:13:17 +0000 Subject: [PATCH 136/469] blockchain: fix flushing txes from the txpool --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7357e44d0..1fdbb92e9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3661,7 +3661,7 @@ bool Blockchain::flush_txes_from_pool(const std::vector &txids) uint64_t fee; bool relayed, do_not_relay, double_spend_seen, pruned; MINFO("Removing txid " << txid << " from the pool"); - if(!m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned)) + if(m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; From 5cbb17b94681fd0c515c1992fa92505791475571 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 14 Dec 2019 16:21:55 +0000 Subject: [PATCH 137/469] wallet2: fix hang in wallet refresh If the hashes received would move the current blockchain past the stop point, the short history would not be updated, since we do not expect another loop, but the daemon might return earlier hashes, causing the end index to not be enough to reach the threshold and this require another loop, which will download the same hashes and cause an infinite loop. --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..31112bf5d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3114,6 +3114,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset()); return; } + current_index = blocks_start_height; if (hashes.size() + current_index < stop_height) { drop_from_short_history(short_chain_history, 3); std::vector::iterator right = hashes.end(); @@ -3123,7 +3124,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, short_chain_history.push_front(*right); } } - current_index = blocks_start_height; for(auto& bl_id: hashes) { if(current_index >= m_blockchain.size()) From bc6d8aa34303e29a20560f69399b562baee58561 Mon Sep 17 00:00:00 2001 From: palomato <42439169+palomato@users.noreply.github.com> Date: Sat, 14 Dec 2019 13:06:09 -0500 Subject: [PATCH 138/469] Corrected message typo Message was "peer claims higher version that we think" Requested change "peer claims higher version than we think" --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index e20934a25..a34d0b0d3 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -314,7 +314,7 @@ namespace cryptonote if (version >= 6 && version != hshd.top_version) { if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version()) - MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think (" << + MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" << (unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version << ") - we may be forked from the network and a software upgrade may be needed"); return false; From ae84ec90c8756ceaa2f05c00ad9dc943ac35b46f Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 16 Dec 2019 13:12:31 +0900 Subject: [PATCH 139/469] wallet-cli/rpc: allow sweep_all to use outputs in all subaddresses within an account --- src/simplewallet/simplewallet.cpp | 15 ++++++++++----- src/wallet/wallet_rpc_server.cpp | 13 ++++++++++++- src/wallet/wallet_rpc_server_commands_defs.h | 4 +++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a35ee40ae..6237aaba6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -180,8 +180,8 @@ namespace const char* USAGE_PAYMENT_ID("payment_id"); const char* USAGE_TRANSFER("transfer [index=[,,...]] [] [] ( |
) []"); const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=[,,...]] [] [] ( | ) []"); - const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=[,,...]] [] []
[]"); - const char* USAGE_SWEEP_ALL("sweep_all [index=[,,...]] [] [] [outputs=]
[]"); + const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=[,,...] | index=all] [] []
[]"); + const char* USAGE_SWEEP_ALL("sweep_all [index=[,,...] | index=all] [] [] [outputs=]
[]"); const char* USAGE_SWEEP_BELOW("sweep_below [index=[,,...]] [] []
[]"); const char* USAGE_SWEEP_SINGLE("sweep_single [] [] [outputs=]
[]"); const char* USAGE_DONATE("donate [index=[,,...]] [] [] []"); @@ -3113,13 +3113,13 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("locked_sweep_all", boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1), tr(USAGE_LOCKED_SWEEP_ALL), - tr("Send all unlocked balance to an address and lock it for (max. 1000000). If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability.")); + tr("Send all unlocked balance to an address and lock it for (max. 1000000). If the parameter \"index=[,,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability.")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr(USAGE_SWEEP_ALL), - tr("Send all unlocked balance to an address. If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=\" is specified and N > 0, wallet splits the transaction into N even outputs.")); + tr("Send all unlocked balance to an address. If the parameter \"index=[,,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_below, _1), tr(USAGE_SWEEP_BELOW), @@ -6818,7 +6818,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector subaddr_indices; if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=") { - if (!parse_subaddress_indices(local_args[0], subaddr_indices)) + if (local_args[0] == "index=all") + { + for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i) + subaddr_indices.insert(i); + } + else if (!parse_subaddress_indices(local_args[0], subaddr_indices)) { print_usage(); return true; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..2a5cd1772 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1394,11 +1394,22 @@ namespace tools return false; } + std::set subaddr_indices; + if (req.subaddr_indices_all) + { + for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i) + subaddr_indices.insert(i); + } + else + { + subaddr_indices= req.subaddr_indices; + } + try { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); + std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..f833eaaff 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 16 +#define WALLET_RPC_VERSION_MINOR 17 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -749,6 +749,7 @@ namespace wallet_rpc std::string address; uint32_t account_index; std::set subaddr_indices; + bool subaddr_indices_all; uint32_t priority; uint64_t ring_size; uint64_t outputs; @@ -764,6 +765,7 @@ namespace wallet_rpc KV_SERIALIZE(address) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) + KV_SERIALIZE_OPT(subaddr_indices_all, false) KV_SERIALIZE(priority) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(outputs, (uint64_t)1) From 716012ca3a11969650c089fef1ca11ace871e4a5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 16 Dec 2019 14:49:13 +0000 Subject: [PATCH 140/469] rpc: drop the obsolete and wrong "unpruned size" log Since we now get pruned data in the first place, the "unpruned" data size will in fact be the pruned data size, leading to confusion --- src/rpc/core_rpc_server.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc93e7023..3d1d9901a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -539,7 +539,7 @@ namespace cryptonote CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK); - size_t pruned_size = 0, unpruned_size = 0, ntxes = 0; + size_t size = 0, ntxes = 0; res.blocks.reserve(bs.size()); res.output_indices.reserve(bs.size()); for(auto& bd: bs) @@ -547,8 +547,7 @@ namespace cryptonote res.blocks.resize(res.blocks.size()+1); res.blocks.back().pruned = req.prune; res.blocks.back().block = bd.first.first; - pruned_size += bd.first.first.size(); - unpruned_size += bd.first.first.size(); + size += bd.first.first.size(); res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); ntxes += bd.second.size(); res.output_indices.back().indices.reserve(1 + bd.second.size()); @@ -557,11 +556,10 @@ namespace cryptonote res.blocks.back().txs.reserve(bd.second.size()); for (std::vector>::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { - unpruned_size += i->second.size(); res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash}); i->second.clear(); i->second.shrink_to_fit(); - pruned_size += res.blocks.back().txs.back().blob.size(); + size += res.blocks.back().txs.back().blob.size(); } const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1); @@ -584,7 +582,7 @@ namespace cryptonote } } - MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, pruned size " << pruned_size << ", unpruned size " << unpruned_size); + MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size); res.status = CORE_RPC_STATUS_OK; return true; } From 352bd132545428be3c4fc99b5f3b2429cb55b563 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 16 Dec 2019 18:24:29 +0000 Subject: [PATCH 141/469] abstract_tcp_server2: guard against negative timeouts --- .../epee/include/net/abstract_tcp_server2.inl | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 128ff10aa..43ede3cc1 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -363,8 +363,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) } delay *= 0.5; - if (delay > 0) { - long int ms = (long int)(delay * 100); + long int ms = (long int)(delay * 100); + if (ms > 0) { reset_timer(boost::posix_time::milliseconds(ms + 1), true); boost::this_thread::sleep_for(boost::chrono::milliseconds(ms)); } @@ -721,7 +721,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::posix_time::milliseconds connection::get_timeout_from_bytes_read(size_t bytes) { boost::posix_time::milliseconds ms = (boost::posix_time::milliseconds)(unsigned)(bytes * TIMEOUT_EXTRA_MS_PER_BYTE); - ms += m_timer.expires_from_now(); + const auto cur = m_timer.expires_from_now().total_milliseconds(); + if (cur > 0) + ms += (boost::posix_time::milliseconds)cur; if (ms > get_default_timeout()) ms = get_default_timeout(); return ms; @@ -747,7 +749,12 @@ PRAGMA_WARNING_DISABLE_VS(4355) template void connection::reset_timer(boost::posix_time::milliseconds ms, bool add) { - MTRACE("Setting " << ms << " expiry"); + if (ms.total_milliseconds() < 0) + { + MWARNING("Ignoring negative timeout " << ms); + return; + } + MTRACE((add ? "Adding" : "Setting") << " " << ms << " expiry"); auto self = safe_shared_from_this(); if(!self) { @@ -760,7 +767,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) return; } if (add) - ms += m_timer.expires_from_now(); + { + const auto cur = m_timer.expires_from_now().total_milliseconds(); + if (cur > 0) + ms += (boost::posix_time::milliseconds)cur; + } m_timer.expires_from_now(ms); m_timer.async_wait([=](const boost::system::error_code& ec) { From f717d5936c522b6be978b9bb3f88d2750658fa08 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 14 Dec 2019 21:15:02 +0000 Subject: [PATCH 142/469] wallet2: guard against race with multiple decrypt_keys users If more than one thread wants to make sure of the spend secret key, then we decrypt on the first caller and reencrypt on the last caller, otherwise we could use an invalid secret key. --- src/wallet/wallet2.cpp | 7 +++++++ src/wallet/wallet2.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..fe267056c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1186,6 +1186,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), + m_decrypt_keys_lockers(0), m_unattended(unattended), m_devices_registered(false), m_device_last_key_image_sync(0), @@ -4343,12 +4344,18 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip void wallet2::encrypt_keys(const crypto::chacha_key &key) { + boost::lock_guard lock(m_decrypt_keys_lock); + if (--m_decrypt_keys_lockers) // another lock left ? + return; m_account.encrypt_keys(key); m_account.decrypt_viewkey(key); } void wallet2::decrypt_keys(const crypto::chacha_key &key) { + boost::lock_guard lock(m_decrypt_keys_lock); + if (m_decrypt_keys_lockers++) // already unlocked ? + return; m_account.encrypt_viewkey(key); m_account.decrypt_keys(key); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b6f72c7e1..aecfd6ba3 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1613,6 +1613,8 @@ private: crypto::chacha_key m_cache_key; boost::optional m_encrypt_keys_after_refresh; + boost::mutex m_decrypt_keys_lock; + unsigned int m_decrypt_keys_lockers; bool m_unattended; bool m_devices_registered; From cce4ea02ff9abbdb0b9af86e6b099172064fc7c2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 17 Dec 2019 19:21:23 +0000 Subject: [PATCH 143/469] README: fix translation link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b6b252e0..1fb42b35f 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Our researchers are available on IRC in [#monero-research-lab on Freenode](https - You can subscribe to an [announcement listserv](https://lists.getmonero.org) to get critical announcements from the Monero core team. The announcement list can be very helpful for knowing when software updates are needed. ## Translations -The CLI wallet is available in different languages. If you want to help translate it, see our self-hosted localization platform, Weblate, on [translate.getmonero.org](https://translate.getmonero.org/projects/CLI/). Every translation *must* be uploaded on the platform, pull requests directly editing the code in this repository will be closed. If you need help with Weblate, you can find a guide with screenshots [here](https://github.com/monero-ecosystem/monero-translations/blob/master/weblate.md). +The CLI wallet is available in different languages. If you want to help translate it, see our self-hosted localization platform, Weblate, on [translate.getmonero.org]( https://translate.getmonero.org/projects/monero/cli-wallet/). Every translation *must* be uploaded on the platform, pull requests directly editing the code in this repository will be closed. If you need help with Weblate, you can find a guide with screenshots [here](https://github.com/monero-ecosystem/monero-translations/blob/master/weblate.md).   If you need help/support/info about translations, contact the localization workgroup. You can find the complete list of contacts on the repository of the workgroup: [monero-translations](https://github.com/monero-ecosystem/monero-translations#contacts). From 21c3d42eb90ba5fa4891ceed00478623fb989f17 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 17 Dec 2019 20:26:29 +0000 Subject: [PATCH 144/469] p2p: drop the peerlist dump to TRACE It's spammy --- src/p2p/net_node.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 45bb10593..f270c788d 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1908,7 +1908,7 @@ namespace nodetool } LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size()); - LOG_DEBUG_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); + LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_); } //----------------------------------------------------------------------------------- From 0eac0c430ab853f10aae3b7953ef42a6405c3f42 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 18 Dec 2019 12:07:33 +0000 Subject: [PATCH 145/469] depends: set several missing build tags --- contrib/depends/toolchain.cmake.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index c56f3eb2e..2634423ab 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -136,11 +136,21 @@ endif() if(ARCHITECTURE STREQUAL "i686") SET(ARCH_ID "i386") if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BUILD_TAG "linux-x86") SET(LINUX_32 ON) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(BUILD_TAG "win-x32") endif() endif() if(ARCHITECTURE STREQUAL "x86_64") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BUILD_TAG "linux-x64") + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(BUILD_TAG "freebsd-x64") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(BUILD_TAG "win-x64") + endif() SET(ARCH_ID "x86_64") endif() From f9b48b91c6e90c8cdae8fb04f22e31aa2d910206 Mon Sep 17 00:00:00 2001 From: Alexander Blair Date: Wed, 18 Dec 2019 22:06:49 -0800 Subject: [PATCH 146/469] GPGKey: Add Snipa Adds GPG key for Alexander Blair (Snipa) --- utils/gpg_keys/snipa.asc | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 utils/gpg_keys/snipa.asc diff --git a/utils/gpg_keys/snipa.asc b/utils/gpg_keys/snipa.asc new file mode 100644 index 000000000..caee18fda --- /dev/null +++ b/utils/gpg_keys/snipa.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +nDKy8l5Mnmigd0+k1Mx+Lw== +=ACfD +-----END PGP PUBLIC KEY BLOCK----- From 81494e3ffa0cdacb85f4e6719ba3dae2b4cc1b39 Mon Sep 17 00:00:00 2001 From: kozyilmaz Date: Thu, 19 Dec 2019 14:18:51 +0100 Subject: [PATCH 147/469] depends: empty spaces in PATH variable cause build failure --- contrib/depends/funcs.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/depends/funcs.mk b/contrib/depends/funcs.mk index 355ae07eb..2d6b37190 100644 --- a/contrib/depends/funcs.mk +++ b/contrib/depends/funcs.mk @@ -135,9 +135,9 @@ $(1)_config_env+=$($(1)_config_env_$(host_arch)_$(host_os)) $($(1)_config_env_$( $(1)_config_env+=PKG_CONFIG_LIBDIR=$($($(1)_type)_prefix)/lib/pkgconfig $(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig -$(1)_config_env+=PATH=$(build_prefix)/bin:$(PATH) -$(1)_build_env+=PATH=$(build_prefix)/bin:$(PATH) -$(1)_stage_env+=PATH=$(build_prefix)/bin:$(PATH) +$(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)" $(1)_autoconf=./configure --host=$($($(1)_type)_host) --disable-dependency-tracking --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)" ifneq ($($(1)_nm),) From 69336931791298bef3af560156bc435ef974e576 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 20 Dec 2019 14:16:06 +0000 Subject: [PATCH 148/469] rpc: fill miner_tx_hash again It was removed to save duplicated generation time, but it can be copied from another instance instead --- src/rpc/core_rpc_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc93e7023..85ef50a57 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2199,6 +2199,7 @@ namespace cryptonote error_resp.message = "Internal error: can't produce valid response."; return false; } + res.miner_tx_hash = res.block_header.miner_tx_hash; for (size_t n = 0; n < blk.tx_hashes.size(); ++n) { res.tx_hashes.push_back(epee::string_tools::pod_to_hex(blk.tx_hashes[n])); From a6c24412e522a57b26ad923ca0fef132a8321720 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Dec 2019 03:09:00 +0000 Subject: [PATCH 149/469] wallet: fix exceptions getting the hash of a pruned tx --- src/simplewallet/simplewallet.cpp | 4 ++-- src/wallet/wallet2.cpp | 14 +++++++------- src/wallet/wallet2.h | 4 ++-- src/wallet/wallet_rpc_server.cpp | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a35ee40ae..d7d127520 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8331,7 +8331,7 @@ bool simple_wallet::get_transfers(std::vector& local_args, std::vec m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - std::vector> process_txs; + std::vector> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); @@ -9999,7 +9999,7 @@ bool simple_wallet::show_transfer(const std::vector &args) try { - std::vector> process_txs; + std::vector> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..3aa602b7c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2847,7 +2847,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector &tx_hashe } //---------------------------------------------------------------------------------------------------- -void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed) +void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed) { MTRACE("update_pool_state start"); @@ -3037,7 +3037,7 @@ void wallet2::update_pool_state(std::vector &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen)); + process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen)); } else { @@ -3068,14 +3068,14 @@ void wallet2::update_pool_state(std::vector> &txs) +void wallet2::process_pool_state(const std::vector> &txs) { const time_t now = time(NULL); for (const auto &e: txs) { - const cryptonote::transaction &tx = e.first; - const bool double_spend_seen = e.second; - const crypto::hash tx_hash = get_transaction_hash(tx); + const cryptonote::transaction &tx = std::get<0>(e); + const crypto::hash &tx_hash = std::get<1>(e); + const bool double_spend_seen = std::get<2>(e); process_new_transaction(tx_hash, tx, std::vector(), 0, 0, now, false, true, double_spend_seen, {}); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) @@ -3294,7 +3294,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // since that might cause a password prompt, which would introduce a data // leak allowing a passive adversary with traffic analysis capability to // infer when we get an incoming output - std::vector> process_pool_txs; + std::vector> process_pool_txs; update_pool_state(process_pool_txs, true); bool first = true, last = false; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b6f72c7e1..c232b0fc6 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1221,8 +1221,8 @@ private: bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; - void update_pool_state(std::vector> &process_txs, bool refreshed = false); - void process_pool_state(const std::vector> &txs); + void update_pool_state(std::vector> &process_txs, bool refreshed = false); + void process_pool_state(const std::vector> &txs); void remove_obsolete_pool_txs(const std::vector &tx_hashes); std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..46db624d0 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2438,7 +2438,7 @@ namespace tools if (req.pool) { - std::vector> process_txs; + std::vector> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); @@ -2521,7 +2521,7 @@ namespace tools } } - std::vector> process_txs; + std::vector> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); From d64e5aa71936d86f91d3f45a9da0468f97078409 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 Oct 2019 14:09:19 +0000 Subject: [PATCH 150/469] wallet: allow message sign/verify for subaddresses --- src/simplewallet/simplewallet.cpp | 23 +++++++++++---- src/wallet/wallet2.cpp | 18 ++++++++++-- src/wallet/wallet2.h | 2 +- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 6 +++- tests/functional_tests/sign_message.py | 31 +++++++++++++++----- utils/python-rpc/framework/wallet.py | 4 ++- 7 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 03693a57c..23452b0ca 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -214,7 +214,7 @@ namespace const char* USAGE_GET_TX_NOTE("get_tx_note "); const char* USAGE_GET_DESCRIPTION("get_description"); const char* USAGE_SET_DESCRIPTION("set_description [free text note]"); - const char* USAGE_SIGN("sign "); + const char* USAGE_SIGN("sign [,] "); const char* USAGE_VERIFY("verify
"); const char* USAGE_EXPORT_KEY_IMAGES("export_key_images [all] "); const char* USAGE_IMPORT_KEY_IMAGES("import_key_images "); @@ -3356,7 +3356,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sign, _1), tr(USAGE_SIGN), - tr("Sign the contents of a file.")); + tr("Sign the contents of a file with the given subaddress (or the main address if not specified)")); m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::on_command, this, &simple_wallet::verify, _1), tr(USAGE_VERIFY), @@ -9593,7 +9593,7 @@ bool simple_wallet::sign(const std::vector &args) fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if (args.size() != 1) + if (args.size() != 1 && args.size() != 2) { PRINT_USAGE(USAGE_SIGN); return true; @@ -9609,7 +9609,20 @@ bool simple_wallet::sign(const std::vector &args) return true; } - std::string filename = args[0]; + subaddress_index index{0, 0}; + if (args.size() == 2) + { + unsigned int a, b; + if (sscanf(args[0].c_str(), "%u,%u", &a, &b) != 2) + { + fail_msg_writer() << tr("Invalid subaddress index format"); + return true; + } + index.major = a; + index.minor = b; + } + + const std::string &filename = args.back(); std::string data; bool r = m_wallet->load_from_file(filename, data); if (!r) @@ -9620,7 +9633,7 @@ bool simple_wallet::sign(const std::vector &args) SCOPED_WALLET_UNLOCK(); - std::string signature = m_wallet->sign(data); + std::string signature = m_wallet->sign(data, index); success_msg_writer() << signature; return true; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9b3e7e8b4..48b8bbf7e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -11777,13 +11777,27 @@ void wallet2::set_account_tag_description(const std::string& tag, const std::str m_account_tags.first[tag] = description; } -std::string wallet2::sign(const std::string &data) const +std::string wallet2::sign(const std::string &data, cryptonote::subaddress_index index) const { crypto::hash hash; crypto::cn_fast_hash(data.data(), data.size(), hash); const cryptonote::account_keys &keys = m_account.get_keys(); crypto::signature signature; - crypto::generate_signature(hash, keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key, signature); + crypto::secret_key skey; + crypto::public_key pkey; + if (index.is_zero()) + { + skey = keys.m_spend_secret_key; + pkey = keys.m_account_address.m_spend_public_key; + } + else + { + skey = keys.m_spend_secret_key; + crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(keys.m_view_secret_key, index); + sc_add((unsigned char*)&skey, (unsigned char*)&m, (unsigned char*)&skey); + secret_key_to_public_key(skey, pkey); + } + crypto::generate_signature(hash, pkey, skey, signature); return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature))); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 640565a4e..38e3afd69 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1182,7 +1182,7 @@ private: */ void set_account_tag_description(const std::string& tag, const std::string& description); - std::string sign(const std::string &data) const; + std::string sign(const std::string &data, cryptonote::subaddress_index index = {0, 0}) const; bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const; /*! diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ec21b2897..b5b572bc8 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1943,7 +1943,7 @@ namespace tools return false; } - res.signature = m_wallet->sign(req.data); + res.signature = m_wallet->sign(req.data, {req.account_index, req.address_index}); return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..c823417eb 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1591,9 +1591,13 @@ namespace wallet_rpc struct request_t { std::string data; + uint32_t account_index; + uint32_t address_index; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(data); + KV_SERIALIZE(data) + KV_SERIALIZE_OPT(account_index, 0u) + KV_SERIALIZE_OPT(address_index, 0u) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py index de8f0cee2..9dd70f8bc 100755 --- a/tests/functional_tests/sign_message.py +++ b/tests/functional_tests/sign_message.py @@ -43,7 +43,8 @@ from framework.wallet import Wallet class MessageSigningTest(): def run_test(self): self.create() - self.check_signing() + self.check_signing(False) + self.check_signing(True) def create(self): print('Creating wallets') @@ -65,20 +66,34 @@ class MessageSigningTest(): assert res.address == self.address[i] assert res.seed == seeds[i] - def check_signing(self): - print('Signing/verifing messages') + def check_signing(self, subaddress): + print('Signing/verifing messages with ' + ('subaddress' if subaddress else 'standard address')) messages = ['foo', ''] + if subaddress: + address = [] + for i in range(2): + res = self.wallet[i].create_account() + if i == 0: + account_index = res.account_index + res = self.wallet[i].create_address(account_index = account_index) + if i == 0: + address_index = res.address_index + address.append(res.address) + else: + address = [self.address[0], self.address[1]] + account_index = 0 + address_index = 0 for message in messages: - res = self.wallet[0].sign(message) + res = self.wallet[0].sign(message, account_index = account_index, address_index = address_index) signature = res.signature for i in range(2): - res = self.wallet[i].verify(message, self.address[0], signature) + res = self.wallet[i].verify(message, address[0], signature) assert res.good - res = self.wallet[i].verify('different', self.address[0], signature) + res = self.wallet[i].verify('different', address[0], signature) assert not res.good - res = self.wallet[i].verify(message, self.address[1], signature) + res = self.wallet[i].verify(message, address[1], signature) assert not res.good - res = self.wallet[i].verify(message, self.address[0], signature + 'x') + res = self.wallet[i].verify(message, address[0], signature + 'x') assert not res.good if __name__ == '__main__': diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index 6a3fabdc9..6371a253c 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -705,11 +705,13 @@ class Wallet(object): } return self.rpc.send_json_rpc_request(check_reserve_proof) - def sign(self, data): + def sign(self, data, account_index = 0, address_index = 0): sign = { 'method': 'sign', 'params' : { 'data': data, + 'account_index': account_index, + 'address_index': address_index, }, 'jsonrpc': '2.0', 'id': '0' From 36bdf40288825f18ab779dd711fc364a71c4c168 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 3 Jan 2020 17:56:21 +0000 Subject: [PATCH 151/469] p2p: fix adding wrong indices to the filtered peer list --- src/p2p/net_node.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 45bb10593..a697e2e44 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1432,10 +1432,10 @@ namespace nodetool std::deque filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits::max(); - size_t idx = 0, skipped = 0; for (int step = 0; step < 2; ++step) { bool skip_duplicate_class_B = step == 0; + size_t idx = 0, skipped = 0; zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ if (filtered.size() >= limit) return false; From 6772ce706223798e823ce91145069f110374a069 Mon Sep 17 00:00:00 2001 From: rating89us <45968869+rating89us@users.noreply.github.com> Date: Sun, 5 Jan 2020 00:21:57 -0300 Subject: [PATCH 152/469] 10 block time is for incoming outputs, not transactions 10 block lock time is for incoming outputs and not only incoming transactions (outgoing transaction has an incoming change output that is also locked for 10 blocks) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b6b252e0..ca4165fb7 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ Dates are provided in the format YYYY-MM-DD. | 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required | 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format | 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format -| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming transactions +| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs | XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX | X's indicate that these details have not been determined as of commit date. From c11e64fc4cd68fac2c491747c07073ca8714f7bf Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sun, 29 Dec 2019 01:38:19 +0100 Subject: [PATCH 153/469] Gitian: Change lxc ip link The ip link currently listed in the README no longer works on the newest version of lxc shipped with ubuntu 18.04, this commit corrects this. --- contrib/gitian/README.md | 6 +++--- contrib/gitian/gitian-build.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 7e3502bcf..c09786c36 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -62,7 +62,7 @@ echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc # make /etc/rc.local script that sets up bridge between guest and host echo '#!/bin/sh -e' > /etc/rc.local echo 'brctl addbr br0' >> /etc/rc.local -echo 'ip addr add 10.0.3.1/24 broadcast 10.0.3.255 dev br0' >> /etc/rc.local +echo 'ip addr add 10.0.2.2/24 broadcast 10.0.2.255 dev br0' >> /etc/rc.local echo 'ip link set br0 up' >> /etc/rc.local echo 'firewall-cmd --zone=trusted --add-interface=br0' >> /etc/rc.local echo 'exit 0' >> /etc/rc.local @@ -70,8 +70,8 @@ chmod +x /etc/rc.local # make sure that USE_LXC is always set when logging in as gitianuser, # and configure LXC IP addresses echo 'export USE_LXC=1' >> /home/gitianuser/.profile -echo 'export GITIAN_HOST_IP=10.0.3.1' >> /home/gitianuser/.profile -echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/gitianuser/.profile +echo 'export GITIAN_HOST_IP=10.0.2.2' >> /home/gitianuser/.profile +echo 'export LXC_GUEST_IP=10.0.2.5' >> /home/gitianuser/.profile reboot ``` diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 64eb218bb..f8927c83b 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -157,9 +157,9 @@ def main(): elif not args.kvm: os.environ['USE_LXC'] = '1' if not 'GITIAN_HOST_IP' in os.environ.keys(): - os.environ['GITIAN_HOST_IP'] = '10.0.3.1' + os.environ['GITIAN_HOST_IP'] = '10.0.2.2' if not 'LXC_GUEST_IP' in os.environ.keys(): - os.environ['LXC_GUEST_IP'] = '10.0.3.5' + os.environ['LXC_GUEST_IP'] = '10.0.2.5' # Disable MacOS build if no SDK found args.nomac = False From a84131cea3b600773d1a108421a12c0adfc476cc Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 7 Jan 2020 14:38:31 +0000 Subject: [PATCH 154/469] p2p: fix off by one adding fallback peers The code would ignore the first one to be added --- src/p2p/net_node.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 45bb10593..fd2fcde55 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1555,14 +1555,14 @@ namespace nodetool if (!m_fallback_seed_nodes_added) { MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); - current_index = m_seed_nodes.size(); + current_index = m_seed_nodes.size() - 1; for (const auto &peer: get_seed_nodes(m_nettype)) { MDEBUG("Fallback seed node: " << peer); append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } m_fallback_seed_nodes_added = true; - if (current_index == m_seed_nodes.size()) + if (current_index == m_seed_nodes.size() - 1) { MWARNING("No fallback seeds, continuing without seeds"); break; From 02224e714d7e3ca0d07416b5882ae0cfc92900f2 Mon Sep 17 00:00:00 2001 From: UkoeHB <37489173+UkoeHB@users.noreply.github.com> Date: Wed, 8 Jan 2020 05:21:43 -0600 Subject: [PATCH 155/469] Fix check_fee() discrepancy. M100 = max{300kb, min{100block_median, m_long_term_effective_median_block_weight}} not M100 = max{300kb, m_long_term_effective_median_block_weight} Fix base reward in get_dynamic_base_fee_estimate(). get_dynamic_base_fee_estimate() should match check_fee() Fee is calculated based on block reward, and the reward penalty takes into account 0.5*max_block_weight (both before and after HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY). Moved median calculation according to best practice of 'keep definitions close to where they are used'. --- src/cryptonote_core/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7357e44d0..f162f4608 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3409,7 +3409,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const if (version >= HF_VERSION_PER_BYTE_FEE) { const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version); + uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min(median, m_long_term_effective_median_block_weight) : median, version); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); needed_fee = tx_weight * fee_per_byte; // quantize fee up to 8 decimals @@ -3467,7 +3467,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0; uint64_t base_reward; - if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) + if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version)) { MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound"); base_reward = BLOCK_REWARD_OVERESTIMATE; From dbcfae6ffe48fca9349c4a14cef26df9742076c0 Mon Sep 17 00:00:00 2001 From: selsta Date: Thu, 9 Jan 2020 18:23:01 +0100 Subject: [PATCH 156/469] README: remove unmaintained build status --- README.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/README.md b/README.md index 7b6b252e0..856ecdf37 100644 --- a/README.md +++ b/README.md @@ -59,26 +59,6 @@ The CLI wallet is available in different languages. If you want to help translat If you need help/support/info about translations, contact the localization workgroup. You can find the complete list of contacts on the repository of the workgroup: [monero-translations](https://github.com/monero-ecosystem/monero-translations#contacts). -## Build Status - -### IMPORTANT - -These builds are of the master branch, which is used for active development and can be either unstable or incompatible with release software. Please compile release branches. - -| Operating System | Processor | Status | -| --------------------- | -------- |--------| -| Ubuntu 16.04 | i686 | [![Ubuntu 16.04 i686](https://build.getmonero.org/png?builder=monero-static-ubuntu-i686)](https://build.getmonero.org/builders/monero-static-ubuntu-i686) -| Ubuntu 16.04 | amd64 | [![Ubuntu 16.04 amd64](https://build.getmonero.org/png?builder=monero-static-ubuntu-amd64)](https://build.getmonero.org/builders/monero-static-ubuntu-amd64) -| Ubuntu 16.04 | armv7 | [![Ubuntu 16.04 armv7](https://build.getmonero.org/png?builder=monero-static-ubuntu-arm7)](https://build.getmonero.org/builders/monero-static-ubuntu-arm7) -| Debian Stable | armv8 | [![Debian armv8](https://build.getmonero.org/png?builder=monero-static-debian-armv8)](https://build.getmonero.org/builders/monero-static-debian-armv8) -| macOS 10.11 | amd64 | [![macOS 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11) -| macOS 10.12 | amd64 | [![macOS 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12) -| macOS 10.13 | amd64 | [![macOS 10.13 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.13)](https://build.getmonero.org/builders/monero-static-osx-10.13) -| FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64) -| DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64) -| Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32) -| Windows (MSYS2/MinGW) | amd64 | [![Windows (MSYS2/MinGW) amd64](https://build.getmonero.org/png?builder=monero-static-win64)](https://build.getmonero.org/builders/monero-static-win64) - ## Coverage | Type | Status | From f507a43a239358b1645f383f6974f0862de2999b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Jan 2020 12:48:57 +0000 Subject: [PATCH 157/469] wallet2: do not remove known rings when a tx fails Even if it fails, the ring composition is known to a potential adversary, and so we should reuse the same ring next time --- src/wallet/wallet2.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..58a531557 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2907,7 +2907,6 @@ void wallet2::update_pool_state(std::vectorsecond.m_state = wallet2::unconfirmed_transfer_details::failed; // the inputs aren't spent anymore, since the tx failed - remove_rings(pit->second.m_tx); for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini) { if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) From 7b88208722910a9c7f83676afa867c7896d1449d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Jan 2020 12:54:43 +0000 Subject: [PATCH 158/469] simplewallet: reword mixin in user message in terms of ring size --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a35ee40ae..30cf104ac 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1690,7 +1690,7 @@ bool simple_wallet::print_ring(const std::vector &args) rings.push_back({key_image, ring}); else if (!m_wallet->get_rings(txid, rings)) { - fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1"); return true; } From 0349347e6d2805e632d5972a50ae418296e9c479 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Jan 2020 17:29:36 +0000 Subject: [PATCH 159/469] ringdb: use a different iv for key and data in rings table This is technically a record encrypted in two pieces, so the iv needs to be different. Some backward compatibility is added to read data written by existing code, but new data is written with the new code. --- src/wallet/ringdb.cpp | 55 +++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index b7efdd75c..5e88ea788 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -39,6 +39,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb" +#define V1TAG ((uint64_t)798237759845202) + static const char zerokey[8] = {0}; static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey }; @@ -63,15 +65,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b) return va < vb ? -1 : va > vb; } -static std::string compress_ring(const std::vector &ring) +static std::string compress_ring(const std::vector &ring, uint64_t tag) { std::string s; + s += tools::get_varint_data(tag); for (uint64_t out: ring) s += tools::get_varint_data(out); return s; } -static std::vector decompress_ring(const std::string &s) +static std::vector decompress_ring(const std::string &s, uint64_t tag) { std::vector ring; int read = 0; @@ -81,6 +84,13 @@ static std::vector decompress_ring(const std::string &s) std::string tmp(i, s.cend()); read = tools::read_varint(tmp.begin(), tmp.end(), out); THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring"); + if (tag) + { + if (tag != out) + return {}; + tag = 0; + continue; + } ring.push_back(out); } return ring; @@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename) return filename.string(); } -static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key) +static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { static const char salt[] = "ringdsb"; - uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)]; + uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt) + sizeof(field)]; memcpy(buffer, &key_image, sizeof(key_image)); memcpy(buffer + sizeof(key_image), &key, sizeof(key)); memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt)); + memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(salt), &field, sizeof(field)); crypto::hash hash; - crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data); + // if field is 0, backward compat mode: hash without the field + crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data); static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes"); crypto::chacha_iv iv; memcpy(&iv, &hash, CHACHA_IV_SIZE); return iv; } -static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string ciphertext; ciphertext.resize(plaintext.size() + sizeof(iv)); crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); @@ -119,14 +131,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image return ciphertext; } -static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key); + return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field); } -static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string plaintext; THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text"); plaintext.resize(ciphertext.size() - sizeof(iv)); @@ -137,11 +149,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector &relative_ring, const crypto::chacha_key &chacha_key) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); - std::string compressed_ring = compress_ring(relative_ring); - std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key); + std::string compressed_ring = compress_ring(relative_ring, V1TAG); + std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1); data.mv_size = data_ciphertext.size(); data.mv_data = (void*)data_ciphertext.c_str(); int dbr = mdb_put(txn, dbi, &key, &data, 0); @@ -297,7 +309,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto for (const crypto::key_image &key_image: key_images) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); @@ -349,7 +361,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im tx_active = true; MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); dbr = mdb_get(txn, dbi_rings, &key, &data); @@ -358,8 +370,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return false; THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); - std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key); - outs = decompress_ring(data_plaintext); + bool try_v0 = false; + std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1); + try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; } + catch(...) { try_v0 = true; } + if (try_v0) + { + data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0); + outs = decompress_ring(data_plaintext, 0); + } MDEBUG("Found ring for key image " << key_image << ":"); MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); outs = cryptonote::relative_output_offsets_to_absolute(outs); From 664720747f7a25c13f5c5daad7583771ea9a9056 Mon Sep 17 00:00:00 2001 From: rbrunner7 Date: Sun, 5 Jan 2020 16:38:39 +0100 Subject: [PATCH 160/469] Daemon: Guard against reporting "synchronized" too early The added condition "hshd.current_height >= target" guards against reporting "synchronized" too early in the special situation that the very first peer sending us data is synced to a lower height than ourselves. --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index e20934a25..80e28e9c6 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -346,7 +346,7 @@ namespace cryptonote if(m_core.have_block(hshd.top_id)) { context.m_state = cryptonote_connection_context::state_normal; - if(is_inital && target == m_core.get_current_blockchain_height()) + if(is_inital && hshd.current_height >= target && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); return true; } From 019c1dc0bb5fc90ecb6480a0541c074a292bfe18 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Fri, 10 Jan 2020 19:16:24 +0100 Subject: [PATCH 161/469] gitian-build.py: Fixing check for docker command. --- contrib/gitian/gitian-build.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 64eb218bb..713b0dce5 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -36,8 +36,11 @@ def setup(): os.chdir('..') make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64'] if args.docker: - if not subprocess.call(['docker', '--help'], shell=False, stdout=subprocess.DEVNULL): - print("Please install docker first manually") + try: + subprocess.check_output(['docker', '--help']) + except: + print("ERROR: Could not find 'docker' command. Ensure this is in your PATH.") + sys.exit(1) make_image_prog += ['--docker'] elif not args.kvm: make_image_prog += ['--lxc'] From 096a9dbdf958d23f74d132247143b8b8d3cb6dcd Mon Sep 17 00:00:00 2001 From: Tadeas Moravec Date: Thu, 9 Jan 2020 10:01:56 +0100 Subject: [PATCH 162/469] Wallet: Distingush amounts for a single subaddress Adding a new `amounts` field ot the output of `get_transfers` RPC method. This field specifies individual payments made to a single subaddress in a single transaction, e.g., made by this command: transfer --- src/wallet/wallet2.cpp | 28 +++++++++++++++++++- src/wallet/wallet2.h | 7 ++++- src/wallet/wallet_rpc_server.cpp | 2 ++ src/wallet/wallet_rpc_server_commands_defs.h | 3 +++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..a850f6bfb 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1842,7 +1842,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // (that is, the prunable stuff may or may not be included) if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); - std::unordered_map tx_money_got_in_outs; // per receiving subaddress index + + // per receiving subaddress index + std::unordered_map tx_money_got_in_outs; + std::unordered_map tx_amounts_individual_outs; + crypto::public_key tx_pub_key = null_pkey; bool notify = false; @@ -1971,6 +1975,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -1994,6 +2002,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -2010,6 +2022,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -2118,6 +2134,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount, error::wallet_internal_error, "Unexpected values of new and old outputs"); tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount; + + amounts_container& tx_amounts_this_out = tx_amounts_individual_outs[tx_scan_info[o].received->index]; // Only for readability on the following lines + auto amount_iterator = std::find(tx_amounts_this_out.begin(), tx_amounts_this_out.end(), tx_scan_info[o].amount); + THROW_WALLET_EXCEPTION_IF(amount_iterator == tx_amounts_this_out.end(), + error::wallet_internal_error, "Unexpected values of new and old outputs"); + tx_amounts_this_out.erase(amount_iterator); } else { @@ -2183,6 +2205,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } } + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs.size() != tx_amounts_individual_outs.size(), error::wallet_internal_error, "Inconsistent size of output arrays"); + uint64_t tx_money_spent_in_ins = 0; // The line below is equivalent to "boost::optional subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679 @@ -2286,6 +2310,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (subaddr_account && i->first.major == *subaddr_account) { sub_change += i->second; + tx_amounts_individual_outs.erase(i->first); i = tx_money_got_in_outs.erase(i); } else @@ -2363,6 +2388,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_tx_hash = txid; payment.m_fee = fee; payment.m_amount = i.second; + payment.m_amounts = tx_amounts_individual_outs[i.first]; payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; payment.m_timestamp = ts; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b6f72c7e1..f643c0d1a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -360,10 +360,12 @@ private: END_SERIALIZE() }; + typedef std::vector amounts_container; struct payment_details { crypto::hash m_tx_hash; uint64_t m_amount; + amounts_container m_amounts; uint64_t m_fee; uint64_t m_block_height; uint64_t m_unlock_time; @@ -1628,7 +1630,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) -BOOST_CLASS_VERSION(tools::wallet2::payment_details, 4) +BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5) BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6) @@ -1939,6 +1941,9 @@ namespace boost return; } a & x.m_coinbase; + if (ver < 5) + return; + a & x.m_amounts; } template diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..ae81e211e 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -326,6 +326,7 @@ namespace tools entry.height = pd.m_block_height; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.amounts = pd.m_amounts; entry.unlock_time = pd.m_unlock_time; entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); entry.fee = pd.m_fee; @@ -408,6 +409,7 @@ namespace tools entry.height = 0; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.amounts = pd.m_amounts; entry.unlock_time = pd.m_unlock_time; entry.locked = true; entry.fee = pd.m_fee; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..9b7d72279 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1354,6 +1354,7 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init response; }; + typedef std::vector amounts_container; struct transfer_entry { std::string txid; @@ -1361,6 +1362,7 @@ namespace wallet_rpc uint64_t height; uint64_t timestamp; uint64_t amount; + amounts_container amounts; uint64_t fee; std::string note; std::list destinations; @@ -1380,6 +1382,7 @@ namespace wallet_rpc KV_SERIALIZE(height); KV_SERIALIZE(timestamp); KV_SERIALIZE(amount); + KV_SERIALIZE(amounts); KV_SERIALIZE(fee); KV_SERIALIZE(note); KV_SERIALIZE(destinations); From 2aa80b1d287a73826edc3fb3a6e7a2ad486e88f9 Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 30 Dec 2019 09:47:16 +0000 Subject: [PATCH 163/469] build: autodetect MSYS2 install path, support non-standard location --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d3d684c5b..2c7bd20a9 100644 --- a/Makefile +++ b/Makefile @@ -81,11 +81,11 @@ debug-static-all: debug-static-win64: mkdir -p $(builddir)/debug - cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE) + cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE) debug-static-win32: mkdir -p $(builddir)/debug - cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE) + cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE) cmake-release: mkdir -p $(builddir)/release @@ -152,11 +152,11 @@ release-static-linux-i686: release-static-win64: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE) + cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE) release-static-win32: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE) + cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE) fuzz: mkdir -p $(builddir)/fuzz From ffe7165ebfbb805b3bba57de8cb3f14d0b3411a0 Mon Sep 17 00:00:00 2001 From: xiphon Date: Fri, 10 Jan 2020 15:25:29 +0000 Subject: [PATCH 164/469] wallet: reroll fake outs selection on local tx_sanity_check failure --- src/cryptonote_core/tx_sanity_check.cpp | 16 +++++---- src/cryptonote_core/tx_sanity_check.h | 6 ++-- src/rpc/core_rpc_server.cpp | 2 +- src/wallet/wallet2.cpp | 46 +++++++++++++++++++++++-- src/wallet/wallet2.h | 1 + 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index 03cbb5c26..e99982def 100644 --- a/src/cryptonote_core/tx_sanity_check.cpp +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -28,7 +28,7 @@ #include #include -#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "blockchain.h" #include "tx_sanity_check.h" @@ -39,7 +39,7 @@ namespace cryptonote { -bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob) +bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available) { cryptonote::transaction tx; @@ -70,14 +70,18 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob n_indices += in_to_key.key_offsets.size(); } + return tx_sanity_check(rct_indices, n_indices, rct_outs_available); +} + +bool tx_sanity_check(const std::set &rct_indices, size_t n_indices, uint64_t rct_outs_available) +{ if (n_indices <= 10) { MDEBUG("n_indices is only " << n_indices << ", not checking"); return true; } - uint64_t n_available = blockchain.get_num_mature_outputs(0); - if (n_available < 10000) + if (rct_outs_available < 10000) return true; if (rct_indices.size() < n_indices * 8 / 10) @@ -88,9 +92,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob std::vector offsets(rct_indices.begin(), rct_indices.end()); uint64_t median = epee::misc_utils::median(offsets); - if (median < n_available * 6 / 10) + if (median < rct_outs_available * 6 / 10) { - MERROR("median offset index is too low (median is " << median << " out of total " << n_available << "offsets). Transactions should contain a higher fraction of recent outputs."); + MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs."); return false; } diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h index c12d1b0b1..4a469462f 100644 --- a/src/cryptonote_core/tx_sanity_check.h +++ b/src/cryptonote_core/tx_sanity_check.h @@ -26,11 +26,11 @@ // 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. +#include #include "cryptonote_basic/blobdatatype.h" namespace cryptonote { - class Blockchain; - - bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob); + bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available); + bool tx_sanity_check(const std::set &rct_indices, size_t n_indices, uint64_t rct_outs_available); } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc93e7023..fe4f919ce 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1087,7 +1087,7 @@ namespace cryptonote return true; } - if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob)) + if (req.do_sanity_checks && !cryptonote::tx_sanity_check(tx_blob, m_core.get_blockchain_storage().get_num_mature_outputs(0))) { res.status = "Failed"; res.reason = "Sanity check failed"; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..5e0c89aba 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -44,6 +44,7 @@ using namespace epee; #include "cryptonote_config.h" +#include "cryptonote_core/tx_sanity_check.h" #include "wallet_rpc_helpers.h" #include "wallet2.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -7733,7 +7734,49 @@ void wallet2::light_wallet_get_outs(std::vector, size_t> outs_unique(const std::vector> &outs) +{ + std::set unique; + size_t total = 0; + + for (const auto &it : outs) + { + for (const auto &out : it) + { + const uint64_t global_index = std::get<0>(out); + unique.insert(global_index); + } + total += it.size(); + } + + return std::make_pair(std::move(unique), total); +} + void wallet2::get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count) +{ + std::vector rct_offsets; + for (size_t attempts = 3; attempts > 0; --attempts) + { + get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets); + + const auto unique = outs_unique(outs); + if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back())) + { + return; + } + + std::vector key_images; + key_images.reserve(selected_transfers.size()); + std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) { + key_images.push_back(m_transfers[index].m_key_image); + }); + unset_ring(key_images); + } + + THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed")); +} + +void wallet2::get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count, std::vector &rct_offsets) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -7755,7 +7798,6 @@ void wallet2::get_outs(std::vector> // if we have at least one rct out, get the distribution, or fall back to the previous system uint64_t rct_start_height; - std::vector rct_offsets; bool has_rct = false; uint64_t max_rct_index = 0; for (size_t idx: selected_transfers) @@ -7764,7 +7806,7 @@ void wallet2::get_outs(std::vector> has_rct = true; max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index); } - const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); + const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets)); if (has_rct_distribution) { // check we're clear enough of rct start, to avoid corner cases below diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b6f72c7e1..23f99b1dd 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1435,6 +1435,7 @@ private: bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(size_t idx, bool strict = true) const; void get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count, std::vector &rct_offsets); bool tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector &unused_transfers_indices, const std::vector &unused_dust_indices) const; std::vector get_only_rct(const std::vector &unused_dust_indices, const std::vector &unused_transfers_indices) const; From 50e59cfadad6215c74bbba9524b72ea1bf783641 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Jan 2020 19:56:47 +0000 Subject: [PATCH 165/469] wallet2: reject zero keys in json input Because the user might do this for reasons unknown. Values beyond l-1 will be reduced, so are accepted. Reported by who-biz. --- src/wallet/wallet2.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4d2ec5103..612340c7f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -591,6 +591,8 @@ std::pair, tools::password_container> generate_f } viewkey = *reinterpret_cast(viewkey_data.data()); crypto::public_key pkey; + if (viewkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(viewkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } @@ -607,6 +609,8 @@ std::pair, tools::password_container> generate_f } spendkey = *reinterpret_cast(spendkey_data.data()); crypto::public_key pkey; + if (spendkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(spendkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } From 957ae8879552c625131dd00e9b22b8ceb32f0302 Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 30 Dec 2019 15:11:58 +0100 Subject: [PATCH 166/469] workflows: add build and test workflow --- .github/workflows/build.yml | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..50297e146 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,62 @@ +name: continuous-integration/gh-actions/cli + +on: [push, pull_request] + +jobs: + build-macos: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: recursive + - name: update brew and install dependencies + run: brew update && brew install boost hidapi zmq libpgm unbound libsodium miniupnpc ldns expat libunwind-headers protobuf + - name: build + run: make -j3 + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: recursive + - uses: numworks/setup-msys2@v1 + - name: update pacman + run: msys2do pacman -Syu --noconfirm + - name: install monero dependencies + run: msys2do pacman -S --noconfirm mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git + - name: build + run: msys2do make release-static-win64 -j3 + + build-ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: recursive + - name: remove bundled boost + run: sudo rm -rf /usr/local/share/boost + - name: update apt + run: sudo apt update + - name: install monero dependencies + run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev + - name: build + run: make -j3 + + test-ubuntu: + needs: build-ubuntu + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: recursive + - name: remove bundled boost + run: sudo rm -rf /usr/local/share/boost + - name: update apt + run: sudo apt update + - name: install monero dependencies + run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev + - name: install requests + run: pip install requests + - name: tests + run: make release-test -j3 From fe92fa1391c200fce4a247341272011099781369 Mon Sep 17 00:00:00 2001 From: cohcho Date: Fri, 17 Jan 2020 19:25:26 +0000 Subject: [PATCH 167/469] [randomx] Add missing randomx_vm_set_cache() --- src/crypto/rx-slow-hash.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index a7a459ad3..0f38356fa 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -278,6 +278,10 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch CTHR_MUTEX_LOCK(rx_dataset_mutex); if (rx_dataset != NULL && rx_dataset_height != seedheight) rx_initdata(cache, miners, seedheight); + else if (rx_dataset == NULL) { + /* this is a no-op if the cache hasn't changed */ + randomx_vm_set_cache(rx_vm, rx_sp->rs_cache); + } CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } else { /* this is a no-op if the cache hasn't changed */ From d0641b42fec6d32763729c93718004fba318b051 Mon Sep 17 00:00:00 2001 From: Aaron Hook Date: Sat, 18 Jan 2020 19:18:56 -0800 Subject: [PATCH 168/469] net: fix incorrect less operator for top/i2p addresses --- src/net/i2p_address.cpp | 3 +- src/net/tor_address.cpp | 3 +- tests/unit_tests/net.cpp | 424 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 406 insertions(+), 24 deletions(-) diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp index cba829d3f..f4cc75fee 100644 --- a/src/net/i2p_address.cpp +++ b/src/net/i2p_address.cpp @@ -171,7 +171,8 @@ namespace net bool i2p_address::less(const i2p_address& rhs) const noexcept { - return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + int res = std::strcmp(host_str(), rhs.host_str()); + return res < 0 || (res == 0 && port() < rhs.port()); } bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp index 904a9a0fc..4414861e7 100644 --- a/src/net/tor_address.cpp +++ b/src/net/tor_address.cpp @@ -173,7 +173,8 @@ namespace net bool tor_address::less(const tor_address& rhs) const noexcept { - return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + int res = std::strcmp(host_str(), rhs.host_str()); + return res < 0 || (res == 0 && port() < rhs.port()); } bool tor_address::is_same_host(const tor_address& rhs) const noexcept diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index 262541bd2..9ef4cf706 100644 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -55,6 +55,7 @@ #include "net/dandelionpp.h" #include "net/error.h" +#include "net/i2p_address.h" #include "net/net_utils_base.h" #include "net/socks.h" #include "net/socks_connect.h" @@ -177,22 +178,33 @@ TEST(tor_address, valid) EXPECT_FALSE(address2.less(*address1)); EXPECT_TRUE(address1->less(address2)); - address2 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion} + ":", 65535)); + net::tor_address address3 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion} + ":", 65535)); - EXPECT_EQ(65535, address2.port()); - EXPECT_STREQ(v3_onion, address2.host_str()); - EXPECT_EQ(std::string{v3_onion} + ":65535", address2.str().c_str()); - EXPECT_TRUE(address2.is_blockable()); - EXPECT_FALSE(address2.equal(*address1)); - EXPECT_FALSE(address1->equal(address2)); - EXPECT_FALSE(address2 == *address1); - EXPECT_FALSE(*address1 == address2); - EXPECT_TRUE(address2 != *address1); - EXPECT_TRUE(*address1 != address2); - EXPECT_TRUE(address2.is_same_host(*address1)); - EXPECT_TRUE(address1->is_same_host(address2)); - EXPECT_FALSE(address2.less(*address1)); - EXPECT_TRUE(address1->less(address2)); + EXPECT_EQ(65535, address3.port()); + EXPECT_STREQ(v3_onion, address3.host_str()); + EXPECT_EQ(std::string{v3_onion} + ":65535", address3.str().c_str()); + EXPECT_TRUE(address3.is_blockable()); + EXPECT_FALSE(address3.equal(*address1)); + EXPECT_FALSE(address1->equal(address3)); + EXPECT_FALSE(address3 == *address1); + EXPECT_FALSE(*address1 == address3); + EXPECT_TRUE(address3 != *address1); + EXPECT_TRUE(*address1 != address3); + EXPECT_TRUE(address3.is_same_host(*address1)); + EXPECT_TRUE(address1->is_same_host(address3)); + EXPECT_FALSE(address3.less(*address1)); + EXPECT_TRUE(address1->less(address3)); + + EXPECT_FALSE(address3.equal(address2)); + EXPECT_FALSE(address2.equal(address3)); + EXPECT_FALSE(address3 == address2); + EXPECT_FALSE(address2 == address3); + EXPECT_TRUE(address3 != address2); + EXPECT_TRUE(address2 != address3); + EXPECT_FALSE(address3.is_same_host(address2)); + EXPECT_FALSE(address2.is_same_host(address3)); + EXPECT_TRUE(address3.less(address2)); + EXPECT_FALSE(address2.less(address3)); } TEST(tor_address, generic_network_address) @@ -220,7 +232,7 @@ TEST(tor_address, generic_network_address) namespace { - struct test_command + struct test_command_tor { net::tor_address tor; @@ -234,7 +246,7 @@ TEST(tor_address, epee_serializev_v2) { std::string buffer{}; { - test_command command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))}; + test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))}; EXPECT_FALSE(command.tor.is_unknown()); EXPECT_NE(net::tor_address{}, command.tor); EXPECT_STREQ(v2_onion, command.tor.host_str()); @@ -245,7 +257,7 @@ TEST(tor_address, epee_serializev_v2) EXPECT_TRUE(stg.store_to_binary(buffer)); } - test_command command{}; + test_command_tor command{}; { EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); @@ -285,7 +297,7 @@ TEST(tor_address, epee_serializev_v3) { std::string buffer{}; { - test_command command{MONERO_UNWRAP(net::tor_address::make(v3_onion, 10))}; + test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v3_onion, 10))}; EXPECT_FALSE(command.tor.is_unknown()); EXPECT_NE(net::tor_address{}, command.tor); EXPECT_STREQ(v3_onion, command.tor.host_str()); @@ -296,7 +308,7 @@ TEST(tor_address, epee_serializev_v3) EXPECT_TRUE(stg.store_to_binary(buffer)); } - test_command command{}; + test_command_tor command{}; { EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); @@ -336,7 +348,7 @@ TEST(tor_address, epee_serialize_unknown) { std::string buffer{}; { - test_command command{net::tor_address::unknown()}; + test_command_tor command{net::tor_address::unknown()}; EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); EXPECT_STREQ(net::tor_address::unknown_str(), command.tor.host_str()); @@ -347,7 +359,7 @@ TEST(tor_address, epee_serialize_unknown) EXPECT_TRUE(stg.store_to_binary(buffer)); } - test_command command{}; + test_command_tor command{}; { EXPECT_TRUE(command.tor.is_unknown()); EXPECT_EQ(net::tor_address{}, command.tor); @@ -513,6 +525,374 @@ TEST(get_network_address, onion) EXPECT_EQ(net::error::invalid_port, address); } +namespace +{ + static constexpr const char b32_i2p[] = + "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopn.b32.i2p"; + static constexpr const char b32_i2p_2[] = + "xmrto2bturnore26xmrto2bturnore26xmrto2bturnore26xmr2.b32.i2p"; +} + +TEST(i2p_address, constants) +{ + static_assert(!net::i2p_address::is_local(), "bad is_local() response"); + static_assert(!net::i2p_address::is_loopback(), "bad is_loopback() response"); + static_assert(net::i2p_address::get_type_id() == epee::net_utils::address_type::i2p, "bad get_type_id() response"); + + EXPECT_FALSE(net::i2p_address::is_local()); + EXPECT_FALSE(net::i2p_address::is_loopback()); + EXPECT_EQ(epee::net_utils::address_type::i2p, net::i2p_address::get_type_id()); + EXPECT_EQ(epee::net_utils::address_type::i2p, net::i2p_address::get_type_id()); +} + +TEST(i2p_address, invalid) +{ + EXPECT_TRUE(net::i2p_address::make("").has_error()); + EXPECT_TRUE(net::i2p_address::make(":").has_error()); + EXPECT_TRUE(net::i2p_address::make(".b32.i2p").has_error()); + EXPECT_TRUE(net::i2p_address::make(".b32.i2p:").has_error()); + EXPECT_TRUE(net::i2p_address::make(b32_i2p + 1).has_error()); + EXPECT_TRUE(net::i2p_address::make(boost::string_ref{b32_i2p, sizeof(b32_i2p) - 2}).has_error()); + EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ":65536").has_error()); + EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ":-1").has_error()); + + std::string i2p{b32_i2p}; + i2p.at(10) = 1; + EXPECT_TRUE(net::i2p_address::make(i2p).has_error()); +} + +TEST(i2p_address, unblockable_types) +{ + net::i2p_address i2p{}; + + ASSERT_NE(nullptr, i2p.host_str()); + EXPECT_STREQ("", i2p.host_str()); + EXPECT_STREQ("", i2p.str().c_str()); + EXPECT_EQ(0u, i2p.port()); + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_FALSE(i2p.is_local()); + EXPECT_FALSE(i2p.is_loopback()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p.get_type_id()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p.get_zone()); + + i2p = net::i2p_address::unknown(); + ASSERT_NE(nullptr, i2p.host_str()); + EXPECT_STREQ("", i2p.host_str()); + EXPECT_STREQ("", i2p.str().c_str()); + EXPECT_EQ(0u, i2p.port()); + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_FALSE(i2p.is_local()); + EXPECT_FALSE(i2p.is_loopback()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p.get_type_id()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p.get_zone()); + + EXPECT_EQ(net::i2p_address{}, net::i2p_address::unknown()); +} + +TEST(i2p_address, valid) +{ + const auto address1 = net::i2p_address::make(b32_i2p); + + ASSERT_TRUE(address1.has_value()); + EXPECT_EQ(0u, address1->port()); + EXPECT_STREQ(b32_i2p, address1->host_str()); + EXPECT_STREQ(b32_i2p, address1->str().c_str()); + EXPECT_TRUE(address1->is_blockable()); + + net::i2p_address address2{*address1}; + + EXPECT_EQ(0u, address2.port()); + EXPECT_STREQ(b32_i2p, address2.host_str()); + EXPECT_STREQ(b32_i2p, address2.str().c_str()); + EXPECT_TRUE(address2.is_blockable()); + EXPECT_TRUE(address2.equal(*address1)); + EXPECT_TRUE(address1->equal(address2)); + EXPECT_TRUE(address2 == *address1); + EXPECT_TRUE(*address1 == address2); + EXPECT_FALSE(address2 != *address1); + EXPECT_FALSE(*address1 != address2); + EXPECT_TRUE(address2.is_same_host(*address1)); + EXPECT_TRUE(address1->is_same_host(address2)); + EXPECT_FALSE(address2.less(*address1)); + EXPECT_FALSE(address1->less(address2)); + + address2 = MONERO_UNWRAP(net::i2p_address::make(std::string{b32_i2p_2} + ":6545")); + + EXPECT_EQ(6545, address2.port()); + EXPECT_STREQ(b32_i2p_2, address2.host_str()); + EXPECT_EQ(std::string{b32_i2p_2} + ":6545", address2.str().c_str()); + EXPECT_TRUE(address2.is_blockable()); + EXPECT_FALSE(address2.equal(*address1)); + EXPECT_FALSE(address1->equal(address2)); + EXPECT_FALSE(address2 == *address1); + EXPECT_FALSE(*address1 == address2); + EXPECT_TRUE(address2 != *address1); + EXPECT_TRUE(*address1 != address2); + EXPECT_FALSE(address2.is_same_host(*address1)); + EXPECT_FALSE(address1->is_same_host(address2)); + EXPECT_FALSE(address2.less(*address1)); + EXPECT_TRUE(address1->less(address2)); + + net::i2p_address address3 = MONERO_UNWRAP(net::i2p_address::make(std::string{b32_i2p} + ":", 65535)); + + EXPECT_EQ(65535, address3.port()); + EXPECT_STREQ(b32_i2p, address3.host_str()); + EXPECT_EQ(std::string{b32_i2p} + ":65535", address3.str().c_str()); + EXPECT_TRUE(address3.is_blockable()); + EXPECT_FALSE(address3.equal(*address1)); + EXPECT_FALSE(address1->equal(address3)); + EXPECT_FALSE(address3 == *address1); + EXPECT_FALSE(*address1 == address3); + EXPECT_TRUE(address3 != *address1); + EXPECT_TRUE(*address1 != address3); + EXPECT_TRUE(address3.is_same_host(*address1)); + EXPECT_TRUE(address1->is_same_host(address3)); + EXPECT_FALSE(address3.less(*address1)); + EXPECT_TRUE(address1->less(address3)); + + EXPECT_FALSE(address3.equal(address2)); + EXPECT_FALSE(address2.equal(address3)); + EXPECT_FALSE(address3 == address2); + EXPECT_FALSE(address2 == address3); + EXPECT_TRUE(address3 != address2); + EXPECT_TRUE(address2 != address3); + EXPECT_FALSE(address3.is_same_host(address2)); + EXPECT_FALSE(address2.is_same_host(address3)); + EXPECT_TRUE(address3.less(address2)); + EXPECT_FALSE(address2.less(address3)); +} + +TEST(i2p_address, generic_network_address) +{ + const epee::net_utils::network_address i2p1{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 8080))}; + const epee::net_utils::network_address i2p2{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 8080))}; + const epee::net_utils::network_address ip{epee::net_utils::ipv4_network_address{100, 200}}; + + EXPECT_EQ(i2p1, i2p2); + EXPECT_NE(ip, i2p1); + EXPECT_LT(ip, i2p1); + + EXPECT_STREQ(b32_i2p, i2p1.host_str().c_str()); + EXPECT_EQ(std::string{b32_i2p} + ":8080", i2p1.str()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p1.get_type_id()); + EXPECT_EQ(epee::net_utils::address_type::i2p, i2p2.get_type_id()); + EXPECT_EQ(epee::net_utils::address_type::ipv4, ip.get_type_id()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p1.get_zone()); + EXPECT_EQ(epee::net_utils::zone::i2p, i2p2.get_zone()); + EXPECT_EQ(epee::net_utils::zone::public_, ip.get_zone()); + EXPECT_TRUE(i2p1.is_blockable()); + EXPECT_TRUE(i2p2.is_blockable()); + EXPECT_TRUE(ip.is_blockable()); +} + +namespace +{ + struct test_command_i2p + { + net::i2p_address i2p; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(i2p); + END_KV_SERIALIZE_MAP() + }; +} + +TEST(i2p_address, epee_serializev_b32) +{ + std::string buffer{}; + { + test_command_i2p command{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 10))}; + EXPECT_FALSE(command.i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, command.i2p); + EXPECT_STREQ(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(10u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(command.store(stg)); + EXPECT_TRUE(stg.store_to_binary(buffer)); + } + + test_command_i2p command{}; + { + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(stg.load_from_binary(buffer)); + EXPECT_TRUE(command.load(stg)); + } + EXPECT_FALSE(command.i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, command.i2p); + EXPECT_STREQ(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(10u, command.i2p.port()); + + // make sure that exceeding max buffer doesn't destroy i2p_address::_load + { + epee::serialization::portable_storage stg{}; + stg.load_from_binary(buffer); + + std::string host{}; + ASSERT_TRUE(stg.get_value("host", host, stg.open_section("i2p", nullptr, false))); + EXPECT_EQ(std::strlen(b32_i2p), host.size()); + + host.push_back('k'); + EXPECT_TRUE(stg.set_value("host", host, stg.open_section("i2p", nullptr, false))); + EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` + } + + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STRNE(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); +} + +TEST(i2p_address, epee_serialize_unknown) +{ + std::string buffer{}; + { + test_command_i2p command{net::i2p_address::unknown()}; + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(command.store(stg)); + EXPECT_TRUE(stg.store_to_binary(buffer)); + } + + test_command_i2p command{}; + { + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STRNE(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + epee::serialization::portable_storage stg{}; + EXPECT_TRUE(stg.load_from_binary(buffer)); + EXPECT_TRUE(command.load(stg)); + } + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); + + // make sure that exceeding max buffer doesn't destroy i2p_address::_load + { + epee::serialization::portable_storage stg{}; + stg.load_from_binary(buffer); + + std::string host{}; + ASSERT_TRUE(stg.get_value("host", host, stg.open_section("i2p", nullptr, false))); + EXPECT_EQ(std::strlen(net::i2p_address::unknown_str()), host.size()); + + host.push_back('k'); + EXPECT_TRUE(stg.set_value("host", host, stg.open_section("i2p", nullptr, false))); + EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE` + } + + EXPECT_TRUE(command.i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, command.i2p); + EXPECT_STRNE(b32_i2p, command.i2p.host_str()); + EXPECT_EQ(0u, command.i2p.port()); +} + +TEST(i2p_address, boost_serialize_b32) +{ + std::string buffer{}; + { + const net::i2p_address i2p = MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 10)); + EXPECT_FALSE(i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, i2p); + EXPECT_STREQ(b32_i2p, i2p.host_str()); + EXPECT_EQ(10u, i2p.port()); + + std::ostringstream stream{}; + { + boost::archive::portable_binary_oarchive archive{stream}; + archive << i2p; + } + buffer = stream.str(); + } + + net::i2p_address i2p{}; + { + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); + + std::istringstream stream{buffer}; + boost::archive::portable_binary_iarchive archive{stream}; + archive >> i2p; + } + EXPECT_FALSE(i2p.is_unknown()); + EXPECT_NE(net::i2p_address{}, i2p); + EXPECT_STREQ(b32_i2p, i2p.host_str()); + EXPECT_EQ(10u, i2p.port()); +} + +TEST(i2p_address, boost_serialize_unknown) +{ + std::string buffer{}; + { + const net::i2p_address i2p{}; + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address::unknown(), i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); + + std::ostringstream stream{}; + { + boost::archive::portable_binary_oarchive archive{stream}; + archive << i2p; + } + buffer = stream.str(); + } + + net::i2p_address i2p{}; + { + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address{}, i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); + + std::istringstream stream{buffer}; + boost::archive::portable_binary_iarchive archive{stream}; + archive >> i2p; + } + EXPECT_TRUE(i2p.is_unknown()); + EXPECT_EQ(net::i2p_address::unknown(), i2p); + EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str()); + EXPECT_EQ(0u, i2p.port()); +} + +TEST(get_network_address, i2p) +{ + expect address = + net::get_network_address("i2p", 0); + EXPECT_EQ(net::error::unsupported_address, address); + + address = net::get_network_address(".b32.i2p", 0); + EXPECT_EQ(net::error::invalid_i2p_address, address); + + address = net::get_network_address(b32_i2p, 1000); + ASSERT_TRUE(bool(address)); + EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id()); + EXPECT_STREQ(b32_i2p, address->host_str().c_str()); + EXPECT_EQ(std::string{b32_i2p} + ":1000", address->str()); + + address = net::get_network_address(std::string{b32_i2p} + ":2000", 1000); + ASSERT_TRUE(bool(address)); + EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id()); + EXPECT_STREQ(b32_i2p, address->host_str().c_str()); + EXPECT_EQ(std::string{b32_i2p} + ":2000", address->str()); + + address = net::get_network_address(std::string{b32_i2p} + ":65536", 1000); + EXPECT_EQ(net::error::invalid_port, address); +} TEST(get_network_address, ipv4) { From 6f330865e55694498034970f6b623cb80efc4517 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Sun, 19 Jan 2020 17:15:20 +0100 Subject: [PATCH 169/469] fix tests bug added in #6110 - e.g., fixes gen_block_big_major_version test, error: generation failed: what=events not set, cannot compute valid RandomX PoW - ask for events only if difficulty > 1 (when it really matters) - throwing an exception changed to logging, so it is easy to spot a problem if tests start to fail. --- tests/core_tests/chaingen.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index c38ea614c..f935d4f64 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -393,11 +393,17 @@ void test_generator::fill_nonce(cryptonote::block& blk, const difficulty_type& d const cryptonote::Blockchain *blockchain = nullptr; std::unique_ptr bc; - if (blk.major_version >= RX_BLOCK_VERSION) + if (blk.major_version >= RX_BLOCK_VERSION && diffic > 1) { - CHECK_AND_ASSERT_THROW_MES(m_events != nullptr, "events not set, cannot compute valid RandomX PoW"); - bc = init_blockchain(*m_events, m_nettype); - blockchain = bc.get(); + if (m_events == nullptr) + { + MDEBUG("events not set, RandomX PoW can fail due to zero seed hash"); + } + else + { + bc = init_blockchain(*m_events, m_nettype); + blockchain = bc.get(); + } } blk.nonce = 0; From 2e9d1e6e1f7dde7c70c7181cdf19407cc17157dd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 24 Jan 2020 16:33:03 +0000 Subject: [PATCH 170/469] wallet2: fix store-tx-info loading The backward compatibility code was always setting it to 1 in modern wallets since store_tx_keys was not present and thus assumed to be 1 by default. Reported by SeventhAlpaca --- src/wallet/wallet2.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4d2ec5103..1f72f90b1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4059,9 +4059,18 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_always_confirm_transfers = field_always_confirm_transfers; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true); m_print_ring_members = field_print_ring_members; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true); - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true); - m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0)); + if (json.HasMember("store_tx_info")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, true, true); + m_store_tx_info = field_store_tx_info; + } + else if (json.HasMember("store_tx_keys")) // backward compatibility + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, true, true); + m_store_tx_info = field_store_tx_keys; + } + else + m_store_tx_info = true; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0); m_default_mixin = field_default_mixin; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0); From dab604e010da4a77cd4ecfc2514f2b4d3dac48b6 Mon Sep 17 00:00:00 2001 From: xiphon Date: Wed, 22 Jan 2020 16:30:47 +0000 Subject: [PATCH 171/469] wallet2_api: implement estimateTransactionFee --- src/wallet/api/wallet.cpp | 20 ++++++++++++++++++++ src/wallet/api/wallet.h | 2 ++ src/wallet/api/wallet2_api.h | 8 ++++++++ src/wallet/wallet2.cpp | 28 ++++++++++++++-------------- src/wallet/wallet2.h | 1 + 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6200c7a1f..33047f703 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1671,6 +1671,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t) delete t; } +uint64_t WalletImpl::estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const +{ + const size_t pubkey_size = 33; + const size_t encrypted_paymentid_size = 11; + const size_t extra_size = pubkey_size + encrypted_paymentid_size; + + return m_wallet->estimate_fee( + m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0), + m_wallet->use_fork_rules(4, 0), + 1, + m_wallet->get_min_ring_size() - 1, + destinations.size() + 1, + extra_size, + m_wallet->use_fork_rules(8, 0), + m_wallet->get_base_fee(), + m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast(priority))), + m_wallet->get_fee_quantization_mask()); +} + TransactionHistory *WalletImpl::history() { return m_history.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 331bf4b38..e46a62340 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -166,6 +166,8 @@ public: bool importKeyImages(const std::string &filename) override; virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; virtual Subaddress * subaddress() override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index e543a115b..3945e55c9 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -879,6 +879,14 @@ struct Wallet */ virtual void disposeTransaction(PendingTransaction * t) = 0; + /*! + * \brief Estimates transaction fee. + * \param destinations Vector consisting of pairs. + * \return Estimated fee. + */ + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const = 0; + /*! * \brief exportKeyImages - exports key images to file * \param filename diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4d2ec5103..0f0547567 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -879,20 +879,6 @@ uint8_t get_bulletproof_fork() return 8; } -uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) -{ - if (use_per_byte_fee) - { - const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); - } - else - { - const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); - } -} - uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) { if (use_per_byte_fee) @@ -7098,6 +7084,20 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto return sign_multisig_tx_to_file(exported_txs, filename, txids); } //---------------------------------------------------------------------------------------------------- +uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const +{ + if (use_per_byte_fee) + { + const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); + } + else + { + const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); + } +} + uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) { static const struct diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 8f840a42d..1d40d7f2f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1243,6 +1243,7 @@ private: std::vector> estimate_backlog(const std::vector> &fee_levels); std::vector> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector &fees); + uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const; uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1); uint64_t get_base_fee(); uint64_t get_fee_quantization_mask(); From dd8c6b170386b64ae1d4344c7abd93d7b814a43b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 19 Nov 2019 16:30:19 +0000 Subject: [PATCH 172/469] wallet: do not split integrated addresses in address book api --- src/simplewallet/simplewallet.cpp | 34 ++----- src/wallet/api/address_book.cpp | 54 +++-------- src/wallet/wallet2.cpp | 10 ++- src/wallet/wallet2.h | 35 ++++++-- src/wallet/wallet_rpc_server.cpp | 95 ++++---------------- src/wallet/wallet_rpc_server_commands_defs.h | 8 -- tests/functional_tests/address_book.py | 64 ++----------- tests/unit_tests/serialization.cpp | 1 - 8 files changed, 78 insertions(+), 223 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 87bbf62d3..d38673cdf 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -196,7 +196,7 @@ namespace " account tag_description "); const char* USAGE_ADDRESS("address [ new

!^ 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 literal 234948 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⪌U6(IqXZk~1xxcl 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