Overhaul tx prefix comms; rename some overloaded methods

Tx prefix communication was missing some needed information on the tx
type, and was a little inefficient.  This redoes the protocol to send
the tx type info and then the entire prefix (rather than starting from a
few bytes in).  It also changes how we number requests and signal the
final piece of a multi-piece transmission.
This commit is contained in:
Jason Rhinelander 2020-07-19 23:29:01 -03:00
parent d044ecd391
commit ad7e63a04f
4 changed files with 63 additions and 58 deletions

View File

@ -35,6 +35,7 @@
#include "cryptonote_basic/subaddress_index.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "common/lock.h"
#include "common/varint.h"
namespace hw {
@ -1443,13 +1444,26 @@ namespace hw {
void device_ledger::get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) {
auto locks = tools::unique_locks(device_locker, command_locker);
int pref_length = 0, pref_offset = 0, offset = 0;
#ifdef DEBUG_HWDEVICE
crypto::hash h_x;
this->controle_device->get_transaction_prefix_hash(tx,h_x);
MDEBUG("get_transaction_prefix_hash [[IN]] h_x/1 "<<h_x);
#endif
// As of protocol version 4, we send:
// - tx version
// - tx type (transfer, registration, stake, lns)
// - tx lock time (if the tx has multiple lock times this will be the largest one)
// We then wait for confirmation from the device, and if we get it we continue by sending the
// data in chunks of 256 bytes at a time. The last chunk will have a p2 subparameter of ff;
// otherwise the p2 subparameters are in order.
//
// In terms of subcommand parameters, then, this goes:
// (1) -- send version, type, locktime
// (2,0) -- 256 bytes and more to come
// (2,1) -- another 256 bytes and more to come
// ...
// (2,ff) -- last 256 bytes. (Note that this could happen instead of (2,0), above).
std::string tx_prefix;
try {
@ -1458,55 +1472,45 @@ namespace hw {
ASSERT_MES_AND_THROW("unable to serialize transaction prefix: " << e.what());
}
pref_length = tx_prefix.size();
const unsigned char* pref = reinterpret_cast<const unsigned char*>(tx_prefix.c_str());
// Safety check because this is the biggest the protocol can support (it's also an insanely
// large tx prefix).
CHECK_AND_ASSERT_THROW_MES(tx_prefix.size() <= 256*256, "Transaction prefix too big.");
offset = set_command_header_noopt(INS_PREFIX_HASH,1);
pref_offset = 0;
unsigned char v;
unsigned char* send = this->buffer_send + set_command_header_noopt(INS_PREFIX_HASH, 1);
//version as varint
do {
v = pref[pref_offset];
this->buffer_send[offset] = v;
offset += 1;
pref_offset += 1;
} while (v&0x80);
// version as varint
tools::write_varint(send, static_cast<std::underlying_type_t<cryptonote::txversion>>(tx.version));
//locktime as var int
do {
v = pref[pref_offset];
this->buffer_send[offset] = v;
offset += 1;
pref_offset += 1;
} while (v&0x80);
// transaction type as varint
tools::write_varint(send, static_cast<std::underlying_type_t<cryptonote::txtype>>(tx.type));
this->buffer_send[4] = offset-5;
this->length_send = offset;
// Transactions can have multiple unlock times; find the longest one and send that
uint64_t max_unlock = 0;
for (size_t i = 0; i < tx.vout.size(); i++)
max_unlock = std::max(max_unlock, tx.get_unlock_time(i));
tools::write_varint(send, max_unlock);
this->length_send = send - this->buffer_send;
this->buffer_send[4] = length_send - 5;
this->exchange_wait_on_input();
//hash remains
int cnt = 0;
while (pref_offset < pref_length) {
int len;
cnt++;
offset = set_command_header(INS_PREFIX_HASH,2,cnt);
len = pref_length - pref_offset;
//options
if (len > (BUFFER_SEND_SIZE-7)) {
len = BUFFER_SEND_SIZE-7;
this->buffer_send[offset] = 0x80;
} else {
this->buffer_send[offset] = 0x00;
}
offset += 1;
//send chunk
memmove(&this->buffer_send[offset], pref+pref_offset, len);
offset += len;
pref_offset += len;
this->buffer_send[4] = offset-5;
this->length_send = offset;
this->exchange();
// hash the full prefix
uint8_t cnt = 0;
for (size_t pos = 0; pos < tx_prefix.size(); pos += 256, ++cnt) {
size_t size = tx_prefix.size() - pos;
if (size > 256)
size = 256;
else
cnt = 0xff;
size_t header_size = set_command_header_noopt(INS_PREFIX_HASH, 2, cnt);
assert(header_size + 256 <= BUFFER_SEND_SIZE);
std::memcpy(this->buffer_send + header_size, &tx_prefix[pos], size);
this->buffer_send[4] = size;
this->length_send = header_size + size;
exchange();
}
memmove(h.data, &this->buffer_recv[0], 32);

View File

@ -4400,9 +4400,10 @@ std::optional<epee::wipeable_string> simple_wallet::new_device_wallet(const boos
message_writer() << tr("Your hardware device will ask for permission to export your wallet view key.\n"
"This is optional, but will significantly improve wallet syncing speed. Your\n"
"spend key (needed to spend funds) does not leave the device.");
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
message_writer(epee::console_color_white, true) << tr("Generated new wallet on hw device: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
m_wallet->restore_from_device(
m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file,
[](const std::string& msg) { message_writer(epee::console_color_green, true) << msg; });
message_writer(epee::console_color_white, true) << tr("Finished setting up wallet from hw device");
}
catch (const std::exception& e)
{
@ -4508,7 +4509,7 @@ std::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::pro
prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str();
else
prefix = tr("Opened wallet");
message_writer(epee::console_color_white, true) <<
message_writer(epee::console_color_green, true) <<
prefix << ": " << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
if (m_wallet->get_account().get_device()) {
message_writer(epee::console_color_white, true) << "Wallet is on device: " << m_wallet->get_account().get_device().get_name();

View File

@ -4937,13 +4937,8 @@ void wallet2::generate(const fs::path& wallet_, const epee::wipeable_string& pas
store();
}
/*!
* \brief Creates a wallet from a device
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \param device_name device string address
*/
void wallet2::restore(const fs::path& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
void wallet2::restore_from_device(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name,
bool create_address_file, std::function<void(std::string msg)> progress_callback)
{
clear();
prepare_file_names(wallet_);
@ -4963,6 +4958,8 @@ void wallet2::restore(const fs::path& wallet_, const epee::wipeable_string& pass
m_account.create_from_device(hwdev);
init_type(m_account.get_device().get_type());
setup_keys(password);
if (progress_callback)
progress_callback(tr("Retrieved wallet address from device: ") + m_account.get_public_address_str(m_nettype));
m_device_name = device_name;
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
@ -4972,6 +4969,7 @@ void wallet2::restore(const fs::path& wallet_, const epee::wipeable_string& pass
m_subaddress_lookahead_major = 5;
m_subaddress_lookahead_minor = 20;
}
progress_callback(tr("Setting up account and subaddresses"));
setup_new_blockchain();
if (!wallet_.empty()) {
store();

View File

@ -519,13 +519,15 @@ private:
const cryptonote::account_public_address &account_public_address,
const crypto::secret_key& viewkey = crypto::secret_key(), bool create_address_file = true);
/*!
* \brief Restore a wallet hold by an HW.
* \brief Restore a wallet from a hardware device
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \param device_name name of HW to use
* \param create_address_file Whether to create an address file
* \param status_callback callback to invoke with progress messages to display to the user
*/
void restore(const fs::path& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = true);
void restore_from_device(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name,
bool create_address_file = false, std::function<void(std::string msg)> status_callback = {});
/*!
* \brief Creates a multisig wallet