Merge pull request #4839
d71f89e2
device/trezor: device/trezor: correct device initialization, status check (Dusan Klinec)65b9bca7
device/trezor: python2 compatibility - bundle dependencies (Dusan Klinec)9cf636af
device/trezor: ask for KI sync on first refresh (Dusan Klinec)d21dad70
device: enable to use multiple independent device wallets (Dusan Klinec)318cc784
device/trezor: passphrase entry on host (Dusan Klinec)
This commit is contained in:
commit
0b31baf6e4
|
@ -56,7 +56,7 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_DEVICE_TREZOR_UDP_RELEASE)
|
if(USE_DEVICE_TREZOR_UDP_RELEASE)
|
||||||
add_definitions(-DWITH_DEVICE_TREZOR_UDP_RELEASE=1)
|
add_definitions(-DUSE_DEVICE_TREZOR_UDP_RELEASE=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (Protobuf_INCLUDE_DIR)
|
if (Protobuf_INCLUDE_DIR)
|
||||||
|
|
|
@ -80,6 +80,14 @@ namespace hw {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class i_device_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 ~i_device_callback() = default;
|
||||||
|
};
|
||||||
|
|
||||||
class device {
|
class device {
|
||||||
protected:
|
protected:
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -129,6 +137,8 @@ namespace hw {
|
||||||
virtual device_type get_type() const = 0;
|
virtual device_type get_type() const = 0;
|
||||||
|
|
||||||
virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
|
virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
|
||||||
|
virtual void set_callback(i_device_callback * callback) {};
|
||||||
|
virtual void set_derivation_path(const std::string &derivation_path) {};
|
||||||
|
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
/* LOCKER */
|
/* LOCKER */
|
||||||
|
|
|
@ -121,7 +121,8 @@ namespace trezor {
|
||||||
const boost::optional<cryptonote::network_type> & network_type){
|
const boost::optional<cryptonote::network_type> & network_type){
|
||||||
AUTO_LOCK_CMD();
|
AUTO_LOCK_CMD();
|
||||||
require_connected();
|
require_connected();
|
||||||
test_ping();
|
device_state_reset_unsafe();
|
||||||
|
require_initialized();
|
||||||
|
|
||||||
auto req = std::make_shared<messages::monero::MoneroGetAddress>();
|
auto req = std::make_shared<messages::monero::MoneroGetAddress>();
|
||||||
this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
|
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){
|
const boost::optional<cryptonote::network_type> & network_type){
|
||||||
AUTO_LOCK_CMD();
|
AUTO_LOCK_CMD();
|
||||||
require_connected();
|
require_connected();
|
||||||
test_ping();
|
device_state_reset_unsafe();
|
||||||
|
require_initialized();
|
||||||
|
|
||||||
auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
|
auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
|
||||||
this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
|
this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
|
||||||
|
@ -152,7 +154,8 @@ namespace trezor {
|
||||||
{
|
{
|
||||||
AUTO_LOCK_CMD();
|
AUTO_LOCK_CMD();
|
||||||
require_connected();
|
require_connected();
|
||||||
test_ping();
|
device_state_reset_unsafe();
|
||||||
|
require_initialized();
|
||||||
|
|
||||||
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;
|
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;
|
||||||
|
|
||||||
|
@ -238,12 +241,11 @@ namespace trezor {
|
||||||
cpend.construction_data = cdata.tx_data;
|
cpend.construction_data = cdata.tx_data;
|
||||||
|
|
||||||
// Transaction check
|
// Transaction check
|
||||||
cryptonote::blobdata tx_blob;
|
try {
|
||||||
cryptonote::transaction tx_deserialized;
|
transaction_check(cdata, aux_data);
|
||||||
bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob);
|
} catch(const std::exception &e){
|
||||||
CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
|
throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what());
|
||||||
r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
|
}
|
||||||
CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
|
|
||||||
|
|
||||||
std::string key_images;
|
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
|
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();
|
AUTO_LOCK_CMD();
|
||||||
require_connected();
|
require_connected();
|
||||||
test_ping();
|
device_state_reset_unsafe();
|
||||||
|
require_initialized();
|
||||||
|
|
||||||
CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index");
|
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);
|
signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
|
||||||
|
@ -294,6 +297,7 @@ namespace trezor {
|
||||||
// Step: Init
|
// Step: Init
|
||||||
auto init_msg = signer->step_init();
|
auto init_msg = signer->step_init();
|
||||||
this->set_msg_addr(init_msg.get());
|
this->set_msg_addr(init_msg.get());
|
||||||
|
transaction_pre_check(init_msg);
|
||||||
|
|
||||||
auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
|
auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
|
||||||
signer->step_init_ack(response);
|
signer->step_init_ack(response);
|
||||||
|
@ -351,6 +355,59 @@ namespace trezor {
|
||||||
signer->step_final_ack(ack_final);
|
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
|
#else //WITH_DEVICE_TREZOR
|
||||||
|
|
||||||
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry) {
|
void register_all(std::map<std::string, std::unique_ptr<device>> ®istry) {
|
||||||
|
|
|
@ -57,9 +57,8 @@ namespace trezor {
|
||||||
*/
|
*/
|
||||||
class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
|
class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
|
||||||
protected:
|
protected:
|
||||||
// To speed up blockchain parsing the view key maybe handle here.
|
void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg);
|
||||||
crypto::secret_key viewkey;
|
void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data);
|
||||||
bool has_view_key;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
device_trezor();
|
device_trezor();
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "device_trezor_base.hpp"
|
#include "device_trezor_base.hpp"
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
namespace hw {
|
namespace hw {
|
||||||
namespace trezor {
|
namespace trezor {
|
||||||
|
@ -36,10 +39,11 @@ namespace trezor {
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
|
#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) {
|
bool device_trezor_base::set_name(const std::string & name) {
|
||||||
this->full_name = name;
|
this->m_full_name = name;
|
||||||
this->name = "";
|
this->name = "";
|
||||||
|
|
||||||
auto delim = name.find(':');
|
auto delim = name.find(':');
|
||||||
|
@ -73,10 +77,10 @@ namespace trezor {
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string device_trezor_base::get_name() const {
|
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 std::string("<disconnected:").append(this->name).append(">");
|
||||||
}
|
}
|
||||||
return this->full_name;
|
return this->m_full_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool device_trezor_base::init() {
|
bool device_trezor_base::init() {
|
||||||
|
@ -135,6 +139,9 @@ namespace trezor {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool device_trezor_base::disconnect() {
|
bool device_trezor_base::disconnect() {
|
||||||
|
m_device_state.clear();
|
||||||
|
m_features.reset();
|
||||||
|
|
||||||
if (m_transport){
|
if (m_transport){
|
||||||
try {
|
try {
|
||||||
m_transport->close();
|
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(){
|
void device_trezor_base::call_ping_unsafe(){
|
||||||
auto pingMsg = std::make_shared<messages::management::Ping>();
|
auto pingMsg = std::make_shared<messages::management::Ping>();
|
||||||
pingMsg->set_message("PING");
|
pingMsg->set_message("PING");
|
||||||
|
@ -213,7 +239,7 @@ namespace trezor {
|
||||||
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
|
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
|
||||||
require_connected();
|
require_connected();
|
||||||
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
this->getTransport()->write(*msg);
|
this->get_transport()->write(*msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericMessage device_trezor_base::read_raw(){
|
GenericMessage device_trezor_base::read_raw(){
|
||||||
|
@ -221,7 +247,7 @@ namespace trezor {
|
||||||
std::shared_ptr<google::protobuf::Message> msg_resp;
|
std::shared_ptr<google::protobuf::Message> msg_resp;
|
||||||
hw::trezor::messages::MessageType msg_resp_type;
|
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);
|
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 */
|
/* TREZOR PROTOCOL */
|
||||||
|
@ -277,6 +336,25 @@ namespace trezor {
|
||||||
return false;
|
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)
|
void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
|
||||||
{
|
{
|
||||||
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
@ -324,7 +402,13 @@ namespace trezor {
|
||||||
// TODO: remove passphrase from memory
|
// TODO: remove passphrase from memory
|
||||||
m.set_passphrase(passphrase.data(), passphrase.size());
|
m.set_passphrase(passphrase.data(), passphrase.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_device_state.empty()){
|
||||||
|
m.set_allocated_state(&m_device_state);
|
||||||
|
}
|
||||||
|
|
||||||
resp = call_raw(&m);
|
resp = call_raw(&m);
|
||||||
|
m.release_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg)
|
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");
|
MDEBUG("on_passhprase_state_request");
|
||||||
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
|
||||||
|
|
||||||
if (m_callback){
|
m_device_state = msg->state();
|
||||||
m_callback->on_passphrase_state_request(msg->state());
|
|
||||||
}
|
|
||||||
|
|
||||||
messages::common::PassphraseStateAck m;
|
messages::common::PassphraseStateAck m;
|
||||||
resp = call_raw(&m);
|
resp = call_raw(&m);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,17 +57,6 @@ namespace trezor {
|
||||||
#ifdef WITH_DEVICE_TREZOR
|
#ifdef WITH_DEVICE_TREZOR
|
||||||
class device_trezor_base;
|
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
|
* TREZOR device template with basic functions
|
||||||
*/
|
*/
|
||||||
|
@ -79,9 +68,12 @@ namespace trezor {
|
||||||
mutable boost::mutex command_locker;
|
mutable boost::mutex command_locker;
|
||||||
|
|
||||||
std::shared_ptr<Transport> m_transport;
|
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;
|
cryptonote::network_type network_type;
|
||||||
|
|
||||||
|
@ -90,8 +82,11 @@ namespace trezor {
|
||||||
//
|
//
|
||||||
|
|
||||||
void require_connected();
|
void require_connected();
|
||||||
|
void require_initialized();
|
||||||
void call_ping_unsafe();
|
void call_ping_unsafe();
|
||||||
void test_ping();
|
void test_ping();
|
||||||
|
void device_state_reset_unsafe();
|
||||||
|
void ensure_derivation_path() noexcept;
|
||||||
|
|
||||||
// Communication methods
|
// Communication methods
|
||||||
|
|
||||||
|
@ -139,7 +134,7 @@ namespace trezor {
|
||||||
// Scoped session closer
|
// Scoped session closer
|
||||||
BOOST_SCOPE_EXIT_ALL(&, this) {
|
BOOST_SCOPE_EXIT_ALL(&, this) {
|
||||||
if (open_session){
|
if (open_session){
|
||||||
this->getTransport()->close();
|
this->get_transport()->close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,9 +182,13 @@ namespace trezor {
|
||||||
msg->add_address_n(x);
|
msg->add_address_n(x);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ensure_derivation_path();
|
||||||
for (unsigned int i : DEFAULT_BIP44_PATH) {
|
for (unsigned int i : DEFAULT_BIP44_PATH) {
|
||||||
msg->add_address_n(i);
|
msg->add_address_n(i);
|
||||||
}
|
}
|
||||||
|
for (unsigned int i : m_wallet_deriv_path) {
|
||||||
|
msg->add_address_n(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (network_type){
|
if (network_type){
|
||||||
|
@ -212,16 +211,26 @@ namespace trezor {
|
||||||
bool reset();
|
bool reset();
|
||||||
|
|
||||||
// Default derivation path for Monero
|
// 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;
|
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;
|
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 */
|
/* SETUP/TEARDOWN */
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
|
@ -249,6 +258,11 @@ namespace trezor {
|
||||||
*/
|
*/
|
||||||
bool ping();
|
bool ping();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs Initialize call to the Trezor, resets to known state.
|
||||||
|
*/
|
||||||
|
void device_state_reset();
|
||||||
|
|
||||||
// Protocol callbacks
|
// Protocol callbacks
|
||||||
void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
|
void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
|
||||||
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
|
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
|
||||||
|
|
|
@ -10,15 +10,29 @@ Install `protoc` for your distribution. Requirements:
|
||||||
|
|
||||||
|
|
||||||
Soft requirement: Python 3, can be easily installed with [pyenv].
|
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
|
```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
|
### Regenerate messages
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -14,12 +14,18 @@ import hashlib
|
||||||
try:
|
try:
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
except:
|
except:
|
||||||
# Py2 backward compatibility, optionally installed by user
|
# Py2 backward compatibility, using bundled sources.
|
||||||
# pip install backports.tempfile
|
# Original source: pip install backports.tempfile
|
||||||
try:
|
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:
|
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"
|
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
|
||||||
|
|
|
@ -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)
|
|
@ -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()
|
|
@ -840,7 +840,7 @@ namespace trezor{
|
||||||
throw exc::DeviceAcquireException("Unable to claim libusb device");
|
throw exc::DeviceAcquireException("Unable to claim libusb device");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_conn_count += 1;
|
m_conn_count = 1;
|
||||||
m_proto->session_begin(*this);
|
m_proto->session_begin(*this);
|
||||||
|
|
||||||
#undef TREZOR_DESTROY_SESSION
|
#undef TREZOR_DESTROY_SESSION
|
||||||
|
|
|
@ -3790,6 +3790,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||||
{
|
{
|
||||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||||
m_wallet = std::move(rc.first);
|
m_wallet = std::move(rc.first);
|
||||||
|
m_wallet->callback(this);
|
||||||
if (!m_wallet)
|
if (!m_wallet)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
|
@ -3807,9 +3808,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||||
m_wallet->set_refresh_from_block_height(m_restore_height);
|
m_wallet->set_refresh_from_block_height(m_restore_height);
|
||||||
|
|
||||||
auto device_desc = tools::wallet2::device_name_option(vm);
|
auto device_desc = tools::wallet2::device_name_option(vm);
|
||||||
|
auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
|
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
|
||||||
|
m_wallet->device_derivation_path(device_derivation_path);
|
||||||
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
|
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
|
||||||
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
|
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
|
||||||
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||||
|
@ -3897,7 +3900,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
||||||
epee::wipeable_string password;
|
epee::wipeable_string password;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter);
|
auto rc = tools::wallet2::make_from_file(vm, false, "", password_prompter);
|
||||||
m_wallet = std::move(rc.first);
|
m_wallet = std::move(rc.first);
|
||||||
password = std::move(std::move(rc.second).password());
|
password = std::move(std::move(rc.second).password());
|
||||||
if (!m_wallet)
|
if (!m_wallet)
|
||||||
|
@ -3905,6 +3908,8 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_wallet->callback(this);
|
||||||
|
m_wallet->load(m_wallet_file, password);
|
||||||
std::string prefix;
|
std::string prefix;
|
||||||
bool ready;
|
bool ready;
|
||||||
uint32_t threshold, total;
|
uint32_t threshold, total;
|
||||||
|
@ -4308,6 +4313,61 @@ boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char
|
||||||
return pwd_container->password();
|
return pwd_container->password();
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::on_button_request()
|
||||||
|
{
|
||||||
|
message_writer(console_color_white, false) << tr("Device requires attention");
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::on_pin_request(epee::wipeable_string & pin)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
rdln::suspend_readline pause_readline;
|
||||||
|
#endif
|
||||||
|
std::string msg = tr("Enter device PIN");
|
||||||
|
auto pwd_container = tools::password_container::prompt(false, msg.c_str());
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN"));
|
||||||
|
pin = pwd_container->password();
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
||||||
|
{
|
||||||
|
if (on_device){
|
||||||
|
message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
rdln::suspend_readline pause_readline;
|
||||||
|
#endif
|
||||||
|
std::string msg = tr("Enter device passphrase");
|
||||||
|
auto pwd_container = tools::password_container::prompt(false, msg.c_str());
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase"));
|
||||||
|
passphrase = pwd_container->password();
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money)
|
||||||
|
{
|
||||||
|
// Key image sync after the first refresh
|
||||||
|
if (!m_wallet->get_account().get_device().has_tx_cold_sign()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!received_money || m_wallet->get_device_last_key_image_sync() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished first refresh for HW device and money received -> KI sync
|
||||||
|
message_writer() << "\n" << tr("The first refresh has finished for the HW-based wallet with received money. hw_key_images_sync is needed. ");
|
||||||
|
|
||||||
|
std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): "));
|
||||||
|
if (std::cin.eof() || !command_line::is_yes(accepted)) {
|
||||||
|
message_writer(console_color_red, false) << tr("hw_key_images_sync skipped. Run command manually before a transfer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_images_sync_intern();
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
|
bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
|
||||||
{
|
{
|
||||||
if (!try_connect_to_daemon(is_init))
|
if (!try_connect_to_daemon(is_init))
|
||||||
|
@ -4325,13 +4385,14 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||||
message_writer() << tr("Starting refresh...");
|
message_writer() << tr("Starting refresh...");
|
||||||
|
|
||||||
uint64_t fetched_blocks = 0;
|
uint64_t fetched_blocks = 0;
|
||||||
|
bool received_money = false;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
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);});
|
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->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks);
|
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money);
|
||||||
ok = true;
|
ok = true;
|
||||||
// Clear line "Height xxx of xxx"
|
// Clear line "Height xxx of xxx"
|
||||||
std::cout << "\r \r";
|
std::cout << "\r \r";
|
||||||
|
@ -4339,6 +4400,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||||
if (is_init)
|
if (is_init)
|
||||||
print_accounts();
|
print_accounts();
|
||||||
show_balance_unlocked();
|
show_balance_unlocked();
|
||||||
|
on_refresh_finished(start_height, fetched_blocks, is_init, received_money);
|
||||||
}
|
}
|
||||||
catch (const tools::error::daemon_busy&)
|
catch (const tools::error::daemon_busy&)
|
||||||
{
|
{
|
||||||
|
@ -8096,13 +8158,13 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
|
||||||
fail_msg_writer() << tr("hw wallet does not support cold KI sync");
|
fail_msg_writer() << tr("hw wallet does not support cold KI sync");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!m_wallet->is_trusted_daemon())
|
|
||||||
{
|
|
||||||
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCK_IDLE_SCOPE();
|
LOCK_IDLE_SCOPE();
|
||||||
|
key_images_sync_intern();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void simple_wallet::key_images_sync_intern(){
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
|
message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
|
||||||
|
@ -8111,19 +8173,23 @@ bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
|
||||||
uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
|
uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
|
||||||
if (height > 0)
|
if (height > 0)
|
||||||
{
|
{
|
||||||
success_msg_writer() << tr("Signed key images imported to height ") << height << ", "
|
success_msg_writer() << tr("Key images synchronized to height ") << height;
|
||||||
<< print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
|
if (!m_wallet->is_trusted_daemon())
|
||||||
} else {
|
{
|
||||||
|
message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
success_msg_writer() << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
fail_msg_writer() << tr("Failed to import key images");
|
fail_msg_writer() << tr("Failed to import key images");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("Failed to import key images: ") << e.what();
|
fail_msg_writer() << tr("Failed to import key images: ") << e.what();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
|
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
|
||||||
|
|
|
@ -241,6 +241,8 @@ namespace cryptonote
|
||||||
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
|
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
|
||||||
std::string get_prompt() const;
|
std::string get_prompt() const;
|
||||||
bool print_seed(bool encrypted);
|
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);
|
||||||
|
|
||||||
struct transfer_view
|
struct transfer_view
|
||||||
{
|
{
|
||||||
|
@ -287,6 +289,9 @@ namespace cryptonote
|
||||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
|
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
|
||||||
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
|
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
|
||||||
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason);
|
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason);
|
||||||
|
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);
|
||||||
//----------------------------------------------------------
|
//----------------------------------------------------------
|
||||||
|
|
||||||
friend class refresh_progress_reporter_t;
|
friend class refresh_progress_reporter_t;
|
||||||
|
|
|
@ -222,6 +222,7 @@ struct options {
|
||||||
};
|
};
|
||||||
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
|
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
|
||||||
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
|
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
|
||||||
|
const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
|
||||||
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
|
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -274,6 +275,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||||
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
|
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
|
||||||
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
||||||
auto device_name = command_line::get_arg(vm, opts.hw_device);
|
auto device_name = command_line::get_arg(vm, opts.hw_device);
|
||||||
|
auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
|
||||||
|
|
||||||
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
|
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
|
||||||
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
|
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
|
||||||
|
@ -329,6 +331,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||||
wallet->set_ring_database(ringdb_path.string());
|
wallet->set_ring_database(ringdb_path.string());
|
||||||
wallet->device_name(device_name);
|
wallet->device_name(device_name);
|
||||||
|
wallet->device_derivation_path(device_derivation_path);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -838,6 +841,24 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wallet_device_callback::on_button_request()
|
||||||
|
{
|
||||||
|
if (wallet)
|
||||||
|
wallet->on_button_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wallet_device_callback::on_pin_request(epee::wipeable_string & pin)
|
||||||
|
{
|
||||||
|
if (wallet)
|
||||||
|
wallet->on_pin_request(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
||||||
|
{
|
||||||
|
if (wallet)
|
||||||
|
wallet->on_passphrase_request(on_device, passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||||
m_multisig_rescan_info(NULL),
|
m_multisig_rescan_info(NULL),
|
||||||
m_multisig_rescan_k(NULL),
|
m_multisig_rescan_k(NULL),
|
||||||
|
@ -891,7 +912,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||||
m_ringdb(),
|
m_ringdb(),
|
||||||
m_last_block_reward(0),
|
m_last_block_reward(0),
|
||||||
m_encrypt_keys_after_refresh(boost::none),
|
m_encrypt_keys_after_refresh(boost::none),
|
||||||
m_unattended(unattended)
|
m_unattended(unattended),
|
||||||
|
m_device_last_key_image_sync(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,6 +936,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_
|
||||||
return command_line::get_arg(vm, options().hw_device);
|
return command_line::get_arg(vm, options().hw_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm)
|
||||||
|
{
|
||||||
|
return command_line::get_arg(vm, options().hw_device_derivation_path);
|
||||||
|
}
|
||||||
|
|
||||||
void wallet2::init_options(boost::program_options::options_description& desc_params)
|
void wallet2::init_options(boost::program_options::options_description& desc_params)
|
||||||
{
|
{
|
||||||
const options opts{};
|
const options opts{};
|
||||||
|
@ -930,6 +957,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
||||||
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
|
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
|
||||||
command_line::add_arg(desc_params, opts.kdf_rounds);
|
command_line::add_arg(desc_params, opts.kdf_rounds);
|
||||||
command_line::add_arg(desc_params, opts.hw_device);
|
command_line::add_arg(desc_params, opts.hw_device);
|
||||||
|
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
|
||||||
command_line::add_arg(desc_params, opts.tx_notify);
|
command_line::add_arg(desc_params, opts.tx_notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +977,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
||||||
return {nullptr, password_container{}};
|
return {nullptr, password_container{}};
|
||||||
}
|
}
|
||||||
auto wallet = make_basic(vm, unattended, opts, password_prompter);
|
auto wallet = make_basic(vm, unattended, opts, password_prompter);
|
||||||
if (wallet)
|
if (wallet && !wallet_file.empty())
|
||||||
{
|
{
|
||||||
wallet->load(wallet_file, pwd->password());
|
wallet->load(wallet_file, pwd->password());
|
||||||
}
|
}
|
||||||
|
@ -1091,15 +1119,17 @@ bool wallet2::reconnect_device()
|
||||||
hw::device &hwdev = lookup_device(m_device_name);
|
hw::device &hwdev = lookup_device(m_device_name);
|
||||||
hwdev.set_name(m_device_name);
|
hwdev.set_name(m_device_name);
|
||||||
hwdev.set_network_type(m_nettype);
|
hwdev.set_network_type(m_nettype);
|
||||||
|
hwdev.set_derivation_path(m_device_derivation_path);
|
||||||
|
hwdev.set_callback(get_device_callback());
|
||||||
r = hwdev.init();
|
r = hwdev.init();
|
||||||
if (!r){
|
if (!r){
|
||||||
LOG_PRINT_L2("Could not init device");
|
MERROR("Could not init device");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = hwdev.connect();
|
r = hwdev.connect();
|
||||||
if (!r){
|
if (!r){
|
||||||
LOG_PRINT_L2("Could not connect to the device");
|
MERROR("Could not connect to the device");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2998,6 +3028,7 @@ bool wallet2::clear()
|
||||||
m_subaddresses.clear();
|
m_subaddresses.clear();
|
||||||
m_subaddress_labels.clear();
|
m_subaddress_labels.clear();
|
||||||
m_multisig_rounds_passed = 0;
|
m_multisig_rounds_passed = 0;
|
||||||
|
m_device_last_key_image_sync = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3159,6 +3190,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
||||||
value.SetString(m_device_name.c_str(), m_device_name.size());
|
value.SetString(m_device_name.c_str(), m_device_name.size());
|
||||||
json.AddMember("device_name", value, json.GetAllocator());
|
json.AddMember("device_name", value, json.GetAllocator());
|
||||||
|
|
||||||
|
value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size());
|
||||||
|
json.AddMember("device_derivation_path", value, json.GetAllocator());
|
||||||
|
|
||||||
// Serialize the JSON object
|
// Serialize the JSON object
|
||||||
rapidjson::StringBuffer buffer;
|
rapidjson::StringBuffer buffer;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||||
|
@ -3278,6 +3312,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||||
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
||||||
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
||||||
m_device_name = "";
|
m_device_name = "";
|
||||||
|
m_device_derivation_path = "";
|
||||||
m_key_device_type = hw::device::device_type::SOFTWARE;
|
m_key_device_type = hw::device::device_type::SOFTWARE;
|
||||||
encrypted_secret_keys = false;
|
encrypted_secret_keys = false;
|
||||||
}
|
}
|
||||||
|
@ -3445,6 +3480,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||||
m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
|
m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string());
|
||||||
|
m_device_derivation_path = field_device_derivation_path;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3459,6 +3497,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||||
hw::device &hwdev = lookup_device(m_device_name);
|
hw::device &hwdev = lookup_device(m_device_name);
|
||||||
THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
|
THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
|
||||||
hwdev.set_network_type(m_nettype);
|
hwdev.set_network_type(m_nettype);
|
||||||
|
hwdev.set_derivation_path(m_device_derivation_path);
|
||||||
|
hwdev.set_callback(get_device_callback());
|
||||||
THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
|
THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
|
||||||
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
|
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
|
||||||
m_account.set_device(hwdev);
|
m_account.set_device(hwdev);
|
||||||
|
@ -3965,6 +4005,8 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
|
||||||
auto &hwdev = lookup_device(device_name);
|
auto &hwdev = lookup_device(device_name);
|
||||||
hwdev.set_name(device_name);
|
hwdev.set_name(device_name);
|
||||||
hwdev.set_network_type(m_nettype);
|
hwdev.set_network_type(m_nettype);
|
||||||
|
hwdev.set_derivation_path(m_device_derivation_path);
|
||||||
|
hwdev.set_callback(get_device_callback());
|
||||||
|
|
||||||
m_account.create_from_device(hwdev);
|
m_account.create_from_device(hwdev);
|
||||||
m_key_device_type = m_account.get_device().get_type();
|
m_key_device_type = m_account.get_device().get_type();
|
||||||
|
@ -9238,9 +9280,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
||||||
auto & hwdev = get_account().get_device();
|
auto & hwdev = get_account().get_device();
|
||||||
if (!hwdev.has_ki_cold_sync()){
|
CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol");
|
||||||
throw std::invalid_argument("Device does not support cold ki sync protocol");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
|
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
|
||||||
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
|
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
|
||||||
|
@ -9251,7 +9291,11 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
|
||||||
|
|
||||||
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
|
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
|
||||||
|
|
||||||
return import_key_images(ski, 0, spent, unspent);
|
// Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted.
|
||||||
|
uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon());
|
||||||
|
m_device_last_key_image_sync = time(NULL);
|
||||||
|
|
||||||
|
return import_res;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
|
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
|
||||||
|
@ -11985,4 +12029,29 @@ uint64_t wallet2::get_segregation_fork_height() const
|
||||||
void wallet2::generate_genesis(cryptonote::block& b) const {
|
void wallet2::generate_genesis(cryptonote::block& b) const {
|
||||||
cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
|
cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
|
||||||
}
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
wallet_device_callback * wallet2::get_device_callback()
|
||||||
|
{
|
||||||
|
if (!m_device_callback){
|
||||||
|
m_device_callback.reset(new wallet_device_callback(this));
|
||||||
|
}
|
||||||
|
return m_device_callback.get();
|
||||||
|
}//----------------------------------------------------------------------------------------------------
|
||||||
|
void wallet2::on_button_request()
|
||||||
|
{
|
||||||
|
if (0 != m_callback)
|
||||||
|
m_callback->on_button_request();
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void wallet2::on_pin_request(epee::wipeable_string & pin)
|
||||||
|
{
|
||||||
|
if (0 != m_callback)
|
||||||
|
m_callback->on_pin_request(pin);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
|
||||||
|
{
|
||||||
|
if (0 != m_callback)
|
||||||
|
m_callback->on_passphrase_request(on_device, passphrase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,11 +100,26 @@ namespace tools
|
||||||
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||||
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||||
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||||
|
// Device callbacks
|
||||||
|
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) {}
|
||||||
// Common callbacks
|
// Common callbacks
|
||||||
virtual void on_pool_tx_removed(const crypto::hash &txid) {}
|
virtual void on_pool_tx_removed(const crypto::hash &txid) {}
|
||||||
virtual ~i_wallet2_callback() {}
|
virtual ~i_wallet2_callback() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class wallet_device_callback : public hw::i_device_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
wallet_device_callback(wallet2 * wallet): wallet(wallet) {};
|
||||||
|
void on_button_request() override;
|
||||||
|
void on_pin_request(epee::wipeable_string & pin) override;
|
||||||
|
void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) override;
|
||||||
|
private:
|
||||||
|
wallet2 * wallet;
|
||||||
|
};
|
||||||
|
|
||||||
struct tx_dust_policy
|
struct tx_dust_policy
|
||||||
{
|
{
|
||||||
uint64_t dust_threshold;
|
uint64_t dust_threshold;
|
||||||
|
@ -156,6 +171,7 @@ namespace tools
|
||||||
{
|
{
|
||||||
friend class ::Serialization_portability_wallet_Test;
|
friend class ::Serialization_portability_wallet_Test;
|
||||||
friend class wallet_keys_unlocker;
|
friend class wallet_keys_unlocker;
|
||||||
|
friend class wallet_device_callback;
|
||||||
public:
|
public:
|
||||||
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
|
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
|
||||||
|
|
||||||
|
@ -177,6 +193,7 @@ namespace tools
|
||||||
static bool has_testnet_option(const boost::program_options::variables_map& vm);
|
static bool has_testnet_option(const boost::program_options::variables_map& vm);
|
||||||
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
|
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
|
||||||
static std::string device_name_option(const boost::program_options::variables_map& vm);
|
static std::string device_name_option(const boost::program_options::variables_map& vm);
|
||||||
|
static std::string device_derivation_path_option(const boost::program_options::variables_map &vm);
|
||||||
static void init_options(boost::program_options::options_description& desc_params);
|
static void init_options(boost::program_options::options_description& desc_params);
|
||||||
|
|
||||||
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
||||||
|
@ -795,6 +812,7 @@ namespace tools
|
||||||
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
|
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
|
||||||
|
|
||||||
uint64_t get_last_block_reward() const { return m_last_block_reward; }
|
uint64_t get_last_block_reward() const { return m_last_block_reward; }
|
||||||
|
uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; }
|
||||||
|
|
||||||
template <class t_archive>
|
template <class t_archive>
|
||||||
inline void serialize(t_archive &a, const unsigned int ver)
|
inline void serialize(t_archive &a, const unsigned int ver)
|
||||||
|
@ -903,6 +921,9 @@ namespace tools
|
||||||
if(ver < 26)
|
if(ver < 26)
|
||||||
return;
|
return;
|
||||||
a & m_tx_device;
|
a & m_tx_device;
|
||||||
|
if(ver < 27)
|
||||||
|
return;
|
||||||
|
a & m_device_last_key_image_sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -964,6 +985,8 @@ namespace tools
|
||||||
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
|
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
|
||||||
const std::string & device_name() const { return m_device_name; }
|
const std::string & device_name() const { return m_device_name; }
|
||||||
void device_name(const std::string & device_name) { m_device_name = device_name; }
|
void device_name(const std::string & device_name) { m_device_name = device_name; }
|
||||||
|
const std::string & device_derivation_path() const { return m_device_derivation_path; }
|
||||||
|
void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; }
|
||||||
|
|
||||||
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
|
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
|
||||||
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
|
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
|
||||||
|
@ -1287,6 +1310,11 @@ namespace tools
|
||||||
void setup_new_blockchain();
|
void setup_new_blockchain();
|
||||||
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
|
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
|
||||||
|
|
||||||
|
wallet_device_callback * get_device_callback();
|
||||||
|
void on_button_request();
|
||||||
|
void on_pin_request(epee::wipeable_string & pin);
|
||||||
|
void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase);
|
||||||
|
|
||||||
cryptonote::account_base m_account;
|
cryptonote::account_base m_account;
|
||||||
boost::optional<epee::net_utils::http::login> m_daemon_login;
|
boost::optional<epee::net_utils::http::login> m_daemon_login;
|
||||||
std::string m_daemon_address;
|
std::string m_daemon_address;
|
||||||
|
@ -1365,6 +1393,8 @@ namespace tools
|
||||||
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
|
||||||
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
|
||||||
std::string m_device_name;
|
std::string m_device_name;
|
||||||
|
std::string m_device_derivation_path;
|
||||||
|
uint64_t m_device_last_key_image_sync;
|
||||||
|
|
||||||
// Aux transaction data from device
|
// Aux transaction data from device
|
||||||
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
std::unordered_map<crypto::hash, std::string> m_tx_device;
|
||||||
|
@ -1398,9 +1428,10 @@ namespace tools
|
||||||
bool m_devices_registered;
|
bool m_devices_registered;
|
||||||
|
|
||||||
std::shared_ptr<tools::Notify> m_tx_notify;
|
std::shared_ptr<tools::Notify> m_tx_notify;
|
||||||
|
std::unique_ptr<wallet_device_callback> m_device_callback;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BOOST_CLASS_VERSION(tools::wallet2, 26)
|
BOOST_CLASS_VERSION(tools::wallet2, 27)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
|
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||||
|
|
|
@ -219,6 +219,14 @@ namespace tools
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
struct password_entry_failed : public wallet_runtime_error
|
||||||
|
{
|
||||||
|
explicit password_entry_failed(std::string&& loc, const std::string &msg = "Password entry failed")
|
||||||
|
: wallet_runtime_error(std::move(loc), msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
const char* const file_error_messages[] = {
|
const char* const file_error_messages[] = {
|
||||||
"file already exists",
|
"file already exists",
|
||||||
"file not found",
|
"file not found",
|
||||||
|
|
Loading…
Reference in New Issue