aboutsummaryrefslogtreecommitdiff
path: root/src/device_trezor
diff options
context:
space:
mode:
Diffstat (limited to 'src/device_trezor')
-rw-r--r--src/device_trezor/device_trezor.cpp77
-rw-r--r--src/device_trezor/device_trezor.hpp5
-rw-r--r--src/device_trezor/device_trezor_base.cpp103
-rw-r--r--src/device_trezor/device_trezor_base.hpp48
-rw-r--r--src/device_trezor/trezor/tools/README.md22
-rw-r--r--src/device_trezor/trezor/tools/pb2cpp.py14
-rw-r--r--src/device_trezor/trezor/tools/py2backports/__init__.py0
-rw-r--r--src/device_trezor/trezor/tools/py2backports/tempfile.py72
-rw-r--r--src/device_trezor/trezor/tools/py2backports/weakref.py148
-rw-r--r--src/device_trezor/trezor/transport.cpp2
10 files changed, 441 insertions, 50 deletions
diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp
index 5096fcea8..8868fb995 100644
--- a/src/device_trezor/device_trezor.cpp
+++ b/src/device_trezor/device_trezor.cpp
@@ -121,7 +121,8 @@ namespace trezor {
const boost::optional<cryptonote::network_type> & network_type){
AUTO_LOCK_CMD();
require_connected();
- test_ping();
+ device_state_reset_unsafe();
+ require_initialized();
auto req = std::make_shared<messages::monero::MoneroGetAddress>();
this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
@@ -136,7 +137,8 @@ namespace trezor {
const boost::optional<cryptonote::network_type> & network_type){
AUTO_LOCK_CMD();
require_connected();
- test_ping();
+ device_state_reset_unsafe();
+ require_initialized();
auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
@@ -152,7 +154,8 @@ namespace trezor {
{
AUTO_LOCK_CMD();
require_connected();
- test_ping();
+ device_state_reset_unsafe();
+ require_initialized();
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;
@@ -238,12 +241,11 @@ namespace trezor {
cpend.construction_data = cdata.tx_data;
// Transaction check
- cryptonote::blobdata tx_blob;
- cryptonote::transaction tx_deserialized;
- bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob);
- CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
- r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
- CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
+ try {
+ transaction_check(cdata, aux_data);
+ } catch(const std::exception &e){
+ throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what());
+ }
std::string key_images;
bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool
@@ -283,7 +285,8 @@ namespace trezor {
{
AUTO_LOCK_CMD();
require_connected();
- test_ping();
+ device_state_reset_unsafe();
+ require_initialized();
CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index");
signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
@@ -294,6 +297,7 @@ namespace trezor {
// Step: Init
auto init_msg = signer->step_init();
this->set_msg_addr(init_msg.get());
+ transaction_pre_check(init_msg);
auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
signer->step_init_ack(response);
@@ -351,6 +355,59 @@ namespace trezor {
signer->step_final_ack(ack_final);
}
+ void device_trezor::transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg)
+ {
+ CHECK_AND_ASSERT_THROW_MES(init_msg, "TransactionInitRequest is empty");
+ CHECK_AND_ASSERT_THROW_MES(init_msg->has_tsx_data(), "TransactionInitRequest has no transaction data");
+ CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features
+ const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0;
+
+ if (nonce_required){
+ // Versions 2.0.9 and lower do not support payment ID
+ CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information");
+ const uint32_t vma = m_features->major_version();
+ const uint32_t vmi = m_features->minor_version();
+ const uint32_t vpa = m_features->patch_version();
+ if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) {
+ throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update.");
+ }
+ }
+ }
+
+ void device_trezor::transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data)
+ {
+ // Simple serialization check
+ cryptonote::blobdata tx_blob;
+ cryptonote::transaction tx_deserialized;
+ bool r = cryptonote::t_serializable_object_to_blob(tdata.tx, tx_blob);
+ CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
+ r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
+ CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
+
+ // Extras check
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ cryptonote::tx_extra_nonce nonce;
+
+ r = cryptonote::parse_tx_extra(tdata.tx.extra, tx_extra_fields);
+ CHECK_AND_ASSERT_THROW_MES(r, "tx.extra parsing failed");
+
+ const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0;
+ const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce);
+ CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent");
+
+ if (nonce_required){
+ const std::string & payment_id = tdata.tsx_data.payment_id();
+ if (payment_id.size() == 32){
+ crypto::hash payment_id_long{};
+ CHECK_AND_ASSERT_THROW_MES(cryptonote::get_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_long), "Long payment ID not present");
+
+ } else if (payment_id.size() == 8){
+ crypto::hash8 payment_id_short{};
+ CHECK_AND_ASSERT_THROW_MES(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_short), "Short payment ID not present");
+ }
+ }
+ }
+
#else //WITH_DEVICE_TREZOR
void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp
index a2134574c..1f08be887 100644
--- a/src/device_trezor/device_trezor.hpp
+++ b/src/device_trezor/device_trezor.hpp
@@ -57,9 +57,8 @@ namespace trezor {
*/
class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
protected:
- // To speed up blockchain parsing the view key maybe handle here.
- crypto::secret_key viewkey;
- bool has_view_key;
+ void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg);
+ void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data);
public:
device_trezor();
diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp
index 38c20c30b..5071932ee 100644
--- a/src/device_trezor/device_trezor_base.cpp
+++ b/src/device_trezor/device_trezor_base.cpp
@@ -28,6 +28,9 @@
//
#include "device_trezor_base.hpp"
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/regex.hpp>
namespace hw {
namespace trezor {
@@ -36,10 +39,11 @@ namespace trezor {
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
+#define TREZOR_BIP44_HARDENED_ZERO 0x80000000
- const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
+ const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080};
- device_trezor_base::device_trezor_base() {
+ device_trezor_base::device_trezor_base(): m_callback(nullptr) {
}
@@ -61,7 +65,7 @@ namespace trezor {
}
bool device_trezor_base::set_name(const std::string & name) {
- this->full_name = name;
+ this->m_full_name = name;
this->name = "";
auto delim = name.find(':');
@@ -73,10 +77,10 @@ namespace trezor {
}
const std::string device_trezor_base::get_name() const {
- if (this->full_name.empty()) {
+ if (this->m_full_name.empty()) {
return std::string("<disconnected:").append(this->name).append(">");
}
- return this->full_name;
+ return this->m_full_name;
}
bool device_trezor_base::init() {
@@ -135,6 +139,9 @@ namespace trezor {
}
bool device_trezor_base::disconnect() {
+ m_device_state.clear();
+ m_features.reset();
+
if (m_transport){
try {
m_transport->close();
@@ -189,6 +196,25 @@ namespace trezor {
}
}
+ void device_trezor_base::require_initialized(){
+ if (!m_features){
+ throw exc::TrezorException("Device state not initialized");
+ }
+
+ if (m_features->has_bootloader_mode() && m_features->bootloader_mode()){
+ throw exc::TrezorException("Device is in the bootloader mode");
+ }
+
+ if (m_features->has_firmware_present() && !m_features->firmware_present()){
+ throw exc::TrezorException("Device has no firmware loaded");
+ }
+
+ // Hard requirement on initialized field, has to be there.
+ if (!m_features->has_initialized() || !m_features->initialized()){
+ throw exc::TrezorException("Device is not initialized");
+ }
+ }
+
void device_trezor_base::call_ping_unsafe(){
auto pingMsg = std::make_shared<messages::management::Ping>();
pingMsg->set_message("PING");
@@ -213,7 +239,7 @@ namespace trezor {
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
require_connected();
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
- this->getTransport()->write(*msg);
+ this->get_transport()->write(*msg);
}
GenericMessage device_trezor_base::read_raw(){
@@ -221,7 +247,7 @@ namespace trezor {
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
- this->getTransport()->read(msg_resp, &msg_resp_type);
+ this->get_transport()->read(msg_resp, &msg_resp_type);
return GenericMessage(msg_resp_type, msg_resp);
}
@@ -252,6 +278,39 @@ namespace trezor {
}
}
+ void device_trezor_base::ensure_derivation_path() noexcept {
+ if (m_wallet_deriv_path.empty()){
+ m_wallet_deriv_path.push_back(TREZOR_BIP44_HARDENED_ZERO); // default 0'
+ }
+ }
+
+ void device_trezor_base::set_derivation_path(const std::string &deriv_path){
+ this->m_wallet_deriv_path.clear();
+
+ if (deriv_path.empty() || deriv_path == "-"){
+ ensure_derivation_path();
+ return;
+ }
+
+ CHECK_AND_ASSERT_THROW_MES(deriv_path.size() <= 255, "Derivation path is too long");
+
+ std::vector<std::string> fields;
+ boost::split(fields, deriv_path, boost::is_any_of("/"));
+ CHECK_AND_ASSERT_THROW_MES(fields.size() <= 10, "Derivation path is too long");
+
+ boost::regex rgx("^([0-9]+)'?$");
+ boost::cmatch match;
+
+ this->m_wallet_deriv_path.reserve(fields.size());
+ for(const std::string & cur : fields){
+ const bool ok = boost::regex_match(cur.c_str(), match, rgx);
+ CHECK_AND_ASSERT_THROW_MES(ok, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur);
+ CHECK_AND_ASSERT_THROW_MES(match[0].length() > 0, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur);
+
+ const unsigned long cidx = std::stoul(match[0].str()) | TREZOR_BIP44_HARDENED_ZERO;
+ this->m_wallet_deriv_path.push_back((unsigned int)cidx);
+ }
+ }
/* ======================================================================= */
/* TREZOR PROTOCOL */
@@ -277,6 +336,25 @@ namespace trezor {
return false;
}
+ void device_trezor_base::device_state_reset_unsafe()
+ {
+ require_connected();
+ auto initMsg = std::make_shared<messages::management::Initialize>();
+
+ if(!m_device_state.empty()) {
+ initMsg->set_allocated_state(&m_device_state);
+ }
+
+ m_features = this->client_exchange<messages::management::Features>(initMsg);
+ initMsg->release_state();
+ }
+
+ void device_trezor_base::device_state_reset()
+ {
+ AUTO_LOCK_CMD();
+ device_state_reset_unsafe();
+ }
+
void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
{
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
@@ -324,7 +402,13 @@ namespace trezor {
// TODO: remove passphrase from memory
m.set_passphrase(passphrase.data(), passphrase.size());
}
+
+ if (!m_device_state.empty()){
+ m.set_allocated_state(&m_device_state);
+ }
+
resp = call_raw(&m);
+ m.release_state();
}
void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg)
@@ -332,10 +416,7 @@ namespace trezor {
MDEBUG("on_passhprase_state_request");
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
- if (m_callback){
- m_callback->on_passphrase_state_request(msg->state());
- }
-
+ m_device_state = msg->state();
messages::common::PassphraseStateAck m;
resp = call_raw(&m);
}
diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp
index 88d419494..3c35e8aca 100644
--- a/src/device_trezor/device_trezor_base.hpp
+++ b/src/device_trezor/device_trezor_base.hpp
@@ -58,17 +58,6 @@ namespace trezor {
class device_trezor_base;
/**
- * Trezor device callbacks
- */
- class trezor_callback {
- public:
- virtual void on_button_request() {};
- virtual void on_pin_request(epee::wipeable_string & pin) {};
- virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {};
- virtual void on_passphrase_state_request(const std::string & state) {};
- };
-
- /**
* TREZOR device template with basic functions
*/
class device_trezor_base : public hw::core::device_default {
@@ -79,9 +68,12 @@ namespace trezor {
mutable boost::mutex command_locker;
std::shared_ptr<Transport> m_transport;
- std::shared_ptr<trezor_callback> m_callback;
+ i_device_callback * m_callback;
- std::string full_name;
+ std::string m_full_name;
+ std::vector<unsigned int> m_wallet_deriv_path;
+ std::string m_device_state; // returned after passphrase entry, session
+ std::shared_ptr<messages::management::Features> m_features; // features from the last device reset
cryptonote::network_type network_type;
@@ -90,8 +82,11 @@ namespace trezor {
//
void require_connected();
+ void require_initialized();
void call_ping_unsafe();
void test_ping();
+ void device_state_reset_unsafe();
+ void ensure_derivation_path() noexcept;
// Communication methods
@@ -139,7 +134,7 @@ namespace trezor {
// Scoped session closer
BOOST_SCOPE_EXIT_ALL(&, this) {
if (open_session){
- this->getTransport()->close();
+ this->get_transport()->close();
}
};
@@ -187,9 +182,13 @@ namespace trezor {
msg->add_address_n(x);
}
} else {
+ ensure_derivation_path();
for (unsigned int i : DEFAULT_BIP44_PATH) {
msg->add_address_n(i);
}
+ for (unsigned int i : m_wallet_deriv_path) {
+ msg->add_address_n(i);
+ }
}
if (network_type){
@@ -212,16 +211,26 @@ namespace trezor {
bool reset();
// Default derivation path for Monero
- static const uint32_t DEFAULT_BIP44_PATH[3];
+ static const uint32_t DEFAULT_BIP44_PATH[2];
- std::shared_ptr<Transport> getTransport(){
+ std::shared_ptr<Transport> get_transport(){
return m_transport;
}
- std::shared_ptr<trezor_callback> getCallback(){
+ void set_callback(i_device_callback * callback) override {
+ m_callback = callback;
+ }
+
+ i_device_callback * get_callback(){
return m_callback;
}
+ std::shared_ptr<messages::management::Features> & get_features() {
+ return m_features;
+ }
+
+ void set_derivation_path(const std::string &deriv_path) override;
+
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
@@ -249,6 +258,11 @@ namespace trezor {
*/
bool ping();
+ /**
+ * Performs Initialize call to the Trezor, resets to known state.
+ */
+ void device_state_reset();
+
// Protocol callbacks
void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
diff --git a/src/device_trezor/trezor/tools/README.md b/src/device_trezor/trezor/tools/README.md
index b176017ac..0802e734a 100644
--- a/src/device_trezor/trezor/tools/README.md
+++ b/src/device_trezor/trezor/tools/README.md
@@ -10,14 +10,28 @@ Install `protoc` for your distribution. Requirements:
Soft requirement: Python 3, can be easily installed with [pyenv].
+If Python 3 is used there are no additional python dependencies.
-### Python 2
+Since Cmake 3.12 the `FindPython` module is used to locate the Python
+interpreter in your system. It preferably searches for Python 3, if none
+is found, it searches for Python 2.
-Workaround if there is no Python3 available:
+Lower version of the cmake uses another module which does not guarantee
+ordering. If you want to override the selected python you can do it in
+the following way:
```bash
-pip install backports.tempfile
-```
+export TREZOR_PYTHON=`which python3`
+```
+
+
+### Python 2.7+
+
+Python 3 has `tempfile.TemporaryDirectory` available but Python 2 lacks
+this class so the message generation code uses `backports.tempfile` package
+bundled in the repository.
+
+The minimal Python versions are 2.7 and 3.4
### Regenerate messages
diff --git a/src/device_trezor/trezor/tools/pb2cpp.py b/src/device_trezor/trezor/tools/pb2cpp.py
index 4d7cc775f..3e0318ea5 100644
--- a/src/device_trezor/trezor/tools/pb2cpp.py
+++ b/src/device_trezor/trezor/tools/pb2cpp.py
@@ -14,12 +14,18 @@ import hashlib
try:
from tempfile import TemporaryDirectory
except:
- # Py2 backward compatibility, optionally installed by user
- # pip install backports.tempfile
+ # Py2 backward compatibility, using bundled sources.
+ # Original source: pip install backports.tempfile
try:
- from backports.tempfile import TemporaryDirectory
+ # Try bundled python version
+ import sys
+ sys.path.append(os.path.dirname(__file__))
+ from py2backports.tempfile import TemporaryDirectory
+
except:
- raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile')
+ raise EnvironmentError('Python 2.7+ or 3.4+ is required. '
+ 'TemporaryDirectory is not available in Python 2.'
+ 'Try to specify python to use, e.g.: "export TREZOR_PYTHON=`which python3`"')
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
diff --git a/src/device_trezor/trezor/tools/py2backports/__init__.py b/src/device_trezor/trezor/tools/py2backports/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/device_trezor/trezor/tools/py2backports/__init__.py
diff --git a/src/device_trezor/trezor/tools/py2backports/tempfile.py b/src/device_trezor/trezor/tools/py2backports/tempfile.py
new file mode 100644
index 000000000..e259dea3f
--- /dev/null
+++ b/src/device_trezor/trezor/tools/py2backports/tempfile.py
@@ -0,0 +1,72 @@
+"""
+https://github.com/pjdelport/backports.tempfile/blob/master/src/backports/tempfile.py
+Partial backport of Python 3.5's tempfile module:
+ TemporaryDirectory
+Backport modifications are marked with marked with "XXX backport".
+"""
+from __future__ import absolute_import
+
+import sys
+import warnings as _warnings
+from shutil import rmtree as _rmtree
+
+from py2backports.weakref import finalize
+
+
+# XXX backport: Rather than backporting all of mkdtemp(), we just create a
+# thin wrapper implementing its Python 3.5 signature.
+if sys.version_info < (3, 5):
+ from tempfile import mkdtemp as old_mkdtemp
+
+ def mkdtemp(suffix=None, prefix=None, dir=None):
+ """
+ Wrap `tempfile.mkdtemp()` to make the suffix and prefix optional (like Python 3.5).
+ """
+ kwargs = {k: v for (k, v) in
+ dict(suffix=suffix, prefix=prefix, dir=dir).items()
+ if v is not None}
+ return old_mkdtemp(**kwargs)
+
+else:
+ from tempfile import mkdtemp
+
+
+# XXX backport: ResourceWarning was added in Python 3.2.
+# For earlier versions, fall back to RuntimeWarning instead.
+_ResourceWarning = RuntimeWarning if sys.version_info < (3, 2) else ResourceWarning
+
+
+class TemporaryDirectory(object):
+ """Create and return a temporary directory. This has the same
+ behavior as mkdtemp but can be used as a context manager. For
+ example:
+ with TemporaryDirectory() as tmpdir:
+ ...
+ Upon exiting the context, the directory and everything contained
+ in it are removed.
+ """
+
+ def __init__(self, suffix=None, prefix=None, dir=None):
+ self.name = mkdtemp(suffix, prefix, dir)
+ self._finalizer = finalize(
+ self, self._cleanup, self.name,
+ warn_message="Implicitly cleaning up {!r}".format(self))
+
+ @classmethod
+ def _cleanup(cls, name, warn_message):
+ _rmtree(name)
+ _warnings.warn(warn_message, _ResourceWarning)
+
+
+ def __repr__(self):
+ return "<{} {!r}>".format(self.__class__.__name__, self.name)
+
+ def __enter__(self):
+ return self.name
+
+ def __exit__(self, exc, value, tb):
+ self.cleanup()
+
+ def cleanup(self):
+ if self._finalizer.detach():
+ _rmtree(self.name)
diff --git a/src/device_trezor/trezor/tools/py2backports/weakref.py b/src/device_trezor/trezor/tools/py2backports/weakref.py
new file mode 100644
index 000000000..eb646812b
--- /dev/null
+++ b/src/device_trezor/trezor/tools/py2backports/weakref.py
@@ -0,0 +1,148 @@
+"""
+https://github.com/pjdelport/backports.weakref/blob/master/src/backports/weakref.py
+Partial backport of Python 3.6's weakref module:
+ finalize (new in Python 3.4)
+Backport modifications are marked with "XXX backport".
+"""
+from __future__ import absolute_import
+
+import itertools
+import sys
+from weakref import ref
+
+__all__ = ['finalize']
+
+
+class finalize(object):
+ """Class for finalization of weakrefable objects
+ finalize(obj, func, *args, **kwargs) returns a callable finalizer
+ object which will be called when obj is garbage collected. The
+ first time the finalizer is called it evaluates func(*arg, **kwargs)
+ and returns the result. After this the finalizer is dead, and
+ calling it just returns None.
+ When the program exits any remaining finalizers for which the
+ atexit attribute is true will be run in reverse order of creation.
+ By default atexit is true.
+ """
+
+ # Finalizer objects don't have any state of their own. They are
+ # just used as keys to lookup _Info objects in the registry. This
+ # ensures that they cannot be part of a ref-cycle.
+
+ __slots__ = ()
+ _registry = {}
+ _shutdown = False
+ _index_iter = itertools.count()
+ _dirty = False
+ _registered_with_atexit = False
+
+ class _Info(object):
+ __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
+
+ def __init__(self, obj, func, *args, **kwargs):
+ if not self._registered_with_atexit:
+ # We may register the exit function more than once because
+ # of a thread race, but that is harmless
+ import atexit
+ atexit.register(self._exitfunc)
+ finalize._registered_with_atexit = True
+ info = self._Info()
+ info.weakref = ref(obj, self)
+ info.func = func
+ info.args = args
+ info.kwargs = kwargs or None
+ info.atexit = True
+ info.index = next(self._index_iter)
+ self._registry[self] = info
+ finalize._dirty = True
+
+ def __call__(self, _=None):
+ """If alive then mark as dead and return func(*args, **kwargs);
+ otherwise return None"""
+ info = self._registry.pop(self, None)
+ if info and not self._shutdown:
+ return info.func(*info.args, **(info.kwargs or {}))
+
+ def detach(self):
+ """If alive then mark as dead and return (obj, func, args, kwargs);
+ otherwise return None"""
+ info = self._registry.get(self)
+ obj = info and info.weakref()
+ if obj is not None and self._registry.pop(self, None):
+ return (obj, info.func, info.args, info.kwargs or {})
+
+ def peek(self):
+ """If alive then return (obj, func, args, kwargs);
+ otherwise return None"""
+ info = self._registry.get(self)
+ obj = info and info.weakref()
+ if obj is not None:
+ return (obj, info.func, info.args, info.kwargs or {})
+
+ @property
+ def alive(self):
+ """Whether finalizer is alive"""
+ return self in self._registry
+
+ @property
+ def atexit(self):
+ """Whether finalizer should be called at exit"""
+ info = self._registry.get(self)
+ return bool(info) and info.atexit
+
+ @atexit.setter
+ def atexit(self, value):
+ info = self._registry.get(self)
+ if info:
+ info.atexit = bool(value)
+
+ def __repr__(self):
+ info = self._registry.get(self)
+ obj = info and info.weakref()
+ if obj is None:
+ return '<%s object at %#x; dead>' % (type(self).__name__, id(self))
+ else:
+ return '<%s object at %#x; for %r at %#x>' % \
+ (type(self).__name__, id(self), type(obj).__name__, id(obj))
+
+ @classmethod
+ def _select_for_exit(cls):
+ # Return live finalizers marked for exit, oldest first
+ L = [(f,i) for (f,i) in cls._registry.items() if i.atexit]
+ L.sort(key=lambda item:item[1].index)
+ return [f for (f,i) in L]
+
+ @classmethod
+ def _exitfunc(cls):
+ # At shutdown invoke finalizers for which atexit is true.
+ # This is called once all other non-daemonic threads have been
+ # joined.
+ reenable_gc = False
+ try:
+ if cls._registry:
+ import gc
+ if gc.isenabled():
+ reenable_gc = True
+ gc.disable()
+ pending = None
+ while True:
+ if pending is None or finalize._dirty:
+ pending = cls._select_for_exit()
+ finalize._dirty = False
+ if not pending:
+ break
+ f = pending.pop()
+ try:
+ # gc is disabled, so (assuming no daemonic
+ # threads) the following is the only line in
+ # this function which might trigger creation
+ # of a new finalizer
+ f()
+ except Exception:
+ sys.excepthook(*sys.exc_info())
+ assert f not in cls._registry
+ finally:
+ # prevent any more finalizers from executing during shutdown
+ finalize._shutdown = True
+ if reenable_gc:
+ gc.enable()
diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp
index 814537eb6..cd66e59e8 100644
--- a/src/device_trezor/trezor/transport.cpp
+++ b/src/device_trezor/trezor/transport.cpp
@@ -840,7 +840,7 @@ namespace trezor{
throw exc::DeviceAcquireException("Unable to claim libusb device");
}
- m_conn_count += 1;
+ m_conn_count = 1;
m_proto->session_begin(*this);
#undef TREZOR_DESTROY_SESSION