From 15fbd963d75e6ecc7df3cd5e6e74ad96878b2bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Feb 2023 11:00:04 +0100 Subject: [PATCH 001/129] Add boost, openssl and hidapi to Qt project file --- wallet/wallet.pro | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wallet/wallet.pro b/wallet/wallet.pro index 0905c116d..664ae1b59 100644 --- a/wallet/wallet.pro +++ b/wallet/wallet.pro @@ -18,6 +18,18 @@ mac { QMAKE_INFO_PLIST = $${NEBLIO_ROOT}/wallet/qt/res/Info.plist } +_BOOST_PATH = /home/vacuumlabs/projects/vl/neblio/neblio/boost_1_65_1 +INCLUDEPATH += "$${_BOOST_PATH}" +LIBS += -L$${_BOOST_PATH}/stage/lib + +_OPENSSL_PATH = /home/vacuumlabs/projects/vl/neblio/neblio/openssl_build +INCLUDEPATH += "$${_OPENSSL_PATH}/include" +LIBS += -L$${_OPENSSL_PATH}/lib + +_HIDAPI_PATH = /usr/local/ +INCLUDEPATH += "$${_HIDAPI_PATH}/include/hidapi" +LIBS += -L$${_HIDAPI_PATH}/lib/libhidapi-libusb.so +LIBS += -lhidapi-libusb # use: qmake "NEBLIO_REST=1" contains(NEBLIO_REST, 1) { From 4cc22cb669f2a0efa2acf0ee5e53c51ac19abbd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Feb 2023 12:03:31 +0100 Subject: [PATCH 002/129] Add ledgercpp source files --- .gitignore | 2 + wallet/ledger/comm.h | 23 ++++++ wallet/ledger/error.cpp | 23 ++++++ wallet/ledger/error.h | 21 ++++++ wallet/ledger/hid_device.cpp | 141 +++++++++++++++++++++++++++++++++++ wallet/ledger/hid_device.h | 29 +++++++ wallet/ledger/ledger.cpp | 39 ++++++++++ wallet/ledger/ledger.h | 33 ++++++++ wallet/ledger/transport.cpp | 59 +++++++++++++++ wallet/ledger/transport.h | 33 ++++++++ wallet/ledger/utils.cpp | 27 +++++++ wallet/ledger/utils.h | 18 +++++ wallet/wallet.pri | 11 +++ 13 files changed, 459 insertions(+) create mode 100644 wallet/ledger/comm.h create mode 100644 wallet/ledger/error.cpp create mode 100644 wallet/ledger/error.h create mode 100644 wallet/ledger/hid_device.cpp create mode 100644 wallet/ledger/hid_device.h create mode 100644 wallet/ledger/ledger.cpp create mode 100644 wallet/ledger/ledger.h create mode 100644 wallet/ledger/transport.cpp create mode 100644 wallet/ledger/transport.h create mode 100644 wallet/ledger/utils.cpp create mode 100644 wallet/ledger/utils.h diff --git a/.gitignore b/.gitignore index 5a3961704..86ccad32d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ wallet/leveldb/build_config.mk debug .neblio release/ +.qtc_clangd +.vscode #compilation and Qt preprocessor part *.qm diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h new file mode 100644 index 000000000..99ac9a2e0 --- /dev/null +++ b/wallet/ledger/comm.h @@ -0,0 +1,23 @@ +#ifndef _LEDGER_COMM +#define _LEDGER_COMM 1 + +#include +#include + +#include "error.h" + +namespace ledger { +class Comm +{ +public: + virtual ~Comm() = default; + + virtual Error open() = 0; + virtual int send(const std::vector& data) = 0; + virtual int recv(std::vector& rdata) = 0; + virtual void close() = 0; + [[nodiscard]] virtual bool is_open() const = 0; +}; +} // namespace ledger + +#endif diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp new file mode 100644 index 000000000..7ff92aec2 --- /dev/null +++ b/wallet/ledger/error.cpp @@ -0,0 +1,23 @@ +#include "error.h" + +namespace ledger { +std::string error_message(Error code) +{ + switch (code) { + case Error::SUCCESS: + return "Ok"; + case Error::DEVICE_NOT_FOUND: + return "Ledger Not Found"; + case Error::DEVICE_OPEN_FAIL: + return "Failed to open Ledger"; + case Error::DEVICE_DATA_SEND_FAIL: + return "Failed to send data to Ledger"; + case Error::DEVICE_DATA_RECV_FAIL: + return "Failed to receive data from Ledger"; + case Error::APDU_INVALID_CMD: + return "Invalid Ledger data"; + default: + return "Unrecognized error"; + } +} +} // namespace ledger diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h new file mode 100644 index 000000000..a221ce5e1 --- /dev/null +++ b/wallet/ledger/error.h @@ -0,0 +1,21 @@ +#ifndef _LEDGER_ERROR +#define _LEDGER_ERROR 1 + +#include + +namespace ledger { +enum class Error +{ + SUCCESS = 0, + DEVICE_NOT_FOUND, + DEVICE_OPEN_FAIL, + DEVICE_DATA_SEND_FAIL, + DEVICE_DATA_RECV_FAIL, + APDU_INVALID_CMD, +}; + +std::string error_message(Error code); + +} // namespace ledger + +#endif diff --git a/wallet/ledger/hid_device.cpp b/wallet/ledger/hid_device.cpp new file mode 100644 index 000000000..bdc2b7305 --- /dev/null +++ b/wallet/ledger/hid_device.cpp @@ -0,0 +1,141 @@ +#include "hid_device.h" +#include "error.h" +#include "utils.h" + +#include + +namespace ledger { +Error HID::open() +{ + if (!opened_) { + auto devices = enumerate_devices(vendor_id_); + if (devices.empty()) { + return Error::DEVICE_NOT_FOUND; + } + + path_ = devices.at(0); + device_ = hid_open_path(path_.c_str()); + if (!device_) { + return Error::DEVICE_OPEN_FAIL; + } + + hid_set_nonblocking(device_, true); + + opened_ = true; + } + + return Error::SUCCESS; +} + +int HID::send(const std::vector& data) +{ + if (data.empty()) + return -1; + + auto data_new = utils::int_to_bytes(data.size(), 2); + data_new.insert(data_new.end(), data.begin(), data.end()); + + size_t offset = 0; + size_t seq_idx = 0; + size_t length = 0; + + while (offset < data_new.size()) { + // Header: channel (0x0101), tag (0x05), sequence index + std::vector header{0x01, 0x01, 0x05}; + + auto seq_idx_bytes = utils::int_to_bytes(seq_idx, 2); + header.insert(header.end(), seq_idx_bytes.begin(), seq_idx_bytes.end()); + + std::vector::iterator it; + if (data_new.size() - offset < 64 - header.size()) { + it = data_new.end(); + } else { + it = data_new.begin() + offset + 64 - header.size(); + } + + std::vector data_chunk{data_new.begin() + offset, it}; + data_chunk.insert(data_chunk.begin(), header.begin(), header.end()); + data_chunk.insert(data_chunk.begin(), 0x00); + + if (hid_write(device_, data_chunk.data(), data_chunk.size()) == -1) + return -1; + + length += data_chunk.size(); + offset += 64 - header.size(); + seq_idx += 1; + } + + return length; +} + +int HID::recv(std::vector& rdata) +{ + int seq_idx = 0; + uint8_t buf[64]; + + hid_set_nonblocking(device_, false); + if (hid_read_timeout(device_, buf, sizeof(buf), timeout_ms_) <= 0) + return -1; + hid_set_nonblocking(device_, true); + + std::vector data_chunk(buf, buf + sizeof(buf)); + + assert(data_chunk[0] == 0x01); + assert(data_chunk[1] == 0x01); + assert(data_chunk[2] == 0x05); + + auto seq_idx_bytes = utils::int_to_bytes(seq_idx, 2); + assert(seq_idx_bytes[0] == data_chunk[3]); + assert(seq_idx_bytes[1] == data_chunk[4]); + + auto data_len = + utils::bytes_to_int(std::vector(data_chunk.begin() + 5, data_chunk.begin() + 7)); + std::vector data(data_chunk.begin() + 7, data_chunk.end()); + + while (data.size() < data_len) { + uint8_t read_bytes[64]; + if (hid_read_timeout(device_, read_bytes, sizeof(read_bytes), 1000) == -1) + return -1; + std::vector tmp(read_bytes, read_bytes + sizeof(read_bytes)); + data.insert(data.end(), tmp.begin() + 5, tmp.end()); + } + + auto sw = + utils::bytes_to_int(std::vector(data.begin() + data_len - 2, data.begin() + data_len)); + rdata = std::vector(data.begin(), data.begin() + data_len - 2); + + return sw; +} + +void HID::close() noexcept +{ + if (opened_) { + hid_close(device_); + opened_ = false; + } + hid_exit(); +} + +bool HID::is_open() const { return opened_; } + +std::vector HID::enumerate_devices(unsigned short vendor_id) noexcept +{ + std::vector devices; + + struct hid_device_info *devs, *cur_dev; + + devs = hid_enumerate(vendor_id, 0x0); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->interface_number == 0 || + // MacOS specific + cur_dev->usage_page == 0xffa0) { + devices.emplace_back(cur_dev->path); + } + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); + + return devices; +} +} // namespace ledger diff --git a/wallet/ledger/hid_device.h b/wallet/ledger/hid_device.h new file mode 100644 index 000000000..df93d1a14 --- /dev/null +++ b/wallet/ledger/hid_device.h @@ -0,0 +1,29 @@ +#ifndef _LEDGER_HID_DEVICE +#define _LEDGER_HID_DEVICE 1 + +#include "comm.h" + +#include + +namespace ledger { +class HID final : public Comm +{ +public: + Error open() override; + int send(const std::vector& data) override; + int recv(std::vector& rdata) override; + void close() noexcept override; + [[nodiscard]] bool is_open() const override; + +private: + static std::vector enumerate_devices(unsigned short vendor_id = 0x2c97) noexcept; + + hid_device* device_ = nullptr; + std::string path_ = {}; + bool opened_ = false; + const int timeout_ms_ = 60 * 1000; + unsigned short vendor_id_ = 0x2c97; // Ledger Vendor ID +}; +} // namespace ledger + +#endif diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp new file mode 100644 index 000000000..a8d8eb698 --- /dev/null +++ b/wallet/ledger/ledger.cpp @@ -0,0 +1,39 @@ +#include "ledger.h" +#include "error.h" +#include "utils.h" + +namespace ledger { +Ledger::Ledger() { this->transport_ = std::unique_ptr(new Transport(Transport::TransportType::HID)); } + +Ledger::~Ledger() { transport_->close(); } + +Error Ledger::open() { return transport_->open(); } + +std::tuple> Ledger::get_public_key(uint32_t account, bool confirm) +{ + auto payload = std::vector(); + // path length + payload.push_back(3); + // m/44'/146'/[account]' derivation path + utils::append_vector(payload, utils::int_to_bytes(utils::hardened(44), 4)); + utils::append_vector(payload, utils::int_to_bytes(utils::hardened(146), 4)); + utils::append_vector(payload, utils::int_to_bytes(utils::hardened(account), 4)); + + auto [err, buffer] = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); + if (err != Error::SUCCESS) + return {err, {}}; + return {err, std::vector(buffer.begin() + 1, buffer.end())}; +} + +std::tuple> Ledger::sign(uint32_t account, const std::vector& msg) +{ + auto payload = utils::int_to_bytes(account, 4); + payload.insert(payload.end(), msg.begin(), msg.end()); + auto [err, buffer] = transport_->exchange(APDU::CLA, APDU::INS_SIGN, 0x00, 0x00, payload); + if (err != Error::SUCCESS) + return {err, {}}; + return {err, std::vector(buffer.begin() + 1, buffer.end())}; +} + +void Ledger::close() { return transport_->close(); } +} // namespace ledger diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h new file mode 100644 index 000000000..e8b8e615c --- /dev/null +++ b/wallet/ledger/ledger.h @@ -0,0 +1,33 @@ +#ifndef _LEDGER_LEDGER +#define _LEDGER_LEDGER 1 + +#include "transport.h" + +namespace ledger { +class Ledger +{ + enum APDU : uint8_t + { + CLA = 0xe0, + INS_GET_APP_CONFIGURATION = 0x01, + INS_GET_PUBLIC_KEY = 0x40, + INS_SIGN = 0x03, + }; + +public: + Ledger(); + ~Ledger(); + + Error open(); + + std::tuple> get_public_key(uint32_t account, bool confirm = false); + std::tuple> sign(uint32_t account, const std::vector& msg); + + void close(); + +private: + std::unique_ptr transport_; +}; +} // namespace ledger + +#endif diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp new file mode 100644 index 000000000..1123a8c63 --- /dev/null +++ b/wallet/ledger/transport.cpp @@ -0,0 +1,59 @@ +#include "transport.h" +#include "error.h" +#include "hid_device.h" + +namespace ledger { +Transport::Transport(TransportType type) +{ + switch (type) { + case TransportType::HID: + comm_ = std::unique_ptr(); + break; + } +} + +Error Transport::open() { return comm_->open(); } + +std::tuple> +Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata) +{ + int length = this->send(cla, ins, p1, p2, cdata); + if (length < 0) + return {Error::DEVICE_DATA_SEND_FAIL, {}}; + + std::vector buffer; + int sw = this->recv(buffer); + if (sw < 0) + return {Error::DEVICE_DATA_RECV_FAIL, {}}; + + if (sw != 0x9000) + return {Error::APDU_INVALID_CMD, {}}; + + return {Error::SUCCESS, buffer}; +} + +void Transport::close() noexcept { return comm_->close(); } + +int Transport::send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata) +{ + if (!comm_->is_open()) + return -1; + + auto header = apdu_header(cla, ins, p1, p2, cdata.size()); + header.insert(header.end(), cdata.begin(), cdata.end()); + return comm_->send(header); +} + +int Transport::recv(std::vector& rdata) +{ + if (!comm_->is_open()) + return -1; + + return comm_->recv(rdata); +} + +std::vector Transport::apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) +{ + return std::vector{cla, ins, p1, p2, lc}; +} +} // namespace ledger diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h new file mode 100644 index 000000000..d429df23f --- /dev/null +++ b/wallet/ledger/transport.h @@ -0,0 +1,33 @@ +#ifndef _LEDGER_TRANSPORT +#define _LEDGER_TRANSPORT 1 + +#include "comm.h" + +#include + +namespace ledger { +class Transport +{ +public: + enum class TransportType : int + { + HID = 0, + }; + + Transport(TransportType type); + Error open(); + std::tuple> + exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata); + void close() noexcept; + +private: + int send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata); + int recv(std::vector& rdata); + static std::vector apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, + uint8_t lc); + + std::unique_ptr comm_; +}; +} // namespace ledger + +#endif diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp new file mode 100644 index 000000000..d2fea0be5 --- /dev/null +++ b/wallet/ledger/utils.cpp @@ -0,0 +1,27 @@ +#include "utils.h" + +namespace ledger::utils { +int bytes_to_int(const std::vector& bytes) +{ + int value = 0; + for (uint8_t byte : bytes) { + value = (value << 8) + byte; + } + return value; +} + +std::vector int_to_bytes(uint32_t n, uint32_t length) +{ + std::vector bytes; + bytes.reserve(length); + for (auto i = 0; i < length; i++) { + bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); + } + return bytes; +} + +uint32_t hardened(uint32_t n) +{ + return n | 0x80000000; +} +} // namespace ledger::utils diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h new file mode 100644 index 000000000..dbee56004 --- /dev/null +++ b/wallet/ledger/utils.h @@ -0,0 +1,18 @@ +#ifndef _LEDGER_UTILS +#define _LEDGER_UTILS 1 + +#include +#include + +namespace ledger::utils { +int bytes_to_int(const std::vector& bytes); +std::vector int_to_bytes(unsigned int n, unsigned int length); +template +void append_vector(std::vector& destination, std::vector source) +{ + destination.insert(destination.end(), source.begin(), source.end()); +} +uint32_t hardened(uint32_t n); +} // namespace ledger::utils + +#endif diff --git a/wallet/wallet.pri b/wallet/wallet.pri index 1df00f0c4..4d91178d7 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -1,6 +1,12 @@ # Input DEPENDPATH += . json qt HEADERS += qt/bitcoingui.h \ + ledger/comm.h \ + ledger/error.h \ + ledger/hid_device.h \ + ledger/ledger.h \ + ledger/transport.h \ + ledger/utils.h \ qt/transactiontablemodel.h \ qt/addresstablemodel.h \ qt/optionsdialog.h \ @@ -200,6 +206,11 @@ HEADERS += \ SOURCES += qt/bitcoin.cpp \ + ledger/error.cpp \ + ledger/hid_device.cpp \ + ledger/ledger.cpp \ + ledger/transport.cpp \ + ledger/utils.cpp \ qt/bitcoingui.cpp \ qt/transactiontablemodel.cpp \ qt/addresstablemodel.cpp \ From 8b9edd739c21669d82063def8a24592f4e54a9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Feb 2023 12:16:06 +0100 Subject: [PATCH 003/129] Add ledgerKeys map to wallet --- wallet/key.h | 2 ++ wallet/wallet.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/wallet/key.h b/wallet/key.h index 011d52ce1..73dca0d10 100644 --- a/wallet/key.h +++ b/wallet/key.h @@ -84,6 +84,8 @@ class CPubKey IMPLEMENT_SERIALIZE(READWRITE(vchPubKey);) + void SetRaw(std::vector rawKey) {vchPubKey = rawKey;} + CKeyID GetID() const { return CKeyID(Hash160(vchPubKey)); } uint256 GetHash() const { return Hash(vchPubKey.begin(), vchPubKey.end()); } diff --git a/wallet/wallet.h b/wallet/wallet.h index 4bea082aa..25e691845 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -426,6 +426,57 @@ class CWallet : public CCryptoKeyStore CBitcoinAddress getNewAddress(const std::string& label); CBitcoinAddress getNewStakingAddress(const std::string& label); CAmount GetStakingBalance(const ITxDB& txdb, bool fIncludeColdStaking) const; + + mutable CCriticalSection cs_LedgerKeyStore; + + // TODO GK - LedgerKeyStore class? (this is copied from keystore) + std::map ledgerKeys; + bool HaveLedgerKey(const CKeyID &address) const + { + bool result; + { + LOCK(cs_LedgerKeyStore); + result = (ledgerKeys.count(address) > 0); + } + return result; + } + + bool AddLedgerKey(const CPubKey& key) + { + { + LOCK(cs_LedgerKeyStore); + ledgerKeys[key.GetID()] = key; + } + return true; + } + + void GetLedgerKeys(std::set &setAddress) const + { + setAddress.clear(); + { + LOCK(cs_LedgerKeyStore); + std::map ::const_iterator mi = ledgerKeys.begin(); + while (mi != ledgerKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + } + + bool GetLedgerKey(const CKeyID &address, CPubKey &pubKeyOut) const + { + { + LOCK(cs_LedgerKeyStore); + std::map ::const_iterator mi = ledgerKeys.find(address); + if (mi != ledgerKeys.end()) + { + pubKeyOut.SetRaw((*mi).second.Raw()); + return true; + } + } + return false; + } }; /** A key allocated from the key pool. */ From ea87b6b467d4fb112994d0896b7368bb41b717a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Feb 2023 12:36:52 +0100 Subject: [PATCH 004/129] Add addLedgerAddress button (PoC) --- wallet/qt/addressbookpage.cpp | 13 +++++++ wallet/qt/addressbookpage.h | 2 ++ wallet/qt/addresstablemodel.cpp | 63 ++++++++++++++++++++++++++++----- wallet/qt/addresstablemodel.h | 9 +++-- wallet/qt/coldstakingpage.cpp | 4 +-- wallet/qt/editaddressdialog.cpp | 17 ++++++++- wallet/qt/editaddressdialog.h | 3 +- wallet/qt/ui_addressbookpage.h | 13 +++++++ wallet/qt/walletmodel.cpp | 8 ++--- wallet/qt/walletmodel.h | 2 +- wallet/script.cpp | 25 ++++++++++++- wallet/wallet.cpp | 4 +-- wallet/wallet.h | 4 +-- wallet/wallet_ismine.h | 10 +++--- 14 files changed, 148 insertions(+), 29 deletions(-) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index ce1bddc37..887d4b9d3 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -227,6 +227,19 @@ void AddressBookPage::on_newAddressButton_clicked() } } +void AddressBookPage::on_newLedgerAddressButton_clicked() +{ + if(!model) + return; + // TODO GK - hide button in sending tab + EditAddressDialog dlg(EditAddressDialog::NewReceivingLedgerAddress, this); + dlg.setModel(model); + if(dlg.exec()) + { + newAddressToSelect = dlg.getAddress(); + } +} + void AddressBookPage::on_deleteButton_clicked() { QTableView *table = ui->tableView; diff --git a/wallet/qt/addressbookpage.h b/wallet/qt/addressbookpage.h index 6524dbad5..94ac3c4c5 100644 --- a/wallet/qt/addressbookpage.h +++ b/wallet/qt/addressbookpage.h @@ -77,6 +77,8 @@ private slots: /** New entry/entries were added to address table */ void selectNewAddress(const QModelIndex &parent, int begin, int end); + void on_newLedgerAddressButton_clicked(); + signals: void signMessage(QString addr); void verifyMessage(QString addr); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 3e68ad941..2eaded669 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -1,5 +1,6 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "ledger/ledger.h" #include "walletmodel.h" #include "base58.h" @@ -10,13 +11,15 @@ const QString AddressTableModel::Send = "S"; const QString AddressTableModel::Receive = "R"; +const QString AddressTableModel::ReceiveLedger = "RL"; struct AddressTableEntry { enum Type { Sending, - Receiving + Receiving, + ReceivingLedger }; Type type; @@ -40,6 +43,21 @@ struct AddressTableEntryLessThan bool operator()(const QString& a, const AddressTableEntry& b) const { return a < b.address; } }; +AddressTableEntry::Type GetEntryType(isminetype fMine) +{ + if (fMine == isminetype::ISMINE_LEDGER) + { + return AddressTableEntry::ReceivingLedger; + } + + if (fMine) + { + return AddressTableEntry::Receiving; + } + + return AddressTableEntry::Sending; +} + // Private implementation class AddressTablePriv { @@ -58,9 +76,9 @@ class AddressTablePriv for (const auto& item : addressBookMap) { const CBitcoinAddress& address = item.first; const std::string& strName = item.second.name; - bool fMine = IsMine(*wallet, address.Get()) != isminetype::ISMINE_NO; + isminetype fMine = IsMine(*wallet, address.Get()); cachedAddressTable.append(AddressTableEntry( - fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending, + GetEntryType(fMine), QString::fromStdString(strName), QString::fromStdString(address.ToString()))); } } @@ -68,7 +86,7 @@ class AddressTablePriv std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); } - void updateEntry(const QString& address, const QString& label, bool isMine, + void updateEntry(const QString& address, const QString& label, isminetype isMine, const QString& /*purpose*/, int status) { // Find address / label in model @@ -79,8 +97,7 @@ class AddressTablePriv int lowerIndex = (lower - cachedAddressTable.begin()); int upperIndex = (upper - cachedAddressTable.begin()); bool inModel = (lower != upper); - AddressTableEntry::Type newEntryType = - isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; + AddressTableEntry::Type newEntryType = GetEntryType(isMine); switch (status) { case CT_NEW: @@ -134,7 +151,7 @@ class AddressTablePriv AddressTableModel::AddressTableModel(CWallet* walletIn, WalletModel* parent) : QAbstractTableModel(parent), walletModel(parent), wallet(walletIn), priv(0) { - columns << tr("Label") << tr("Address"); + columns << tr("Is ledger") << tr("Label") << tr("Address"); priv = new AddressTablePriv(walletIn, this); priv->refreshAddressTable(); } @@ -162,6 +179,8 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { + case IsLedger: + return rec->type == AddressTableEntry::ReceivingLedger ? tr("Ledger") : ""; case Label: if (rec->label.isEmpty() && role == Qt::DisplayRole) { return tr("(no label)"); @@ -295,7 +314,7 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex& par } } -void AddressTableModel::updateEntry(const QString& address, const QString& label, bool isMine, +void AddressTableModel::updateEntry(const QString& address, const QString& label, isminetype isMine, const QString& purpose, int status) { // Update address book model from Bitcoin core @@ -335,6 +354,34 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con return QString(); } strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + } else if (type == ReceiveLedger) { + ledger::Ledger l; + auto e = l.open(); + if (e != ledger::Error::SUCCESS) { + // TODO GK - handle error + return QString(); + } + + auto result = l.get_public_key(0, true); + if (std::get<0>(result) != ledger::Error::SUCCESS) { + // TODO GK - handle error + return QString(); + } + auto resultData = std::get<1>(result); + + auto pubKeyLen = (int)resultData[0] * 16; + + auto pubKeyStart = resultData.begin(); + auto pubKeyEnd = pubKeyStart + pubKeyLen + 1; + + std::vector pubKey(pubKeyLen + 1); + copy(pubKeyStart, pubKeyEnd, pubKey.begin()); + + auto myKeyStr = std::string(pubKey.begin(), pubKey.end()); + + CPubKey cpubkey(pubKey); + wallet->AddLedgerKey(cpubkey); + strAddress = CBitcoinAddress(cpubkey.GetID()).ToString(); } else { return QString(); } diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index b5d04302e..408483079 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -3,6 +3,7 @@ #include #include +#include "wallet_ismine.h" class AddressTablePriv; class CWallet; @@ -20,8 +21,9 @@ class AddressTableModel : public QAbstractTableModel enum ColumnIndex { - Label = 0, /**< User specified label */ - Address = 1 /**< Bitcoin address */ + IsLedger = 0, + Label = 1, /**< User specified label */ + Address = 2 /**< Bitcoin address */ }; enum RoleIndex @@ -42,6 +44,7 @@ class AddressTableModel : public QAbstractTableModel static const QString Send; /**< Specifies send address */ static const QString Receive; /**< Specifies receive address */ + static const QString ReceiveLedger; /**< Specifies Ledger address */ /** @name Methods overridden from QAbstractTableModel @{*/ @@ -91,7 +94,7 @@ class AddressTableModel : public QAbstractTableModel public slots: /* Update address list from core. */ - void updateEntry(const QString& address, const QString& label, bool isMine, const QString& purpose, + void updateEntry(const QString& address, const QString& label, isminetype isMine, const QString& purpose, int status); friend class AddressTablePriv; diff --git a/wallet/qt/coldstakingpage.cpp b/wallet/qt/coldstakingpage.cpp index a8357f1bb..ffae06ae1 100644 --- a/wallet/qt/coldstakingpage.cpp +++ b/wallet/qt/coldstakingpage.cpp @@ -1,4 +1,4 @@ -#include "coldstakingpage.h" +#include "coldstakingpage.h" #include "bitcoinunits.h" #include "checkpoints.h" @@ -167,7 +167,7 @@ void ColdStakingPage::setWalletModel(WalletModel* wModel) notifyWalletConnection = model->getWalletModel()->getWallet()->NotifyAddressBookChanged.connect( [this](CWallet* /*wallet*/, const CTxDestination& /*address*/, const std::string& /*label*/, - bool /*isMine*/, const std::string& /*purpose*/, + uint /*isMine*/, const std::string& /*purpose*/, ChangeType /*status*/) { refreshData(); }); } diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 0ed2b2fc0..692f9ff7d 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -20,6 +20,10 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : setWindowTitle(tr("New receiving address")); ui->addressEdit->setEnabled(false); break; + case NewReceivingLedgerAddress: + setWindowTitle(tr("New receiving Ledger address")); + ui->addressEdit->setEnabled(false); + break; case NewSendingAddress: setWindowTitle(tr("New sending address")); break; @@ -65,9 +69,20 @@ bool EditAddressDialog::saveCurrentRow() switch(mode) { case NewReceivingAddress: + address = model->addRow( + AddressTableModel::Receive, + ui->labelEdit->text(), + ui->addressEdit->text()); + break; case NewSendingAddress: address = model->addRow( - mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, + AddressTableModel::Send, + ui->labelEdit->text(), + ui->addressEdit->text()); + break; + case NewReceivingLedgerAddress: + address = model->addRow( + AddressTableModel::ReceiveLedger, ui->labelEdit->text(), ui->addressEdit->text()); break; diff --git a/wallet/qt/editaddressdialog.h b/wallet/qt/editaddressdialog.h index f9a1b476c..9b246722b 100644 --- a/wallet/qt/editaddressdialog.h +++ b/wallet/qt/editaddressdialog.h @@ -21,9 +21,10 @@ class EditAddressDialog : public QDialog public: enum Mode { NewReceivingAddress, + NewReceivingLedgerAddress, NewSendingAddress, EditReceivingAddress, - EditSendingAddress + EditSendingAddress, }; explicit EditAddressDialog(Mode modeIn, QWidget *parent = 0); diff --git a/wallet/qt/ui_addressbookpage.h b/wallet/qt/ui_addressbookpage.h index 33a870593..48eec844c 100644 --- a/wallet/qt/ui_addressbookpage.h +++ b/wallet/qt/ui_addressbookpage.h @@ -33,6 +33,7 @@ class Ui_AddressBookPage QTableView *tableView; QHBoxLayout *horizontalLayout; QPushButton *newAddressButton; + QPushButton *newLedgerAddressButton; QPushButton *copyToClipboard; QPushButton *showQRCode; QPushButton *signMessage; @@ -76,6 +77,14 @@ class Ui_AddressBookPage newAddressButton->setIcon(icon); horizontalLayout->addWidget(newAddressButton); + + newLedgerAddressButton = new QPushButton(AddressBookPage); + newLedgerAddressButton->setObjectName(QStringLiteral("newLedgerAddressButton")); + QIcon icon100; + icon.addFile(QStringLiteral(":/icons/add"), QSize(), QIcon::Normal, QIcon::Off); + newLedgerAddressButton->setIcon(icon100); + + horizontalLayout->addWidget(newLedgerAddressButton); copyToClipboard = new QPushButton(AddressBookPage); copyToClipboard->setObjectName(QStringLiteral("copyToClipboard")); @@ -152,6 +161,10 @@ class Ui_AddressBookPage newAddressButton->setToolTip(QApplication::translate("AddressBookPage", "Create a new address", Q_NULLPTR)); #endif // QT_NO_TOOLTIP newAddressButton->setText(QApplication::translate("AddressBookPage", "&New Address", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + newLedgerAddressButton->setToolTip(QApplication::translate("AddressBookPage", "Create a new Ledger address", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + newLedgerAddressButton->setText(QApplication::translate("AddressBookPage", "&New Ledger Address", Q_NULLPTR)); #ifndef QT_NO_TOOLTIP copyToClipboard->setToolTip(QApplication::translate("AddressBookPage", "Copy the currently selected address to the system clipboard", Q_NULLPTR)); #endif // QT_NO_TOOLTIP diff --git a/wallet/qt/walletmodel.cpp b/wallet/qt/walletmodel.cpp index c5c2fc86b..aa3ddfc29 100644 --- a/wallet/qt/walletmodel.cpp +++ b/wallet/qt/walletmodel.cpp @@ -194,11 +194,11 @@ void WalletModel::updateNumTransactions() } } -void WalletModel::updateAddressBook(const QString& address, const QString& label, bool isMine, +void WalletModel::updateAddressBook(const QString& address, const QString& label, uint isMine, const QString& purpose, int status) { if (addressTableModel) - addressTableModel->updateEntry(address, label, isMine, purpose, status); + addressTableModel->updateEntry(address, label, (isminetype) isMine, purpose, status); } bool WalletModel::validateAddress(const QString& address) @@ -439,14 +439,14 @@ static void NotifyKeyStoreStatusChanged(WalletModel* walletmodel, CCryptoKeyStor static void NotifyAddressBookChanged(WalletModel* walletmodel, CWallet* /*wallet*/, const CTxDestination& address, const std::string& label, - bool isMine, const std::string& purpose, ChangeType status) + uint isMine, const std::string& purpose, ChangeType status) { NLog.write(b_sev::info, "NotifyAddressBookChanged {} {} isMine={} purpose={} status={}", CBitcoinAddress(address).ToString(), label.c_str(), isMine, purpose, status); QMetaObject::invokeMethod( walletmodel, "updateAddressBook", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(CBitcoinAddress(address).ToString())), - Q_ARG(QString, QString::fromStdString(label)), Q_ARG(bool, isMine), + Q_ARG(QString, QString::fromStdString(label)), Q_ARG(uint, isMine), Q_ARG(QString, QString::fromStdString(purpose)), Q_ARG(int, status)); } diff --git a/wallet/qt/walletmodel.h b/wallet/qt/walletmodel.h index 24cafe2c6..395f8ac4a 100644 --- a/wallet/qt/walletmodel.h +++ b/wallet/qt/walletmodel.h @@ -214,7 +214,7 @@ public slots: /**/ void updateNumTransactions(); /* New, updated or removed address book entry */ - void updateAddressBook(const QString& address, const QString& label, bool isMine, + void updateAddressBook(const QString& address, const QString& label, uint isMine, const QString& purpose, int status); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ void pollBalanceChanged(); diff --git a/wallet/script.cpp b/wallet/script.cpp index 032a60a34..9c691ea77 100644 --- a/wallet/script.cpp +++ b/wallet/script.cpp @@ -17,6 +17,7 @@ using namespace boost; #include "script.h" #include "sync.h" #include "util.h" +#include "wallet.h" template std::vector ToByteVector(const T& in) @@ -1625,9 +1626,31 @@ class CKeyStoreIsMineVisitor : public boost::static_visitor } }; +class CWalletIsMineVisitor : public boost::static_visitor +{ +private: + const CWallet* wallet; + +public: + CWalletIsMineVisitor(const CWallet* walletIn): wallet(walletIn){}; + isminetype operator()(const CNoDestination& /*dest*/) const { return isminetype::ISMINE_NO; } + isminetype operator()(const CKeyID& keyID) const + { + return wallet->HaveLedgerKey(keyID) ? isminetype::ISMINE_SPENDABLE : isminetype::ISMINE_NO; + } + isminetype operator()(const CScriptID& scriptID) const {return isminetype::ISMINE_NO; } +}; + isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest) { - return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); + auto isMineKey = boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); + if (isMineKey != isminetype::ISMINE_NO) + { + return isMineKey; + } + + const CWallet *wallet = (CWallet*) &keystore; + return boost::apply_visitor(CWalletIsMineVisitor(wallet), dest); } isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 7f378fdae..a72d27d23 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2694,7 +2694,7 @@ bool CWallet::SetAddressBookEntry(const CTxDestination& address, const string& s d.purpose = strPurpose; mapAddressBook.set(address, d); } - NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, strPurpose, + NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), strPurpose, (fUpdated ? CT_UPDATED : CT_NEW)); if (!fFileBacked) return false; @@ -2715,7 +2715,7 @@ bool CWallet::DelAddressBookName(const CTxDestination& address) mapAddressBook.erase(address); } - NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, purpose, + NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), purpose, CT_DELETED); if (!fFileBacked) diff --git a/wallet/wallet.h b/wallet/wallet.h index 25e691845..50ae76484 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -367,7 +367,7 @@ class CWallet : public CCryptoKeyStore * @note called with lock cs_wallet held. */ boost::signals2::signal NotifyAddressBookChanged; diff --git a/wallet/wallet_ismine.h b/wallet/wallet_ismine.h index d28edc9b8..d789114b0 100644 --- a/wallet/wallet_ismine.h +++ b/wallet/wallet_ismine.h @@ -14,7 +14,7 @@ class CKeyStore; class CScript; /** IsMine() return codes */ -enum isminetype : uint_fast16_t +enum isminetype : uint { ISMINE_NO = 0, //! Indicates that we dont know how to create a scriptSig that would solve this if we were given the @@ -28,9 +28,11 @@ enum isminetype : uint_fast16_t ISMINE_COLD = 8, //! Indicates that we have the spending key of a P2CS ISMINE_SPENDABLE_DELEGATED = 16, - ISMINE_SPENDABLE_ALL = ISMINE_SPENDABLE_DELEGATED | ISMINE_SPENDABLE, - ISMINE_SPENDABLE_STAKEABLE = ISMINE_SPENDABLE_DELEGATED | ISMINE_COLD, - ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE | ISMINE_COLD | ISMINE_SPENDABLE_DELEGATED + //! Indicates that this wallet belongs to the Ledger HW wallet + ISMINE_LEDGER = 32, + ISMINE_SPENDABLE_ALL = ISMINE_SPENDABLE_DELEGATED | ISMINE_SPENDABLE | ISMINE_LEDGER, + ISMINE_SPENDABLE_STAKEABLE = ISMINE_SPENDABLE_DELEGATED | ISMINE_COLD, // TODO GK - Ledger?? + ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE | ISMINE_COLD | ISMINE_SPENDABLE_DELEGATED | ISMINE_LEDGER }; /** used for bitflags of isminetype */ using isminefilter = uint8_t; From ea7c4841bdab5568d41c76c92e449328ddfbcec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Feb 2023 12:48:09 +0100 Subject: [PATCH 005/129] Fix Ledger transport --- wallet/ledger/transport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 1123a8c63..2fed14fa1 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -7,7 +7,7 @@ Transport::Transport(TransportType type) { switch (type) { case TransportType::HID: - comm_ = std::unique_ptr(); + comm_ = std::unique_ptr(new HID()); break; } } From a3925ebac1be2e283e90c397273942a6e1a948a5 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 24 Feb 2023 15:18:34 +0100 Subject: [PATCH 006/129] Extend gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 86ccad32d..ea5e91a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,9 @@ wallet/test/data/txs_ntp1tests_ntp1_txs_testnet.json wallet/test/data/txs_ntp1tests_raw_neblio_txs_testnet.json wallet/test/data/ntp1txids_to_test.txt wallet/test/data/ntp1txids_to_test_testnet.txt + +boost*/ +openssl*/ +boost_build +openssl_build +*.tar.gz From 9104f5b05babdcccbd056d5d6413185d4a81dc63 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 24 Feb 2023 15:19:11 +0100 Subject: [PATCH 007/129] Fix Is Ledger column displaying --- wallet/qt/addresstablemodel.cpp | 9 ++++++--- wallet/script.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 2eaded669..8007bd9f1 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -11,7 +11,8 @@ const QString AddressTableModel::Send = "S"; const QString AddressTableModel::Receive = "R"; -const QString AddressTableModel::ReceiveLedger = "RL"; +const QString AddressTableModel::ReceiveLedger = "RL"; // TODO DM this is accidentally matched by +// proxyModel->setFilterFixedString(AddressTableModel::Receive); struct AddressTableEntry { @@ -202,6 +203,8 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const return Send; case AddressTableEntry::Receiving: return Receive; + case AddressTableEntry::ReceivingLedger: + return ReceiveLedger; default: break; } @@ -297,7 +300,7 @@ Qt::ItemFlags AddressTableModel::flags(const QModelIndex& index) const // Can edit address and label for sending addresses, // and only label for receiving addresses. if (rec->type == AddressTableEntry::Sending || - (rec->type == AddressTableEntry::Receiving && index.column() == Label)) { + (rec->type == AddressTableEntry::Receiving && index.column() == Label)) { // TODO DM? retval |= Qt::ItemIsEditable; } return retval; @@ -398,7 +401,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex& parent { Q_UNUSED(parent); AddressTableEntry* rec = priv->index(row); - if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving) { + if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving) { // TODO DM? // Can only remove one row at a time, and cannot remove rows not in model. // Also refuse to remove receiving addresses. return false; diff --git a/wallet/script.cpp b/wallet/script.cpp index 9c691ea77..cf8116f24 100644 --- a/wallet/script.cpp +++ b/wallet/script.cpp @@ -1636,7 +1636,7 @@ class CWalletIsMineVisitor : public boost::static_visitor isminetype operator()(const CNoDestination& /*dest*/) const { return isminetype::ISMINE_NO; } isminetype operator()(const CKeyID& keyID) const { - return wallet->HaveLedgerKey(keyID) ? isminetype::ISMINE_SPENDABLE : isminetype::ISMINE_NO; + return wallet->HaveLedgerKey(keyID) ? isminetype::ISMINE_LEDGER : isminetype::ISMINE_NO; } isminetype operator()(const CScriptID& scriptID) const {return isminetype::ISMINE_NO; } }; From 3c9678438f55e5a0d134142e07c190e205c59643 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 24 Feb 2023 15:46:55 +0100 Subject: [PATCH 008/129] Use variables in dependency paths --- wallet/wallet.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wallet/wallet.pro b/wallet/wallet.pro index 664ae1b59..a55cb49f2 100644 --- a/wallet/wallet.pro +++ b/wallet/wallet.pro @@ -18,11 +18,11 @@ mac { QMAKE_INFO_PLIST = $${NEBLIO_ROOT}/wallet/qt/res/Info.plist } -_BOOST_PATH = /home/vacuumlabs/projects/vl/neblio/neblio/boost_1_65_1 +_BOOST_PATH = $${NEBLIO_ROOT}/boost_1_65_1 INCLUDEPATH += "$${_BOOST_PATH}" LIBS += -L$${_BOOST_PATH}/stage/lib -_OPENSSL_PATH = /home/vacuumlabs/projects/vl/neblio/neblio/openssl_build +_OPENSSL_PATH = $${NEBLIO_ROOT}/openssl_build INCLUDEPATH += "$${_OPENSSL_PATH}/include" LIBS += -L$${_OPENSSL_PATH}/lib From 9006d00c68e818a88cc8996604bf80008165e45c Mon Sep 17 00:00:00 2001 From: David Misiak Date: Wed, 1 Mar 2023 13:21:37 +0100 Subject: [PATCH 009/129] Fix ledger address persistency --- wallet/wallet.h | 13 +++++++++++++ wallet/walletdb.cpp | 18 ++++++++++++++++++ wallet/walletdb.h | 2 ++ 3 files changed, 33 insertions(+) diff --git a/wallet/wallet.h b/wallet/wallet.h index 50ae76484..f2d288a3c 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -442,6 +442,19 @@ class CWallet : public CCryptoKeyStore } bool AddLedgerKey(const CPubKey& key) + { + { + LOCK(cs_LedgerKeyStore); + ledgerKeys[key.GetID()] = key; + } + if (!fFileBacked) + return true; + + // TODO DM do we need the lock here? + return CWalletDB(strWalletFile).WriteLedgerKey(key); + } + + bool LoadLedgerKey(const CPubKey& key) { { LOCK(cs_LedgerKeyStore); diff --git a/wallet/walletdb.cpp b/wallet/walletdb.cpp index d41a3816a..a88c88d6d 100644 --- a/wallet/walletdb.cpp +++ b/wallet/walletdb.cpp @@ -61,6 +61,13 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, return Write(std::make_pair(std::string("key"), vchPubKey.Raw()), vchPrivKey, false); } +bool CWalletDB::WriteLedgerKey(const CPubKey& vchPubKey) +{ + return Write(std::make_pair(std::string("ledgerkey"), vchPubKey.Raw()), vchPubKey, false); +} + +// TODO DM: EraseLedgerKey? + bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata& keyMeta) @@ -454,6 +461,17 @@ bool ReadKeyValue(const ITxDB& txdb, CWallet* pwallet, CDataStream& ssKey, CData strErr = "Error reading wallet database: Default Key corrupt"; return false; } + } else if (strType == "ledgerkey") { + CPubKey vchPubKey; + ssValue >> vchPubKey; + if (!vchPubKey.IsValid()) { + strErr = "Error reading wallet database: Ledger Key corrupt"; + return false; + } + if (!pwallet->LoadLedgerKey(vchPubKey)) { + strErr = "Error reading wallet database: LoadLedgerKey failed"; + return false; + } } else if (strType == "pool") { int64_t nIndex; ssKey >> nIndex; diff --git a/wallet/walletdb.h b/wallet/walletdb.h index 7b1598eca..bf2ff1e3b 100644 --- a/wallet/walletdb.h +++ b/wallet/walletdb.h @@ -62,6 +62,8 @@ class CWalletDB : public CDB bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta); + bool WriteLedgerKey(const CPubKey& vchPubKey); + bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata& keyMeta); From f2cbadb0a75201569c80950ac1d6a1404dae2233 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 6 Mar 2023 18:22:40 +0100 Subject: [PATCH 010/129] Add clean.sh --- clean.sh | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 clean.sh diff --git a/clean.sh b/clean.sh new file mode 100755 index 000000000..097063fd1 --- /dev/null +++ b/clean.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +echo "Are you sure you want to delete the neblio data directory?" +read -p "Close neblio app and press enter to continue... " + +[ -e ~/.neblio ] && rm -r ~/.neblio +mkdir ~/.neblio + +cat < ~/.neblio/neblio.conf +regtest=1 +server=1 +port=9999 +rpcport=6326 +listenonion=0 +rpcuser=user +rpcpassword=password +EOT + +read -p "Start neblio and paste your address here: " ADDR + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" + +sleep 1 + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" From a2596638dd012f0c2e3f63da48f743052d031224 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 6 Mar 2023 18:25:49 +0100 Subject: [PATCH 011/129] Derive full paths --- wallet/ledger/ledger.cpp | 8 +++++--- wallet/ledger/ledger.h | 2 +- wallet/qt/addresstablemodel.cpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index a8d8eb698..5b7543b0d 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -9,15 +9,17 @@ Ledger::~Ledger() { transport_->close(); } Error Ledger::open() { return transport_->open(); } -std::tuple> Ledger::get_public_key(uint32_t account, bool confirm) +std::tuple> Ledger::get_public_key(uint32_t account, uint32_t index, bool confirm) { auto payload = std::vector(); // path length - payload.push_back(3); - // m/44'/146'/[account]' derivation path + payload.push_back(5); + // m/44'/146'/[account]'/0/[index] derivation path utils::append_vector(payload, utils::int_to_bytes(utils::hardened(44), 4)); utils::append_vector(payload, utils::int_to_bytes(utils::hardened(146), 4)); utils::append_vector(payload, utils::int_to_bytes(utils::hardened(account), 4)); + utils::append_vector(payload, utils::int_to_bytes(0, 4)); + utils::append_vector(payload, utils::int_to_bytes(index, 4)); auto [err, buffer] = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); if (err != Error::SUCCESS) diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index e8b8e615c..773311045 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -20,7 +20,7 @@ class Ledger Error open(); - std::tuple> get_public_key(uint32_t account, bool confirm = false); + std::tuple> get_public_key(uint32_t account, uint32_t index, bool confirm = false); std::tuple> sign(uint32_t account, const std::vector& msg); void close(); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 8007bd9f1..92b2f12b5 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -365,7 +365,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con return QString(); } - auto result = l.get_public_key(0, true); + auto result = l.get_public_key(0, 0, true); if (std::get<0>(result) != ledger::Error::SUCCESS) { // TODO GK - handle error return QString(); From ca1d57d023602288bf3ea7f4a2c2da6127938cb2 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 7 Mar 2023 21:32:34 +0100 Subject: [PATCH 012/129] Unify adding addresses, improve UI --- wallet/ledger/utils.h | 3 ++ wallet/qt/addressbookpage.cpp | 13 ------ wallet/qt/addressbookpage.h | 2 - wallet/qt/addresstablemodel.cpp | 69 ++++++++++++++++++++++++-------- wallet/qt/addresstablemodel.h | 10 +++-- wallet/qt/editaddressdialog.cpp | 49 +++++++++++++++++------ wallet/qt/editaddressdialog.h | 3 +- wallet/qt/ui_addressbookpage.h | 13 ------ wallet/qt/ui_editaddressdialog.h | 68 +++++++++++++++++++++++++++++++ wallet/wallet.h | 20 ++++----- wallet/walletdb.cpp | 13 +++--- wallet/walletdb.h | 17 +++++++- 12 files changed, 201 insertions(+), 79 deletions(-) diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index dbee56004..ff9322bb1 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -13,6 +13,9 @@ void append_vector(std::vector& destination, std::vector source) destination.insert(destination.end(), source.begin(), source.end()); } uint32_t hardened(uint32_t n); + +const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; +const uint32_t MAX_RECOMMENDED_INDEX = 50000; } // namespace ledger::utils #endif diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index 887d4b9d3..ce1bddc37 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -227,19 +227,6 @@ void AddressBookPage::on_newAddressButton_clicked() } } -void AddressBookPage::on_newLedgerAddressButton_clicked() -{ - if(!model) - return; - // TODO GK - hide button in sending tab - EditAddressDialog dlg(EditAddressDialog::NewReceivingLedgerAddress, this); - dlg.setModel(model); - if(dlg.exec()) - { - newAddressToSelect = dlg.getAddress(); - } -} - void AddressBookPage::on_deleteButton_clicked() { QTableView *table = ui->tableView; diff --git a/wallet/qt/addressbookpage.h b/wallet/qt/addressbookpage.h index 94ac3c4c5..6524dbad5 100644 --- a/wallet/qt/addressbookpage.h +++ b/wallet/qt/addressbookpage.h @@ -77,8 +77,6 @@ private slots: /** New entry/entries were added to address table */ void selectNewAddress(const QModelIndex &parent, int begin, int end); - void on_newLedgerAddressButton_clicked(); - signals: void signMessage(QString addr); void verifyMessage(QString addr); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 92b2f12b5..3ad4501a3 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -26,10 +26,14 @@ struct AddressTableEntry Type type; QString label; QString address; + uint32_t ledgerAccount; + uint32_t ledgerIndex; AddressTableEntry() {} - AddressTableEntry(Type typeIn, const QString& labelIn, const QString& addressIn) - : type(typeIn), label(labelIn), address(addressIn) + AddressTableEntry(Type typeIn, const QString& labelIn, const QString& addressIn, + uint32_t ledgerAccountIn, uint32_t ledgerIndexIn) + : type(typeIn), label(labelIn), address(addressIn), ledgerAccount(ledgerAccountIn), + ledgerIndex(ledgerIndexIn) { } }; @@ -75,12 +79,26 @@ class AddressTablePriv { const auto addressBookMap = wallet->mapAddressBook.getInternalMap(); for (const auto& item : addressBookMap) { - const CBitcoinAddress& address = item.first; - const std::string& strName = item.second.name; - isminetype fMine = IsMine(*wallet, address.Get()); + const CBitcoinAddress& address = item.first; + const std::string& strName = item.second.name; + isminetype fMine = IsMine(*wallet, address.Get()); + AddressTableEntry::Type type = GetEntryType(fMine); + + CKeyID ledgerKedId; + CLedgerKey ledgerKey; + if (type == AddressTableEntry::ReceivingLedger) + { + address.GetKeyID(ledgerKedId); + wallet->GetLedgerKey(ledgerKedId, ledgerKey); + } + cachedAddressTable.append(AddressTableEntry( - GetEntryType(fMine), - QString::fromStdString(strName), QString::fromStdString(address.ToString()))); + type, + QString::fromStdString(strName), + QString::fromStdString(address.ToString()), + ledgerKey.account, + ledgerKey.index + )); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order @@ -108,9 +126,21 @@ class AddressTablePriv "already in model"); break; } - parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); - cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address)); - parent->endInsertRows(); + { + CKeyID ledgerKeyId; + CLedgerKey ledgerKey; + if (newEntryType == AddressTableEntry::ReceivingLedger) + { + CBitcoinAddress(address.toStdString()).GetKeyID(ledgerKeyId); + wallet->GetLedgerKey(ledgerKeyId, ledgerKey); + } + + parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); + cachedAddressTable.insert( + lowerIndex, + AddressTableEntry(newEntryType, label, address, ledgerKey.account, ledgerKey.index)); + parent->endInsertRows(); + } break; case CT_UPDATED: if (!inModel) { @@ -152,7 +182,7 @@ class AddressTablePriv AddressTableModel::AddressTableModel(CWallet* walletIn, WalletModel* parent) : QAbstractTableModel(parent), walletModel(parent), wallet(walletIn), priv(0) { - columns << tr("Is ledger") << tr("Label") << tr("Address"); + columns << tr("Label") << tr("Address") << tr("Is Ledger") << tr("Account") << tr("Index"); priv = new AddressTablePriv(walletIn, this); priv->refreshAddressTable(); } @@ -179,9 +209,8 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const AddressTableEntry* rec = static_cast(index.internalPointer()); if (role == Qt::DisplayRole || role == Qt::EditRole) { + bool isLedger = rec->type == AddressTableEntry::ReceivingLedger; switch (index.column()) { - case IsLedger: - return rec->type == AddressTableEntry::ReceivingLedger ? tr("Ledger") : ""; case Label: if (rec->label.isEmpty() && role == Qt::DisplayRole) { return tr("(no label)"); @@ -190,6 +219,12 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const } case Address: return rec->address; + case IsLedger: + return isLedger ? "Yes" : ""; + case LedgerAccount: + return isLedger ? QString::number(rec->ledgerAccount) : ""; + case LedgerIndex: + return isLedger ? QString::number(rec->ledgerIndex) : ""; } } else if (role == Qt::FontRole) { QFont font; @@ -324,7 +359,8 @@ void AddressTableModel::updateEntry(const QString& address, const QString& label priv->updateEntry(address, label, isMine, purpose, status); } -QString AddressTableModel::addRow(const QString& type, const QString& label, const QString& address) +QString AddressTableModel::addRow(const QString& type, const QString& label, const QString& address, + uint32_t ledgerAccount, uint32_t ledgerIndex) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); @@ -365,7 +401,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con return QString(); } - auto result = l.get_public_key(0, 0, true); + auto result = l.get_public_key(ledgerAccount, ledgerIndex, true); if (std::get<0>(result) != ledger::Error::SUCCESS) { // TODO GK - handle error return QString(); @@ -383,7 +419,8 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con auto myKeyStr = std::string(pubKey.begin(), pubKey.end()); CPubKey cpubkey(pubKey); - wallet->AddLedgerKey(cpubkey); + CLedgerKey ledgerKey(cpubkey, ledgerAccount, ledgerIndex); + wallet->AddLedgerKey(ledgerKey); strAddress = CBitcoinAddress(cpubkey.GetID()).ToString(); } else { return QString(); diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index 408483079..ef79b6a3e 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -21,9 +21,11 @@ class AddressTableModel : public QAbstractTableModel enum ColumnIndex { - IsLedger = 0, - Label = 1, /**< User specified label */ - Address = 2 /**< Bitcoin address */ + Label = 0, /**< User specified label */ + Address = 1, /**< Bitcoin address */ + IsLedger = 2, + LedgerAccount = 3, + LedgerIndex = 4, }; enum RoleIndex @@ -61,7 +63,7 @@ class AddressTableModel : public QAbstractTableModel /* Add an address to the model. Returns the added address on success, and an empty string otherwise. */ - QString addRow(const QString& type, const QString& label, const QString& address); + QString addRow(const QString& type, const QString& label, const QString& address, const uint32_t ledgerAccount, const uint32_t ledgerIndex); /* Look up label for address in address book, if not found return empty string. */ diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 692f9ff7d..53d9fb9ff 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -14,30 +14,40 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : GUIUtil::setupAddressWidget(ui->addressEdit, this); + bool ledgerItemsEnabled = true; switch(modeIn) { case NewReceivingAddress: setWindowTitle(tr("New receiving address")); ui->addressEdit->setEnabled(false); break; - case NewReceivingLedgerAddress: - setWindowTitle(tr("New receiving Ledger address")); - ui->addressEdit->setEnabled(false); - break; case NewSendingAddress: setWindowTitle(tr("New sending address")); + ledgerItemsEnabled = false; break; case EditReceivingAddress: setWindowTitle(tr("Edit receiving address")); ui->addressEdit->setEnabled(false); + ledgerItemsEnabled = false; break; case EditSendingAddress: setWindowTitle(tr("Edit sending address")); + ledgerItemsEnabled = false; break; } + if (!ledgerItemsEnabled) { + ui->ledgerCheckBox->setEnabled(false); + ui->ledgerAccountEdit->setEnabled(false); + ui->ledgerIndexEdit->setEnabled(false); + } + mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + + // Ledger submenu collapsed by default + ui->ledgerWidget->setVisible(false); + ui->ledgerInfoLabel->setVisible(false); } EditAddressDialog::~EditAddressDialog() @@ -54,6 +64,9 @@ void EditAddressDialog::setModel(AddressTableModel *modelIn) mapper->setModel(modelIn); mapper->addMapping(ui->labelEdit, AddressTableModel::Label); mapper->addMapping(ui->addressEdit, AddressTableModel::Address); + mapper->addMapping(ui->ledgerCheckBox, AddressTableModel::IsLedger); + mapper->addMapping(ui->ledgerAccountEdit, AddressTableModel::LedgerAccount); + mapper->addMapping(ui->ledgerIndexEdit, AddressTableModel::LedgerIndex); } void EditAddressDialog::loadRow(int row) @@ -66,25 +79,26 @@ bool EditAddressDialog::saveCurrentRow() if(!model) return false; + bool isLedger = ui->ledgerCheckBox->isChecked(); switch(mode) { case NewReceivingAddress: address = model->addRow( - AddressTableModel::Receive, + isLedger ? AddressTableModel::ReceiveLedger : AddressTableModel::Receive, ui->labelEdit->text(), - ui->addressEdit->text()); + ui->addressEdit->text(), + ui->ledgerAccountEdit->text().toUInt(), + ui->ledgerIndexEdit->text().toUInt() + ); break; case NewSendingAddress: address = model->addRow( AddressTableModel::Send, ui->labelEdit->text(), - ui->addressEdit->text()); - break; - case NewReceivingLedgerAddress: - address = model->addRow( - AddressTableModel::ReceiveLedger, - ui->labelEdit->text(), - ui->addressEdit->text()); + ui->addressEdit->text(), + ui->ledgerAccountEdit->text().toUInt(), + ui->ledgerIndexEdit->text().toUInt() + ); break; case EditReceivingAddress: case EditSendingAddress: @@ -139,6 +153,15 @@ void EditAddressDialog::accept() QDialog::accept(); } +void EditAddressDialog::on_ledgerCheckBox_toggled(bool checked) +{ + ui->ledgerWidget->setVisible(checked); + ui->ledgerInfoLabel->setVisible(checked); + + // reset dialog height after hiding ledger items + setFixedHeight(sizeHint().height()); +} + QString EditAddressDialog::getAddress() const { return address; diff --git a/wallet/qt/editaddressdialog.h b/wallet/qt/editaddressdialog.h index 9b246722b..e30c760a9 100644 --- a/wallet/qt/editaddressdialog.h +++ b/wallet/qt/editaddressdialog.h @@ -21,7 +21,6 @@ class EditAddressDialog : public QDialog public: enum Mode { NewReceivingAddress, - NewReceivingLedgerAddress, NewSendingAddress, EditReceivingAddress, EditSendingAddress, @@ -39,6 +38,8 @@ class EditAddressDialog : public QDialog public slots: void accept(); + void on_ledgerCheckBox_toggled(bool checked); + private: bool saveCurrentRow(); diff --git a/wallet/qt/ui_addressbookpage.h b/wallet/qt/ui_addressbookpage.h index 48eec844c..33a870593 100644 --- a/wallet/qt/ui_addressbookpage.h +++ b/wallet/qt/ui_addressbookpage.h @@ -33,7 +33,6 @@ class Ui_AddressBookPage QTableView *tableView; QHBoxLayout *horizontalLayout; QPushButton *newAddressButton; - QPushButton *newLedgerAddressButton; QPushButton *copyToClipboard; QPushButton *showQRCode; QPushButton *signMessage; @@ -77,14 +76,6 @@ class Ui_AddressBookPage newAddressButton->setIcon(icon); horizontalLayout->addWidget(newAddressButton); - - newLedgerAddressButton = new QPushButton(AddressBookPage); - newLedgerAddressButton->setObjectName(QStringLiteral("newLedgerAddressButton")); - QIcon icon100; - icon.addFile(QStringLiteral(":/icons/add"), QSize(), QIcon::Normal, QIcon::Off); - newLedgerAddressButton->setIcon(icon100); - - horizontalLayout->addWidget(newLedgerAddressButton); copyToClipboard = new QPushButton(AddressBookPage); copyToClipboard->setObjectName(QStringLiteral("copyToClipboard")); @@ -161,10 +152,6 @@ class Ui_AddressBookPage newAddressButton->setToolTip(QApplication::translate("AddressBookPage", "Create a new address", Q_NULLPTR)); #endif // QT_NO_TOOLTIP newAddressButton->setText(QApplication::translate("AddressBookPage", "&New Address", Q_NULLPTR)); -#ifndef QT_NO_TOOLTIP - newLedgerAddressButton->setToolTip(QApplication::translate("AddressBookPage", "Create a new Ledger address", Q_NULLPTR)); -#endif // QT_NO_TOOLTIP - newLedgerAddressButton->setText(QApplication::translate("AddressBookPage", "&New Ledger Address", Q_NULLPTR)); #ifndef QT_NO_TOOLTIP copyToClipboard->setToolTip(QApplication::translate("AddressBookPage", "Copy the currently selected address to the system clipboard", Q_NULLPTR)); #endif // QT_NO_TOOLTIP diff --git a/wallet/qt/ui_editaddressdialog.h b/wallet/qt/ui_editaddressdialog.h index fded1990e..6c56fd774 100644 --- a/wallet/qt/ui_editaddressdialog.h +++ b/wallet/qt/ui_editaddressdialog.h @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include "ledger/utils.h" QT_BEGIN_NAMESPACE @@ -34,6 +37,15 @@ class Ui_EditAddressDialog QLineEdit *addressEdit; QDialogButtonBox *buttonBox; + QCheckBox *ledgerCheckBox; + QWidget *ledgerWidget; + QFormLayout *ledgerFormLayout; + QLabel *ledgerAccountLabel; + QLineEdit *ledgerAccountEdit; + QLabel *ledgerIndexLabel; + QLineEdit *ledgerIndexEdit; + QLabel *ledgerInfoLabel; + void setupUi(QDialog *EditAddressDialog) { if (EditAddressDialog->objectName().isEmpty()) @@ -68,6 +80,47 @@ class Ui_EditAddressDialog verticalLayout->addLayout(formLayout); + + ledgerCheckBox = new QCheckBox(EditAddressDialog); + ledgerCheckBox->setObjectName(QStringLiteral("ledgerCheckBox")); + verticalLayout->addWidget(ledgerCheckBox); + + ledgerWidget = new QWidget(EditAddressDialog); + ledgerWidget->setObjectName(QStringLiteral("ledgerWidget")); + + ledgerFormLayout = new QFormLayout(); + ledgerFormLayout->setObjectName(QStringLiteral("ledgerFormLayout")); + ledgerFormLayout->setContentsMargins(0, 0, 0, 0); + ledgerWidget->setLayout(ledgerFormLayout); + + ledgerAccountLabel = new QLabel(EditAddressDialog); + ledgerAccountLabel->setObjectName(QStringLiteral("ledgerAccountLabel")); + ledgerFormLayout->setWidget(0, QFormLayout::LabelRole, ledgerAccountLabel); + + ledgerAccountEdit = new QLineEdit(EditAddressDialog); + ledgerAccountEdit->setObjectName(QStringLiteral("ledgerAccountEdit")); + ledgerAccountEdit->setValidator(new QIntValidator(0, ledger::utils::MAX_RECOMMENDED_ACCOUNT)); + ledgerAccountEdit->setText("0"); + ledgerFormLayout->setWidget(0, QFormLayout::FieldRole, ledgerAccountEdit); + + ledgerIndexLabel = new QLabel(EditAddressDialog); + ledgerIndexLabel->setObjectName(QStringLiteral("ledgerIndexLabel")); + ledgerFormLayout->setWidget(1, QFormLayout::LabelRole, ledgerIndexLabel); + + ledgerIndexEdit = new QLineEdit(EditAddressDialog); + ledgerIndexEdit->setObjectName(QStringLiteral("ledgerIndexEdit")); + ledgerIndexEdit->setValidator(new QIntValidator(0, ledger::utils::MAX_RECOMMENDED_INDEX)); + ledgerIndexEdit->setText("0"); + ledgerFormLayout->setWidget(1, QFormLayout::FieldRole, ledgerIndexEdit); + + verticalLayout->addWidget(ledgerWidget); + + ledgerInfoLabel = new QLabel(EditAddressDialog); + ledgerInfoLabel->setObjectName(QStringLiteral("ledgerInfoLabel")); + ledgerInfoLabel->setWordWrap(true); + verticalLayout->addWidget(ledgerInfoLabel); + + buttonBox = new QDialogButtonBox(EditAddressDialog); buttonBox->setObjectName(QStringLiteral("buttonBox")); buttonBox->setOrientation(Qt::Horizontal); @@ -78,6 +131,8 @@ class Ui_EditAddressDialog #ifndef QT_NO_SHORTCUT label->setBuddy(labelEdit); label_2->setBuddy(addressEdit); + ledgerAccountLabel->setBuddy(ledgerAccountEdit); + ledgerIndexLabel->setBuddy(ledgerIndexEdit); #endif // QT_NO_SHORTCUT retranslateUi(EditAddressDialog); @@ -98,6 +153,19 @@ class Ui_EditAddressDialog #ifndef QT_NO_TOOLTIP addressEdit->setToolTip(QApplication::translate("EditAddressDialog", "The address associated with this address book entry. This can only be modified for sending addresses.", Q_NULLPTR)); #endif // QT_NO_TOOLTIP + ledgerCheckBox->setText(QApplication::translate("EditAddressDialog", "L&edger address", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + ledgerCheckBox->setToolTip(QApplication::translate("EditAddressDialog", "Should the address be controlled by a Ledger hardware wallet", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + ledgerAccountLabel->setText(QApplication::translate("EditAddressDialog", "Ledger acc&ount number", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + ledgerAccountEdit->setToolTip(QApplication::translate("EditAddressDialog", "The account number in the Ledger address derivation path", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + ledgerIndexLabel->setText(QApplication::translate("EditAddressDialog", "Ledger a&ddress index", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + ledgerIndexEdit->setToolTip(QApplication::translate("EditAddressDialog", "The address index in the Ledger address derivation path", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + ledgerInfoLabel->setText(QApplication::translate("EditAddressDialog", "Make sure to connect your Ledger device and open the neblio app before continuing.", Q_NULLPTR)); } // retranslateUi }; diff --git a/wallet/wallet.h b/wallet/wallet.h index f2d288a3c..0c5fd7cfd 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -430,7 +430,7 @@ class CWallet : public CCryptoKeyStore mutable CCriticalSection cs_LedgerKeyStore; // TODO GK - LedgerKeyStore class? (this is copied from keystore) - std::map ledgerKeys; + std::map ledgerKeys; bool HaveLedgerKey(const CKeyID &address) const { bool result; @@ -441,24 +441,24 @@ class CWallet : public CCryptoKeyStore return result; } - bool AddLedgerKey(const CPubKey& key) + bool AddLedgerKey(const CLedgerKey& ledgerKey) { { LOCK(cs_LedgerKeyStore); - ledgerKeys[key.GetID()] = key; + ledgerKeys[ledgerKey.vchPubKey.GetID()] = ledgerKey; } if (!fFileBacked) return true; // TODO DM do we need the lock here? - return CWalletDB(strWalletFile).WriteLedgerKey(key); + return CWalletDB(strWalletFile).WriteLedgerKey(ledgerKey); } - bool LoadLedgerKey(const CPubKey& key) + bool LoadLedgerKey(const CLedgerKey& ledgerKey) { { LOCK(cs_LedgerKeyStore); - ledgerKeys[key.GetID()] = key; + ledgerKeys[ledgerKey.vchPubKey.GetID()] = ledgerKey; } return true; } @@ -468,7 +468,7 @@ class CWallet : public CCryptoKeyStore setAddress.clear(); { LOCK(cs_LedgerKeyStore); - std::map ::const_iterator mi = ledgerKeys.begin(); + std::map ::const_iterator mi = ledgerKeys.begin(); while (mi != ledgerKeys.end()) { setAddress.insert((*mi).first); @@ -477,14 +477,14 @@ class CWallet : public CCryptoKeyStore } } - bool GetLedgerKey(const CKeyID &address, CPubKey &pubKeyOut) const + bool GetLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut) const { { LOCK(cs_LedgerKeyStore); - std::map ::const_iterator mi = ledgerKeys.find(address); + std::map ::const_iterator mi = ledgerKeys.find(address); if (mi != ledgerKeys.end()) { - pubKeyOut.SetRaw((*mi).second.Raw()); + ledgerKeyOut = (*mi).second; return true; } } diff --git a/wallet/walletdb.cpp b/wallet/walletdb.cpp index a88c88d6d..472868e64 100644 --- a/wallet/walletdb.cpp +++ b/wallet/walletdb.cpp @@ -61,9 +61,10 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, return Write(std::make_pair(std::string("key"), vchPubKey.Raw()), vchPrivKey, false); } -bool CWalletDB::WriteLedgerKey(const CPubKey& vchPubKey) +bool CWalletDB::WriteLedgerKey(const CLedgerKey& ledgerKey) { - return Write(std::make_pair(std::string("ledgerkey"), vchPubKey.Raw()), vchPubKey, false); + nWalletDBUpdated++; + return Write(std::make_pair(std::string("ledgerkey"), ledgerKey.vchPubKey.Raw()), ledgerKey, false); } // TODO DM: EraseLedgerKey? @@ -462,13 +463,13 @@ bool ReadKeyValue(const ITxDB& txdb, CWallet* pwallet, CDataStream& ssKey, CData return false; } } else if (strType == "ledgerkey") { - CPubKey vchPubKey; - ssValue >> vchPubKey; - if (!vchPubKey.IsValid()) { + CLedgerKey ledgerKey; + ssValue >> ledgerKey; + if (!ledgerKey.vchPubKey.IsValid()) { strErr = "Error reading wallet database: Ledger Key corrupt"; return false; } - if (!pwallet->LoadLedgerKey(vchPubKey)) { + if (!pwallet->LoadLedgerKey(ledgerKey)) { strErr = "Error reading wallet database: LoadLedgerKey failed"; return false; } diff --git a/wallet/walletdb.h b/wallet/walletdb.h index bf2ff1e3b..62a502372 100644 --- a/wallet/walletdb.h +++ b/wallet/walletdb.h @@ -38,6 +38,21 @@ class CKeyMetadata void SetNull(); }; +class CLedgerKey +{ +public: + CPubKey vchPubKey; + uint32_t account; + uint32_t index; + + CLedgerKey() {} + + CLedgerKey(const CPubKey& vchPubKeyIn, uint32_t accountIn, uint32_t indexIn) : + vchPubKey(vchPubKeyIn), account(accountIn), index(indexIn) {}; + + IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(account); READWRITE(index);) +}; + /** Access to the wallet database (wallet.dat) */ class CWalletDB : public CDB { @@ -62,7 +77,7 @@ class CWalletDB : public CDB bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta); - bool WriteLedgerKey(const CPubKey& vchPubKey); + bool WriteLedgerKey(const CLedgerKey& ledgerKey); bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata& keyMeta); From e754af14d414fa60865bd166659fb1c8b6ca5c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 13 Mar 2023 08:26:40 +0100 Subject: [PATCH 013/129] Update ledgercpp files --- wallet/ledger/base58.h | 75 +++ wallet/ledger/bip32.cpp | 58 +++ wallet/ledger/bip32.h | 16 + wallet/ledger/bytes.h | 12 + wallet/ledger/comm.h | 27 +- wallet/ledger/error.cpp | 40 +- wallet/ledger/error.h | 24 +- wallet/ledger/hash.h | 172 +++++++ wallet/ledger/hid_device.cpp | 281 ++++++------ wallet/ledger/hid_device.h | 40 +- wallet/ledger/ledger.cpp | 409 +++++++++++++++-- wallet/ledger/ledger.h | 70 +-- wallet/ledger/transport.cpp | 95 ++-- wallet/ledger/transport.h | 43 +- wallet/ledger/tx.cpp | 140 ++++++ wallet/ledger/tx.h | 65 +++ wallet/ledger/uint256.h | 784 ++++++++++++++++++++++++++++++++ wallet/ledger/utils.cpp | 222 ++++++++- wallet/ledger/utils.h | 30 +- wallet/qt/addresstablemodel.cpp | 26 +- wallet/wallet.pri | 8 + 21 files changed, 2258 insertions(+), 379 deletions(-) create mode 100644 wallet/ledger/base58.h create mode 100644 wallet/ledger/bip32.cpp create mode 100644 wallet/ledger/bip32.h create mode 100644 wallet/ledger/bytes.h create mode 100644 wallet/ledger/hash.h create mode 100644 wallet/ledger/tx.cpp create mode 100644 wallet/ledger/tx.h create mode 100644 wallet/ledger/uint256.h diff --git a/wallet/ledger/base58.h b/wallet/ledger/base58.h new file mode 100644 index 000000000..bb4fb271e --- /dev/null +++ b/wallet/ledger/base58.h @@ -0,0 +1,75 @@ +#include "bytes.h" + +#include +#include +#include +#include +#include +#include + +using namespace ledger; + +inline static constexpr const uint8_t Base58Map[] = { + '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', + 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z'}; +inline static constexpr const uint8_t AlphaMap[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0xff, 0x11, 0x12, 0x13, 0x14, 0x15, 0xff, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0xff, 0x2c, 0x2d, 0x2e, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff}; + +std::string Base58Encode(const bytes &data) +{ + bytes digits((data.size() * 138 / 100) + 1); + size_t digitslen = 1; + for (size_t i = 0; i < data.size(); i++) + { + uint32_t carry = static_cast(data[i]); + for (size_t j = 0; j < digitslen; j++) + { + carry = carry + static_cast(digits[j] << 8); + digits[j] = static_cast(carry % 58); + carry /= 58; + } + for (; carry; carry /= 58) + digits[digitslen++] = static_cast(carry % 58); + } + std::string result; + for (size_t i = 0; i < (data.size() - 1) && !data[i]; i++) + result.push_back(Base58Map[0]); + for (size_t i = 0; i < digitslen; i++) + result.push_back(Base58Map[digits[digitslen - 1 - i]]); + return result; +} + +bytes Base58Decode(const std::string &data) +{ + bytes result((data.size() * 138 / 100) + 1); + size_t resultlen = 1; + for (size_t i = 0; i < data.size(); i++) + { + uint32_t carry = static_cast(AlphaMap[data[i] & 0x7f]); + for (size_t j = 0; j < resultlen; j++, carry >>= 8) + { + carry += static_cast(result[j] * 58); + result[j] = static_cast(carry); + } + for (; carry; carry >>= 8) + result[resultlen++] = static_cast(carry); + } + result.resize(resultlen); + for (size_t i = 0; i < (data.size() - 1) && data[i] == AlphaMap[0]; i++) + result.push_back(0); + std::reverse(result.begin(), result.end()); + return result; +} \ No newline at end of file diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp new file mode 100644 index 000000000..377567956 --- /dev/null +++ b/wallet/ledger/bip32.cpp @@ -0,0 +1,58 @@ +#include "bip32.h" +#include "utils.h" + +#include + +namespace ledger::bip32 +{ + uint32_t Harden(uint32_t n) + { + return n | 0x80000000; + } + + // copied from https://github.com/bitcoin/bitcoin/blob/master/src/util/bip32.cpp#L13 + // and adjusted for uint8_t instead of uint32_t vector + bytes ParseHDKeypath(const std::string &keypath_str) + { + bytes keypath; + std::stringstream ss(keypath_str); + std::string item; + bool first = true; + while (std::getline(ss, item, '/')) + { + if (item.compare("m") == 0) + { + if (first) + { + first = false; + continue; + } + throw std::runtime_error("Invalid keypath"); + } + // Finds whether it is hardened + uint32_t path = 0; + size_t pos = item.find("'"); + if (pos != std::string::npos) + { + // The hardened tick can only be in the last index of the string + if (pos != item.size() - 1) + { + throw std::runtime_error("Invalid keypath"); + } + path |= 0x80000000; + item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick + } + + // Ensure this is only numbers + if (item.find_first_not_of("0123456789") != std::string::npos) + { + throw std::runtime_error("Invalid keypath"); + } + + utils::AppendUint32(keypath, std::stoul(item) | path); + + first = false; + } + return keypath; + } +} // namespace ledger::utils diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h new file mode 100644 index 000000000..97e992154 --- /dev/null +++ b/wallet/ledger/bip32.h @@ -0,0 +1,16 @@ +#ifndef __LEDGER_BIP32 +#define __LEDGER_BIP32 1 + +#include "ledger.h" + +#include +#include +#include + +namespace ledger::bip32 +{ + uint32_t Harden(uint32_t n); + bytes ParseHDKeypath(const std::string &keypath_str); +} + +#endif \ No newline at end of file diff --git a/wallet/ledger/bytes.h b/wallet/ledger/bytes.h new file mode 100644 index 000000000..968fa6071 --- /dev/null +++ b/wallet/ledger/bytes.h @@ -0,0 +1,12 @@ +#ifndef __LEDGER_BYTES +#define __LEDGER_BYTES 1 + +#include +#include + +namespace ledger +{ + typedef std::vector bytes; +} + +#endif \ No newline at end of file diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h index 99ac9a2e0..96ce5958a 100644 --- a/wallet/ledger/comm.h +++ b/wallet/ledger/comm.h @@ -1,23 +1,22 @@ -#ifndef _LEDGER_COMM -#define _LEDGER_COMM 1 +#pragma once #include #include +#include "bytes.h" #include "error.h" -namespace ledger { -class Comm +namespace ledger { -public: - virtual ~Comm() = default; + class Comm + { + public: + virtual ~Comm() = default; - virtual Error open() = 0; - virtual int send(const std::vector& data) = 0; - virtual int recv(std::vector& rdata) = 0; - virtual void close() = 0; - [[nodiscard]] virtual bool is_open() const = 0; -}; + virtual Error open() = 0; + virtual int send(const bytes &data) = 0; + virtual int recv(bytes &rdata) = 0; + virtual void close() = 0; + [[nodiscard]] virtual bool is_open() const = 0; + }; } // namespace ledger - -#endif diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index 7ff92aec2..14eb53b21 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -1,23 +1,25 @@ #include "error.h" -namespace ledger { -std::string error_message(Error code) +namespace ledger { - switch (code) { - case Error::SUCCESS: - return "Ok"; - case Error::DEVICE_NOT_FOUND: - return "Ledger Not Found"; - case Error::DEVICE_OPEN_FAIL: - return "Failed to open Ledger"; - case Error::DEVICE_DATA_SEND_FAIL: - return "Failed to send data to Ledger"; - case Error::DEVICE_DATA_RECV_FAIL: - return "Failed to receive data from Ledger"; - case Error::APDU_INVALID_CMD: - return "Invalid Ledger data"; - default: - return "Unrecognized error"; - } + std::string error_message(Error code) + { + switch (code) + { + case Error::SUCCESS: + return "Ok"; + case Error::DEVICE_NOT_FOUND: + return "Ledger Not Found"; + case Error::DEVICE_OPEN_FAIL: + return "Failed to open Ledger"; + case Error::DEVICE_DATA_SEND_FAIL: + return "Failed to send data to Ledger"; + case Error::DEVICE_DATA_RECV_FAIL: + return "Failed to receive data from Ledger"; + case Error::APDU_INVALID_CMD: + return "Invalid Ledger data"; + default: + return "Unrecognized error"; + } + } } -} // namespace ledger diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index a221ce5e1..ab62f5722 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -1,21 +1,17 @@ -#ifndef _LEDGER_ERROR -#define _LEDGER_ERROR 1 +#pragma once #include namespace ledger { -enum class Error -{ - SUCCESS = 0, - DEVICE_NOT_FOUND, - DEVICE_OPEN_FAIL, - DEVICE_DATA_SEND_FAIL, - DEVICE_DATA_RECV_FAIL, - APDU_INVALID_CMD, -}; + enum class Error { + SUCCESS = 0, + DEVICE_NOT_FOUND, + DEVICE_OPEN_FAIL, + DEVICE_DATA_SEND_FAIL, + DEVICE_DATA_RECV_FAIL, + APDU_INVALID_CMD, + }; -std::string error_message(Error code); + std::string error_message(Error code); } // namespace ledger - -#endif diff --git a/wallet/ledger/hash.h b/wallet/ledger/hash.h new file mode 100644 index 000000000..574c0ef60 --- /dev/null +++ b/wallet/ledger/hash.h @@ -0,0 +1,172 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_HASH_H +#define BITCOIN_HASH_H + +#include "uint256.h" + +#include +#include +#include +#include + +template +inline uint256 Hash(const T1 pbegin, const T1 pend) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char *)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), + (unsigned char *)&hash1); + uint256 hash2; + SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; +} + +class CHashWriter +{ +private: + SHA256_CTX ctx; + +public: + int nType; + int nVersion; + + void Init() { SHA256_Init(&ctx); } + + CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { Init(); } + + CHashWriter &write(const char *pch, size_t size) + { + SHA256_Update(&ctx, pch, size); + return (*this); + } + + // invalidates the object + uint256 GetHash() + { + uint256 hash1; + SHA256_Final((unsigned char *)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; + } + + // template + // CHashWriter &operator<<(const T &obj) + // { + // // Serialize to this stream + // ::Serialize(*this, obj, nType, nVersion); + // return (*this); + // } +}; + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char *)&p1begin[0]), + (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char *)&p2begin[0]), + (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Final((unsigned char *)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end, const T3 p3begin, + const T3 p3end) +{ + static unsigned char pblank[1]; + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char *)&p1begin[0]), + (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char *)&p2begin[0]), + (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char *)&p3begin[0]), + (p3end - p3begin) * sizeof(p3begin[0])); + SHA256_Final((unsigned char *)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; +} + +// template +// uint256 SerializeHash(const T &obj, int nType = SER_GETHASH, int nVersion = PROTOCOL_VERSION) +// { +// CHashWriter ss(nType, nVersion); +// ss << obj; +// return ss.GetHash(); +// } + +inline uint160 Hash160(const std::vector &vch) +{ + uint256 hash1; + SHA256(&vch[0], vch.size(), (unsigned char *)&hash1); + uint160 hash2; + RIPEMD160((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; +} + +unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector &vDataToHash); + +template +class HashCalculator +{ + CTXType ctx; + +public: + HashCalculator() { reset(); } + void push_data(const std::string &data) + { + UpdateFunc(&ctx, reinterpret_cast(&data.front()), data.size()); + } + void push_data(const std::vector &data) + { + UpdateFunc(&ctx, reinterpret_cast(&data.front()), data.size()); + } + void push_data(const std::vector &data) + { + UpdateFunc(&ctx, reinterpret_cast(&data.front()), data.size()); + } + void reset() + { + ctx = CTXType(); + InitFunc(&ctx); + } + std::string getHashAndReset() + { + std::string res; + res.resize(DigestSize); + FinalFunc(reinterpret_cast(&res.front()), &ctx); + reset(); + return res; + } +}; + +using Sha1Calculator = HashCalculator; +using Sha224Calculator = + HashCalculator; +using Sha256Calculator = + HashCalculator; +using Sha384Calculator = + HashCalculator; +using Sha512Calculator = + HashCalculator; +using Md5Calculator = HashCalculator; +using Ripemd160HashCalculator = HashCalculator; + +void *KDF_SHA256(const void *in, size_t inlen, void *out, size_t *outlen); +void *KDF_SHA512(const void *in, size_t inlen, void *out, size_t *outlen); + +#endif diff --git a/wallet/ledger/hid_device.cpp b/wallet/ledger/hid_device.cpp index bdc2b7305..4c6897283 100644 --- a/wallet/ledger/hid_device.cpp +++ b/wallet/ledger/hid_device.cpp @@ -1,141 +1,154 @@ -#include "hid_device.h" #include "error.h" +#include "hid_device.h" #include "utils.h" #include -namespace ledger { -Error HID::open() -{ - if (!opened_) { - auto devices = enumerate_devices(vendor_id_); - if (devices.empty()) { - return Error::DEVICE_NOT_FOUND; - } - - path_ = devices.at(0); - device_ = hid_open_path(path_.c_str()); - if (!device_) { - return Error::DEVICE_OPEN_FAIL; - } - - hid_set_nonblocking(device_, true); - - opened_ = true; - } - - return Error::SUCCESS; -} - -int HID::send(const std::vector& data) -{ - if (data.empty()) - return -1; - - auto data_new = utils::int_to_bytes(data.size(), 2); - data_new.insert(data_new.end(), data.begin(), data.end()); - - size_t offset = 0; - size_t seq_idx = 0; - size_t length = 0; - - while (offset < data_new.size()) { - // Header: channel (0x0101), tag (0x05), sequence index - std::vector header{0x01, 0x01, 0x05}; - - auto seq_idx_bytes = utils::int_to_bytes(seq_idx, 2); - header.insert(header.end(), seq_idx_bytes.begin(), seq_idx_bytes.end()); - - std::vector::iterator it; - if (data_new.size() - offset < 64 - header.size()) { - it = data_new.end(); - } else { - it = data_new.begin() + offset + 64 - header.size(); - } - - std::vector data_chunk{data_new.begin() + offset, it}; - data_chunk.insert(data_chunk.begin(), header.begin(), header.end()); - data_chunk.insert(data_chunk.begin(), 0x00); - - if (hid_write(device_, data_chunk.data(), data_chunk.size()) == -1) - return -1; - - length += data_chunk.size(); - offset += 64 - header.size(); - seq_idx += 1; - } - - return length; -} - -int HID::recv(std::vector& rdata) -{ - int seq_idx = 0; - uint8_t buf[64]; - - hid_set_nonblocking(device_, false); - if (hid_read_timeout(device_, buf, sizeof(buf), timeout_ms_) <= 0) - return -1; - hid_set_nonblocking(device_, true); - - std::vector data_chunk(buf, buf + sizeof(buf)); - - assert(data_chunk[0] == 0x01); - assert(data_chunk[1] == 0x01); - assert(data_chunk[2] == 0x05); - - auto seq_idx_bytes = utils::int_to_bytes(seq_idx, 2); - assert(seq_idx_bytes[0] == data_chunk[3]); - assert(seq_idx_bytes[1] == data_chunk[4]); - - auto data_len = - utils::bytes_to_int(std::vector(data_chunk.begin() + 5, data_chunk.begin() + 7)); - std::vector data(data_chunk.begin() + 7, data_chunk.end()); - - while (data.size() < data_len) { - uint8_t read_bytes[64]; - if (hid_read_timeout(device_, read_bytes, sizeof(read_bytes), 1000) == -1) - return -1; - std::vector tmp(read_bytes, read_bytes + sizeof(read_bytes)); - data.insert(data.end(), tmp.begin() + 5, tmp.end()); - } - - auto sw = - utils::bytes_to_int(std::vector(data.begin() + data_len - 2, data.begin() + data_len)); - rdata = std::vector(data.begin(), data.begin() + data_len - 2); - - return sw; -} - -void HID::close() noexcept -{ - if (opened_) { - hid_close(device_); - opened_ = false; - } - hid_exit(); -} - -bool HID::is_open() const { return opened_; } - -std::vector HID::enumerate_devices(unsigned short vendor_id) noexcept +namespace ledger { - std::vector devices; - - struct hid_device_info *devs, *cur_dev; - - devs = hid_enumerate(vendor_id, 0x0); - cur_dev = devs; - while (cur_dev) { - if (cur_dev->interface_number == 0 || - // MacOS specific - cur_dev->usage_page == 0xffa0) { - devices.emplace_back(cur_dev->path); - } - cur_dev = cur_dev->next; - } - hid_free_enumeration(devs); - - return devices; -} + Error HID::open() + { + if (!opened_) + { + auto devices = enumerate_devices(vendor_id_); + if (devices.empty()) + { + return Error::DEVICE_NOT_FOUND; + } + + path_ = devices.at(0); + device_ = hid_open_path(path_.c_str()); + if (!device_) + { + return Error::DEVICE_OPEN_FAIL; + } + + hid_set_nonblocking(device_, true); + + opened_ = true; + } + + return Error::SUCCESS; + } + + int HID::send(const bytes &data) + { + if (data.empty()) + return -1; + + auto data_new = utils::IntToBytes(data.size(), 2); + data_new.insert(data_new.end(), data.begin(), data.end()); + + size_t offset = 0; + size_t seq_idx = 0; + size_t length = 0; + + while (offset < data_new.size()) + { + // Header: channel (0x0101), tag (0x05), sequence index + bytes header{0x01, 0x01, 0x05}; + + auto seq_idx_bytes = utils::IntToBytes(seq_idx, 2); + header.insert(header.end(), seq_idx_bytes.begin(), seq_idx_bytes.end()); + + bytes::iterator it; + if (data_new.size() - offset < 64 - header.size()) + { + it = data_new.end(); + } + else + { + it = data_new.begin() + offset + 64 - header.size(); + } + + bytes data_chunk{data_new.begin() + offset, it}; + data_chunk.insert(data_chunk.begin(), header.begin(), header.end()); + data_chunk.insert(data_chunk.begin(), 0x00); + + if (hid_write(device_, data_chunk.data(), data_chunk.size()) == -1) + return -1; + + length += data_chunk.size(); + offset += 64 - header.size(); + seq_idx += 1; + } + + return length; + } + + int HID::recv(bytes &rdata) + { + int seq_idx = 0; + uint8_t buf[64]; + + hid_set_nonblocking(device_, false); + if (hid_read_timeout(device_, buf, sizeof(buf), timeout_ms_) <= 0) + return -1; + hid_set_nonblocking(device_, true); + + bytes data_chunk(buf, buf + sizeof(buf)); + + assert(data_chunk[0] == 0x01); + assert(data_chunk[1] == 0x01); + assert(data_chunk[2] == 0x05); + + auto seq_idx_bytes = utils::IntToBytes(seq_idx, 2); + assert(seq_idx_bytes[0] == data_chunk[3]); + assert(seq_idx_bytes[1] == data_chunk[4]); + + auto data_len = utils::BytesToInt(bytes(data_chunk.begin() + 5, data_chunk.begin() + 7)); + bytes data(data_chunk.begin() + 7, data_chunk.end()); + + while (data.size() < data_len) + { + uint8_t read_bytes[64]; + if (hid_read_timeout(device_, read_bytes, sizeof(read_bytes), 1000) == -1) + return -1; + bytes tmp(read_bytes, read_bytes + sizeof(read_bytes)); + data.insert(data.end(), tmp.begin() + 5, tmp.end()); + } + + auto sw = utils::BytesToInt(bytes(data.begin() + data_len - 2, data.begin() + data_len)); + rdata = bytes(data.begin(), data.begin() + data_len - 2); + + return sw; + } + + void HID::close() noexcept + { + if (opened_) + { + hid_close(device_); + opened_ = false; + } + hid_exit(); + } + + bool HID::is_open() const + { + return opened_; + } + + std::vector HID::enumerate_devices(unsigned short vendor_id) noexcept + { + std::vector devices; + + struct hid_device_info *devs, *cur_dev; + + devs = hid_enumerate(vendor_id, 0x0); + cur_dev = devs; + while (cur_dev) + { + if (cur_dev->interface_number == 0 || + // MacOS specific + cur_dev->usage_page == 0xffa0) + { + devices.emplace_back(cur_dev->path); + } + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); + + return devices; + } } // namespace ledger diff --git a/wallet/ledger/hid_device.h b/wallet/ledger/hid_device.h index df93d1a14..52271f7bc 100644 --- a/wallet/ledger/hid_device.h +++ b/wallet/ledger/hid_device.h @@ -1,29 +1,27 @@ -#ifndef _LEDGER_HID_DEVICE -#define _LEDGER_HID_DEVICE 1 +#pragma once #include "comm.h" -#include +#include "hidapi/hidapi.h" -namespace ledger { -class HID final : public Comm +namespace ledger { -public: - Error open() override; - int send(const std::vector& data) override; - int recv(std::vector& rdata) override; - void close() noexcept override; - [[nodiscard]] bool is_open() const override; + class HID final : public Comm + { + public: + Error open() override; + int send(const bytes &data) override; + int recv(bytes &rdata) override; + void close() noexcept override; + [[nodiscard]] bool is_open() const override; -private: - static std::vector enumerate_devices(unsigned short vendor_id = 0x2c97) noexcept; + private: + static std::vector enumerate_devices(unsigned short vendor_id = 0x2c97) noexcept; - hid_device* device_ = nullptr; - std::string path_ = {}; - bool opened_ = false; - const int timeout_ms_ = 60 * 1000; - unsigned short vendor_id_ = 0x2c97; // Ledger Vendor ID -}; + hid_device *device_ = nullptr; + std::string path_ = {}; + bool opened_ = false; + const int timeout_ms_ = 60 * 1000; + unsigned short vendor_id_ = 0x2c97; // Ledger Vendor ID + }; } // namespace ledger - -#endif diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 5b7543b0d..a8ebdfcee 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -1,41 +1,384 @@ #include "ledger.h" #include "error.h" +#include "hash.h" #include "utils.h" +#include "base58.h" +#include "bip32.h" +#include "tx.h" -namespace ledger { -Ledger::Ledger() { this->transport_ = std::unique_ptr(new Transport(Transport::TransportType::HID)); } +#include +#include -Ledger::~Ledger() { transport_->close(); } +namespace ledger +{ + Ledger::Ledger() { this->transport_ = std::unique_ptr(new Transport(Transport::TransportType::HID)); } -Error Ledger::open() { return transport_->open(); } + Ledger::~Ledger() { transport_->close(); } -std::tuple> Ledger::get_public_key(uint32_t account, uint32_t index, bool confirm) -{ - auto payload = std::vector(); - // path length - payload.push_back(5); - // m/44'/146'/[account]'/0/[index] derivation path - utils::append_vector(payload, utils::int_to_bytes(utils::hardened(44), 4)); - utils::append_vector(payload, utils::int_to_bytes(utils::hardened(146), 4)); - utils::append_vector(payload, utils::int_to_bytes(utils::hardened(account), 4)); - utils::append_vector(payload, utils::int_to_bytes(0, 4)); - utils::append_vector(payload, utils::int_to_bytes(index, 4)); - - auto [err, buffer] = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); - if (err != Error::SUCCESS) - return {err, {}}; - return {err, std::vector(buffer.begin() + 1, buffer.end())}; -} - -std::tuple> Ledger::sign(uint32_t account, const std::vector& msg) -{ - auto payload = utils::int_to_bytes(account, 4); - payload.insert(payload.end(), msg.begin(), msg.end()); - auto [err, buffer] = transport_->exchange(APDU::CLA, APDU::INS_SIGN, 0x00, 0x00, payload); - if (err != Error::SUCCESS) - return {err, {}}; - return {err, std::vector(buffer.begin() + 1, buffer.end())}; -} - -void Ledger::close() { return transport_->close(); } + Error Ledger::open() + { + std::cout << "Opening Ledger connection." << std::endl; + auto openError = transport_->open(); + if (openError != ledger::Error::SUCCESS) + { + throw ledger::error_message(openError); + } + std::cout << "Ledger connection opened." << std::endl; + } + + std::tuple Ledger::GetPublicKey(const std::string &path, bool confirm) + { + auto payload = bytes(); + + auto pathBytes = bip32::ParseHDKeypath(path); + payload.push_back(pathBytes.size() / 4); + utils::AppendVector(payload, pathBytes); + + auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x02, payload); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw error_message(err); + + auto offset = 1; + auto pubKeyLen = (int)buffer[offset] * 16 + 1; + auto pubKey = utils::Splice(buffer, offset, pubKeyLen); + offset += pubKeyLen; + + auto addressLen = (int)buffer[offset]; + offset++; + auto address = utils::Splice(buffer, offset, addressLen); + offset += addressLen; + + auto chainCode = utils::Splice(buffer, offset, 32); + offset += 32; + + if (offset != buffer.size()) + throw "Something went wrong"; + + return {pubKey, std::string(address.begin(), address.end()), chainCode}; + } + + std::tuple Ledger::GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &transactionData) + { + auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + return {err, {}}; + + return {err, bytes(buffer.begin(), buffer.end())}; + } + + std::tuple Ledger::GetTrustedInput(uint32_t indexLookup, Tx tx) + { + bytes serializedTransaction; + utils::AppendUint32(serializedTransaction, tx.version, true); + utils::AppendUint32(serializedTransaction, tx.time, true); + + utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.inputs.size())); + for (auto input : tx.inputs) + { + utils::AppendVector(serializedTransaction, input.prevout); + utils::AppendVector(serializedTransaction, utils::CreateVarint(input.script.size())); + utils::AppendVector(serializedTransaction, input.script); + utils::AppendUint32(serializedTransaction, input.sequence); + } + + utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.outputs.size())); + for (auto output : tx.outputs) + { + utils::AppendUint64(serializedTransaction, output.amount, true); + utils::AppendVector(serializedTransaction, utils::CreateVarint(output.script.size())); + utils::AppendVector(serializedTransaction, output.script); + } + + utils::AppendUint32(serializedTransaction, tx.locktime); + + return GetTrustedInput(indexLookup, serializedTransaction); + } + + std::tuple Ledger::GetTrustedInput(uint32_t indexLookup, const bytes &serializedTransaction) + { + auto MAX_CHUNK_SIZE = 255; + std::vector chunks; + auto offset = 0; + + bytes data; + utils::AppendUint32(data, indexLookup); + + utils::AppendVector(data, serializedTransaction); + + while (offset != data.size()) + { + auto chunkSize = data.size() - offset > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : data.size() - offset; + chunks.push_back(utils::Splice(data, offset, chunkSize)); + offset += chunkSize; + } + + auto isFirst = true; + bytes finalResults; + for (auto &chunk : chunks) + { + auto result = GetTrustedInputRaw(isFirst, 0, chunk); + if (std::get<0>(result) != Error::SUCCESS) + { + return {std::get<0>(result), {}}; + } + + isFirst = false; + finalResults = std::get<1>(result); + } + + return {Error::SUCCESS, finalResults}; + } + + void Ledger::UntrustedHashTxInputFinalize(Tx tx, const std::string &changePath) + { + auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE; + auto p2 = 0x00; + + auto p1 = 0xFF; + if (changePath.length() > 0) + { + auto serializedChangePath = bip32::ParseHDKeypath(changePath); + + bytes changePathData; + changePathData.push_back(serializedChangePath.size() / 4); + utils::AppendVector(changePathData, serializedChangePath); + + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, changePathData); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + } + else + { + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, {0x00}); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + } + + p1 = 0x00; + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, utils::CreateVarint(tx.outputs.size())); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + + for (auto i = 0; i < tx.outputs.size(); i++) + { + p1 = i < tx.outputs.size() - 1 ? 0x00 : 0x80; + + auto output = tx.outputs[i]; + bytes outputData; + utils::AppendUint64(outputData, output.amount, true); + utils::AppendVector(outputData, utils::CreateVarint(output.script.size())); + utils::AppendVector(outputData, output.script); + + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, outputData); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + } + } + + void Ledger::UntrustedHashTxInputStart(Tx tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction) + { + auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START; + auto p1 = 0x00; + auto p2 = isNewTransaction ? 0x02 : 0x80; + + bytes data; + utils::AppendUint32(data, tx.version, true); + utils::AppendUint32(data, tx.time, true); + utils::AppendVector(data, utils::CreateVarint(trustedInputs.size())); + + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, data); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + + p1 = 0x80; + for (auto i = 0; i < trustedInputs.size(); i++) + { + auto trustedInput = trustedInputs[i]; + auto _script = i == inputIndex ? script : bytes(); + + bytes _data; + _data.push_back(0x01); + _data.push_back(trustedInput.serialized.size()); + utils::AppendVector(_data, trustedInput.serialized); + utils::AppendVector(_data, utils::CreateVarint(_script.size())); + + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, _data); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + + bytes scriptData; + utils::AppendVector(scriptData, _script); + utils::AppendUint32(scriptData, 0xfffffffd, true); + + result = transport_->exchange(APDU::CLA, ins, p1, p2, scriptData); + err = std::get<0>(result); + buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + } + } + + std::vector> Ledger::SignTransaction(const std::string &address, uint64_t amount, uint64_t fees, const std::string &changePath, const std::vector &signPaths, const std::vector> &rawUtxos, uint32_t locktime) + { + Tx tx; + tx.version = 2; + tx.time = 0; + tx.locktime = locktime; + + // build UTxOs and count amount available + std::vector utxos; + uint64_t amountAvailable = 0; + for (const auto &rawUtxo : rawUtxos) + { + Utxo utxo; + utxo.raw = std::get<0>(rawUtxo); + utxo.index = std::get<1>(rawUtxo); + + auto utxoTx = ledger::DeserializeTransaction(utxo.raw); + utxo.tx = utxoTx; + + utxos.push_back(utxo); + + auto amount = utxoTx.outputs[utxo.index].amount; + amountAvailable += amount; + } + + // get trusted inputs + std::vector trustedInputs; + for (auto i = 0; i < utxos.size(); i++) + { + const auto &utxo = utxos[i]; + + const auto serializedTrustedInputResult = GetTrustedInput(utxo.index, utxo.tx); + auto trustedInput = ledger::DeserializeTrustedInput(std::get<1>(serializedTrustedInputResult)); + + TxInput txInput; + txInput.prevout = trustedInput.prevTxId; + + auto publicKeyResult = GetPublicKey(signPaths[i], false); + auto publicKey = utils::CompressPubKey(std::get<0>(publicKeyResult)); + + auto pubKeyHash = Hash160(publicKey); + bytes pubKeyHashVector(pubKeyHash.begin(), pubKeyHash.end()); + + bytes finalScriptPubKey; + finalScriptPubKey.push_back(0x76); + finalScriptPubKey.push_back(0xa9); + finalScriptPubKey.push_back(0x14); + utils::AppendVector(finalScriptPubKey, pubKeyHashVector); + finalScriptPubKey.push_back(0x88); + finalScriptPubKey.push_back(0xac); + + txInput.script = finalScriptPubKey; + txInput.sequence = 0xfffffffd; + + trustedInputs.push_back(trustedInput); + tx.inputs.push_back(txInput); + } + + // create change output + if (amountAvailable - fees > amount) + { + auto publicKeyResult = GetPublicKey(changePath, false); + auto publicKey = utils::CompressPubKey(std::get<0>(publicKeyResult)); + auto publicKeyHash = Hash160(publicKey); + + // TODO GK - other key structures? + bytes changeScriptPublicKey; + changeScriptPublicKey.push_back(0x76); + changeScriptPublicKey.push_back(0xa9); + changeScriptPublicKey.push_back(0x14); + utils::AppendVector(changeScriptPublicKey, bytes(publicKeyHash.begin(), publicKeyHash.end())); + changeScriptPublicKey.push_back(0x88); + changeScriptPublicKey.push_back(0xac); + + TxOutput txChangeOutput; + // TODO GK - fix amount + txChangeOutput.amount = amount - fees; + txChangeOutput.script = changeScriptPublicKey; + tx.outputs.push_back(txChangeOutput); + } + + // create output to address + // TODO GK - other key structures? + bytes scriptPublicKey; + scriptPublicKey.push_back(0x76); + scriptPublicKey.push_back(0xa9); + scriptPublicKey.push_back(0x14); + auto addressDecoded = Base58Decode(address); + utils::AppendVector(scriptPublicKey, bytes(addressDecoded.begin() + 1, addressDecoded.end() - 4)); + scriptPublicKey.push_back(0x88); + scriptPublicKey.push_back(0xac); + + TxOutput txOutput; + txOutput.amount = amount; + txOutput.script = scriptPublicKey; + tx.outputs.push_back(txOutput); + + for (auto i = 0; i < tx.inputs.size(); i++) + { + UntrustedHashTxInputStart(tx, trustedInputs, i, tx.inputs[i].script, i == 0); + } + + UntrustedHashTxInputFinalize(tx, changePath); + + std::vector> signatures; + for (auto i = 0; i < tx.inputs.size(); i++) + { + UntrustedHashTxInputStart(tx, {trustedInputs[i]}, 0, tx.inputs[i].script, false); + + auto amount = tx.outputs[i].amount; + + auto ins = INS_UNTRUSTED_HASH_SIGN; + auto p1 = 0x00; + auto p2 = 0x00; + + auto serializedChangePath = bip32::ParseHDKeypath(signPaths[i]); + + bytes data; + data.push_back(serializedChangePath.size() / 4); + utils::AppendVector(data, serializedChangePath); + data.push_back(0x00); + utils::AppendUint32(data, locktime); + data.push_back(0x01); + + auto result = transport_->exchange(APDU::CLA, ins, p1, p2, data); + auto err = std::get<0>(result); + auto buffer = std::get<1>(result); + if (err != Error::SUCCESS) + throw err; + + if (buffer[0] & 0x01) + { + bytes data; + data.push_back(0x30); + utils::AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); + signatures.push_back({{1}, data}); + } + else + { + signatures.push_back({{0}, buffer}); + } + } + + return signatures; + } + + void Ledger::close() { return transport_->close(); } } // namespace ledger diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 773311045..c65954f39 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -1,33 +1,45 @@ -#ifndef _LEDGER_LEDGER -#define _LEDGER_LEDGER 1 +#pragma once +#include "bytes.h" #include "transport.h" +#include "tx.h" -namespace ledger { -class Ledger +namespace ledger { - enum APDU : uint8_t - { - CLA = 0xe0, - INS_GET_APP_CONFIGURATION = 0x01, - INS_GET_PUBLIC_KEY = 0x40, - INS_SIGN = 0x03, - }; - -public: - Ledger(); - ~Ledger(); - - Error open(); - - std::tuple> get_public_key(uint32_t account, uint32_t index, bool confirm = false); - std::tuple> sign(uint32_t account, const std::vector& msg); - - void close(); - -private: - std::unique_ptr transport_; -}; -} // namespace ledger - -#endif + class Ledger + { + enum APDU : uint8_t + { + CLA = 0xe0, + INS_GET_APP_CONFIGURATION = 0x01, + INS_GET_PUBLIC_KEY = 0x40, + INS_SIGN = 0x03, + INS_GET_TRUSTED_INPUT = 0x42, + INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START = 0x44, + INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE = 0x4A, + INS_UNTRUSTED_HASH_SIGN = 0x48 + }; + + public: + Ledger(); + ~Ledger(); + + Error open(); + + std::tuple GetPublicKey(const std::string &path, bool confirm); + std::vector> SignTransaction(const std::string &address, uint64_t amount, uint64_t fees, const std::string &changePath, const std::vector &signPaths, const std::vector> &rawUtxos, uint32_t locktime); + + void close(); + + private: + std::unique_ptr transport_; + + std::tuple ProcessScriptBlocks(const bytes &script, uint32_t sequence); + std::tuple GetTrustedInput(uint32_t indexLookup, Tx tx); + std::tuple GetTrustedInput(uint32_t indexLookup, const bytes &serializedTransaction); + std::tuple GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &data); + void UntrustedHashTxInputFinalize(Tx tx, const std::string &changePath); + void UntrustedHashTxInputStart(Tx tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction); + TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); + }; +} diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 2fed14fa1..d53bcc08b 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -1,59 +1,68 @@ -#include "transport.h" #include "error.h" #include "hid_device.h" +#include "transport.h" +#include "utils.h" -namespace ledger { -Transport::Transport(TransportType type) +namespace ledger { - switch (type) { - case TransportType::HID: - comm_ = std::unique_ptr(new HID()); - break; - } -} + Transport::Transport(TransportType type) + { + switch (type) + { + case TransportType::HID: + comm_ = std::unique_ptr(new HID()); + break; + } + } -Error Transport::open() { return comm_->open(); } + Error Transport::open() + { + return comm_->open(); + } -std::tuple> -Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata) -{ - int length = this->send(cla, ins, p1, p2, cdata); - if (length < 0) - return {Error::DEVICE_DATA_SEND_FAIL, {}}; + std::tuple Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) + { + int length = this->send(cla, ins, p1, p2, cdata); + if (length < 0) + return {Error::DEVICE_DATA_SEND_FAIL, {}}; - std::vector buffer; - int sw = this->recv(buffer); - if (sw < 0) - return {Error::DEVICE_DATA_RECV_FAIL, {}}; + bytes buffer; + int sw = this->recv(buffer); + if (sw < 0) + return {Error::DEVICE_DATA_RECV_FAIL, {}}; - if (sw != 0x9000) - return {Error::APDU_INVALID_CMD, {}}; + if (sw != 0x9000) + return {Error::APDU_INVALID_CMD, {}}; - return {Error::SUCCESS, buffer}; -} + return {Error::SUCCESS, buffer}; + } -void Transport::close() noexcept { return comm_->close(); } + void Transport::close() noexcept + { + return comm_->close(); + } -int Transport::send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata) -{ - if (!comm_->is_open()) - return -1; + int Transport::send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) + { + if (!comm_->is_open()) + return -1; - auto header = apdu_header(cla, ins, p1, p2, cdata.size()); - header.insert(header.end(), cdata.begin(), cdata.end()); - return comm_->send(header); -} + auto header = apdu_header(cla, ins, p1, p2, cdata.size()); + header.insert(header.end(), cdata.begin(), cdata.end()); -int Transport::recv(std::vector& rdata) -{ - if (!comm_->is_open()) - return -1; + return comm_->send(header); + } - return comm_->recv(rdata); -} + int Transport::recv(bytes &rdata) + { + if (!comm_->is_open()) + return -1; -std::vector Transport::apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) -{ - return std::vector{cla, ins, p1, p2, lc}; -} + return comm_->recv(rdata); + } + + bytes Transport::apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) + { + return bytes{cla, ins, p1, p2, lc}; + } } // namespace ledger diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index d429df23f..1264f64d4 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -1,33 +1,30 @@ -#ifndef _LEDGER_TRANSPORT -#define _LEDGER_TRANSPORT 1 +#pragma once +#include "bytes.h" #include "comm.h" #include -namespace ledger { -class Transport +namespace ledger { -public: - enum class TransportType : int - { - HID = 0, - }; + class Transport + { + public: + enum class TransportType : int + { + HID = 0, + }; - Transport(TransportType type); - Error open(); - std::tuple> - exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata); - void close() noexcept; + Transport(TransportType type); + Error open(); + std::tuple exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); + void close() noexcept; -private: - int send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const std::vector& cdata); - int recv(std::vector& rdata); - static std::vector apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, - uint8_t lc); + private: + int send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); + int recv(bytes &rdata); + static bytes apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc); - std::unique_ptr comm_; -}; + std::unique_ptr comm_; + }; } // namespace ledger - -#endif diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp new file mode 100644 index 000000000..d29a1feec --- /dev/null +++ b/wallet/ledger/tx.cpp @@ -0,0 +1,140 @@ +#include "tx.h" +#include "utils.h" + +namespace ledger +{ + Tx DeserializeTransaction(const bytes &transaction) + { + Tx tx; + tx.inputs = std::vector(); + tx.outputs = std::vector(); + + auto offset = 0; + + tx.version = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); + offset += 4; + + tx.time = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); + offset += 4; + + auto varint = utils::DeserializeVarint(transaction, offset); + auto inputsCount = std::get<0>(varint); + offset += std::get<1>(varint); + + auto flags = 0; + if (inputsCount == 0) + { + flags = utils::BytesToInt(utils::Splice(transaction, offset, 1)); + offset += 1; + + varint = utils::DeserializeVarint(transaction, offset); + inputsCount = std::get<0>(varint); + offset += std::get<1>(varint); + } + + for (auto i = 0; i < inputsCount; i++) + { + TxInput input; + + input.prevout = utils::Splice(transaction, offset, 36); + offset += 36; + + varint = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(varint); + input.script = utils::Splice(transaction, offset, std::get<0>(varint)); + + offset += std::get<0>(varint); + input.sequence = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + offset += 4; + + tx.inputs.push_back(input); + } + + varint = utils::DeserializeVarint(transaction, offset); + auto numberOutputs = std::get<0>(varint); + offset += std::get<1>(varint); + + for (auto i = 0; i < numberOutputs; i++) + { + TxOutput output; + + output.amount = utils::BytesToUint64(utils::Splice(transaction, offset, 8), true); + offset += 8; + + varint = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(varint); + + output.script = utils::Splice(transaction, offset, std::get<0>(varint)); + offset += std::get<0>(varint); + + tx.outputs.push_back(output); + } + + if (flags != 0) + { + TxWitness txWitness; + for (auto i = 0; i < inputsCount; i++) + { + auto numberOfWitnesses = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(numberOfWitnesses); + + TxInWitness txInWitness; + ScriptWitness scriptWitness; + for (auto j = 0; j < std::get<0>(numberOfWitnesses); j++) + { + auto scriptWitnessSize = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(scriptWitnessSize); + scriptWitness.stack.push_back(bytes(transaction.begin() + offset, transaction.begin() + offset + std::get<0>(scriptWitnessSize))); + offset += std::get<0>(scriptWitnessSize); + } + + txInWitness.scriptWitness = scriptWitness; + txWitness.txInWitnesses.push_back(txInWitness); + } + } + + tx.locktime = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + + return tx; + } + + TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput) + { + TrustedInput trustedInput; + + // TODO GK - direct assignment ok? + utils::AppendVector(trustedInput.serialized, serializedTrustedInput); + + auto offset = 0; + + auto trustedInputMagic = serializedTrustedInput[offset]; + if (trustedInputMagic != 0x32) + throw "Invalid trusted input magic"; + offset += 1; + + auto zeroByte = serializedTrustedInput[offset]; + if (zeroByte != 0x00) + throw "Zero byte is not a zero byte"; + offset += 1; + + trustedInput.random = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 2)); + offset += 2; + + trustedInput.prevTxId = utils::Splice(serializedTrustedInput, offset, 32); + offset += 32; + + trustedInput.outIndex = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 4), true); + offset += 4; + + trustedInput.amount = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 8), true); + offset += 8; + + trustedInput.hmac = utils::Splice(serializedTrustedInput, offset, 8); + offset += 8; + + if (offset != serializedTrustedInput.size()) + throw "Leftover bytes in trusted input"; + + return trustedInput; + } +} // namespace ledger diff --git a/wallet/ledger/tx.h b/wallet/ledger/tx.h new file mode 100644 index 000000000..5256072f8 --- /dev/null +++ b/wallet/ledger/tx.h @@ -0,0 +1,65 @@ +#pragma once + +#include "bytes.h" +#include "transport.h" + +namespace ledger +{ + struct TxInput + { + bytes prevout; + bytes script; + uint32_t sequence; + }; + + struct TxOutput + { + uint64_t amount; + bytes script; + }; + + struct ScriptWitness + { + std::vector stack; + }; + + struct TxInWitness + { + ScriptWitness scriptWitness; + }; + + struct TxWitness + { + std::vector txInWitnesses; + }; + + struct Tx + { + uint32_t version; + uint32_t time; + std::vector inputs; + std::vector outputs; + uint32_t locktime; + TxWitness witness; + }; + + struct TrustedInput + { + bytes serialized; + uint16_t random; + bytes prevTxId; + uint32_t outIndex; + uint64_t amount; + bytes hmac; + }; + + struct Utxo + { + bytes raw; + uint32_t index; + Tx tx; + }; + + Tx DeserializeTransaction(const bytes &transaction); + TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); +} diff --git a/wallet/ledger/uint256.h b/wallet/ledger/uint256.h new file mode 100644 index 000000000..8c31baefa --- /dev/null +++ b/wallet/ledger/uint256.h @@ -0,0 +1,784 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include +#include +#include + +#include +#include +#include + +inline int Testuint256AdHoc(std::vector vArg); + +/** Base class without constructors for uint256 and uint160. + * This makes the compiler let u use it in a union. + */ +template +class base_uint +{ +protected: + enum + { + WIDTH = BITS / 32 + }; + static_assert(WIDTH * 32 == BITS, "You cannot have a width that is not a multiple of 32"); + uint32_t pn[WIDTH]; + +public: + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + double getdouble() const + { + double ret = 0.0; + double fact = 1.0; + for (int i = 0; i < WIDTH; i++) + { + ret += fact * pn[i]; + fact *= 4294967296.0; + } + return ret; + } + + base_uint &operator=(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint &operator^=(const base_uint &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint &operator&=(const base_uint &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint &operator|=(const base_uint &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint &operator^=(uint64_t b) + { + pn[0] ^= (uint32_t)b; + pn[1] ^= (uint32_t)(b >> 32); + return *this; + } + + base_uint &operator|=(uint64_t b) + { + pn[0] |= (uint32_t)b; + pn[1] |= (uint32_t)(b >> 32); + return *this; + } + + base_uint &operator<<=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i + k + 1 < WIDTH && shift != 0) + pn[i + k + 1] |= (a.pn[i] >> (32 - shift)); + if (i + k < WIDTH) + pn[i + k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint &operator>>=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i - k - 1 >= 0 && shift != 0) + pn[i - k - 1] |= (a.pn[i] << (32 - shift)); + if (i - k >= 0) + pn[i - k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint &operator+=(const base_uint &b) + { + uint64_t carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64_t n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint &operator-=(const base_uint &b) + { + *this += -b; + return *this; + } + + base_uint &operator+=(uint64_t b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint &operator-=(uint64_t b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + base_uint &operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH - 1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint &operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH - 1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + friend inline bool operator<(const base_uint &a, const base_uint &b) + { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint &a, const base_uint &b) + { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint &a, const base_uint &b) + { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint &a, const base_uint &b) + { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint &a, const base_uint &b) + { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) + return false; + return true; + } + + friend inline bool operator==(const base_uint &a, uint64_t b) + { + if (a.pn[0] != (uint32_t)b) + return false; + if (a.pn[1] != (uint32_t)(b >> 32)) + return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) + return false; + return true; + } + + friend inline bool operator!=(const base_uint &a, const base_uint &b) + { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint &a, uint64_t b) + { + return (!(a == b)); + } + + std::string GetHex() const + { + char psz[sizeof(pn) * 2 + 1]; + for (unsigned int i = 0; i < sizeof(pn); i++) + sprintf(psz + i * 2, "%02x", ((unsigned char *)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn) * 2); + } + + void SetHex(const char *psz) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + + // hex string to uint + static const unsigned char phexdigit[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + const char *pbegin = psz; + while (phexdigit[(unsigned char)*psz] || *psz == '0') + psz++; + psz--; + unsigned char *p1 = (unsigned char *)pn; + unsigned char *pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) + { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) + { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string &str) + { + SetHex(str.c_str()); + } + + std::string ToString() const + { + return (GetHex()); + } + + unsigned char *begin() + { + return (unsigned char *)&pn[0]; + } + + unsigned char *end() + { + return (unsigned char *)&pn[WIDTH]; + } + + const unsigned char *begin() const + { + return (unsigned char *)&pn[0]; + } + + const unsigned char *end() const + { + return (unsigned char *)&pn[WIDTH]; + } + + unsigned int size() const + { + return sizeof(pn); + } + + uint64_t Get64(int n = 0) const + { + return pn[2 * n] | (uint64_t)pn[2 * n + 1] << 32; + } + + unsigned int GetSerializeSize(int /*nType*/, int /*nVersion*/) const + { + return sizeof(pn); + } + + template + void Serialize(Stream &s, int /*nType*/, int /*nVersion*/) const + { + s.write((char *)pn, sizeof(pn)); + } + + template + void Unserialize(Stream &s, int /*nType*/, int /*nVersion*/) + { + s.read((char *)pn, sizeof(pn)); + } + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +/** 160-bit unsigned integer */ +class uint160 : public base_uint160 +{ +public: + typedef base_uint160 basetype; + + uint160() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160 &operator=(const basetype &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160 &operator=(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string &str) + { + SetHex(str); + } + + explicit uint160(const std::vector &vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160 &a, uint64_t b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160 &a, uint64_t b) { return (base_uint160)a != b; } +inline const uint160 operator<<(const base_uint160 &a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const base_uint160 &a, unsigned int shift) { return uint160(a) >>= shift; } +inline const uint160 operator<<(const uint160 &a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const uint160 &a, unsigned int shift) { return uint160(a) >>= shift; } + +inline const uint160 operator^(const base_uint160 &a, const base_uint160 &b) { return uint160(a) ^= b; } +inline const uint160 operator&(const base_uint160 &a, const base_uint160 &b) { return uint160(a) &= b; } +inline const uint160 operator|(const base_uint160 &a, const base_uint160 &b) { return uint160(a) |= b; } +inline const uint160 operator+(const base_uint160 &a, const base_uint160 &b) { return uint160(a) += b; } +inline const uint160 operator-(const base_uint160 &a, const base_uint160 &b) { return uint160(a) -= b; } + +inline bool operator<(const base_uint160 &a, const uint160 &b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160 &a, const uint160 &b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160 &a, const uint160 &b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160 &a, const uint160 &b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160 &a, const uint160 &b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160 &a, const uint160 &b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160 &a, const uint160 &b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160 &a, const uint160 &b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160 &a, const uint160 &b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160 &a, const uint160 &b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160 &a, const uint160 &b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160 &a, const base_uint160 &b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160 &a, const base_uint160 &b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160 &a, const base_uint160 &b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160 &a, const base_uint160 &b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160 &a, const base_uint160 &b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160 &a, const base_uint160 &b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160 &a, const base_uint160 &b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160 &a, const base_uint160 &b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160 &a, const base_uint160 &b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160 &a, const base_uint160 &b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160 &a, const base_uint160 &b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160 &a, const uint160 &b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160 &a, const uint160 &b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160 &a, const uint160 &b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160 &a, const uint160 &b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160 &a, const uint160 &b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160 &a, const uint160 &b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160 &a, const uint160 &b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160 &a, const uint160 &b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160 &a, const uint160 &b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160 &a, const uint160 &b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160 &a, const uint160 &b) { return (base_uint160)a - (base_uint160)b; } + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +/** 256-bit unsigned integer */ +class uint256 : public base_uint256 +{ +public: + typedef base_uint256 basetype; + + uint256() + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256 &operator=(const basetype &b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256 &operator=(uint64_t b) + { + pn[0] = (uint32_t)b; + pn[1] = (uint32_t)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string &str) + { + SetHex(str); + } + + explicit uint256(const std::vector &vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } + + bool IsNull() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + void SetNull() + { + std::memset(pn, 0, sizeof(pn)); + } +}; + +namespace std +{ + template <> + struct hash + { + std::size_t operator()(const uint256 &k) const { return std::hash()(k.Get64(0)); } + }; +} // namespace std + +inline bool operator==(const uint256 &a, uint64_t b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256 &a, uint64_t b) { return (base_uint256)a != b; } +inline const uint256 operator<<(const base_uint256 &a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const base_uint256 &a, unsigned int shift) { return uint256(a) >>= shift; } +inline const uint256 operator<<(const uint256 &a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const uint256 &a, unsigned int shift) { return uint256(a) >>= shift; } + +inline const uint256 operator^(const base_uint256 &a, const base_uint256 &b) { return uint256(a) ^= b; } +inline const uint256 operator&(const base_uint256 &a, const base_uint256 &b) { return uint256(a) &= b; } +inline const uint256 operator|(const base_uint256 &a, const base_uint256 &b) { return uint256(a) |= b; } +inline const uint256 operator+(const base_uint256 &a, const base_uint256 &b) { return uint256(a) += b; } +inline const uint256 operator-(const base_uint256 &a, const base_uint256 &b) { return uint256(a) -= b; } + +inline bool operator<(const base_uint256 &a, const uint256 &b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const base_uint256 &a, const uint256 &b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const base_uint256 &a, const uint256 &b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const base_uint256 &a, const uint256 &b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const base_uint256 &a, const uint256 &b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const base_uint256 &a, const uint256 &b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const base_uint256 &a, const uint256 &b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const base_uint256 &a, const uint256 &b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const base_uint256 &a, const uint256 &b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const base_uint256 &a, const uint256 &b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const base_uint256 &a, const uint256 &b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256 &a, const base_uint256 &b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256 &a, const base_uint256 &b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256 &a, const base_uint256 &b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256 &a, const base_uint256 &b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256 &a, const base_uint256 &b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256 &a, const base_uint256 &b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256 &a, const base_uint256 &b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256 &a, const base_uint256 &b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256 &a, const base_uint256 &b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256 &a, const base_uint256 &b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256 &a, const base_uint256 &b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256 &a, const uint256 &b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256 &a, const uint256 &b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256 &a, const uint256 &b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256 &a, const uint256 &b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256 &a, const uint256 &b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256 &a, const uint256 &b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256 &a, const uint256 &b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256 &a, const uint256 &b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256 &a, const uint256 &b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256 &a, const uint256 &b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256 &a, const uint256 &b) { return (base_uint256)a - (base_uint256)b; } + +#ifdef TEST_UINT256 + +#include "logging/logger.h" + +inline int Testuint256AdHoc(std::vector /*vArg*/) +{ + uint256 g(0); + + NLog.write(b_sev::info, "{}", g.ToString()); + g--; + NLog.write(b_sev::info, "g--"); + NLog.write(b_sev::info, "{}", g.ToString()); + g--; + NLog.write(b_sev::info, "g--"); + NLog.write(b_sev::info, "{}", g.ToString()); + g++; + NLog.write(b_sev::info, "g++"); + NLog.write(b_sev::info, "{}", g.ToString()); + g++; + NLog.write(b_sev::info, "g++"); + NLog.write(b_sev::info, "{}", g.ToString()); + g++; + NLog.write(b_sev::info, "g++"); + NLog.write(b_sev::info, "{}", g.ToString()); + g++; + NLog.write(b_sev::info, "g++"); + NLog.write(b_sev::info, "{}", g.ToString()); + + uint256 a(7); + NLog.write(b_sev::info, "a=7"); + NLog.write(b_sev::info, "{}", a.ToString()); + + uint256 b; + NLog.write(b_sev::info, "b undefined"); + NLog.write(b_sev::info, "{}", b.ToString()); + int c = 3; + + a = c; + a.pn[3] = 15; + NLog.write(b_sev::info, "{}", a.ToString()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + NLog.write(b_sev::info, "{}", a.ToString()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + NLog.write(b_sev::info, "a {}", a.ToString()); + + a = a | b | (uint256)0x1000; + + NLog.write(b_sev::info, "a {}", a.ToString()); + NLog.write(b_sev::info, "b {}", b.ToString()); + + a = 0xfffffffe; + a.pn[4] = 9; + + NLog.write(b_sev::info, "{}", a.ToString()); + a++; + NLog.write(b_sev::info, "{}", a.ToString()); + a++; + NLog.write(b_sev::info, "{}", a.ToString()); + a++; + NLog.write(b_sev::info, "{}", a.ToString()); + a++; + NLog.write(b_sev::info, "{}", a.ToString()); + + a--; + NLog.write(b_sev::info, "{}", a.ToString()); + a--; + NLog.write(b_sev::info, "{}", a.ToString()); + a--; + NLog.write(b_sev::info, "{}", a.ToString()); + uint256 d = a--; + NLog.write(b_sev::info, "{}", d.ToString()); + NLog.write(b_sev::info, "{}", a.ToString()); + a--; + NLog.write(b_sev::info, "{}", a.ToString()); + a--; + NLog.write(b_sev::info, "{}", a.ToString()); + + d = a; + + NLog.write(b_sev::info, "{}", d.ToString()); + for (int i = uint256::WIDTH - 1; i >= 0; i--) + NLog.write(b_sev::info, "%08x", d.pn[i]); + NLog.write(b_sev::info, ""); + + uint256 neg = d; + neg = ~neg; + NLog.write(b_sev::info, "{}", neg.ToString()); + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + NLog.write(b_sev::info, ""); + NLog.write(b_sev::info, "{}", e.ToString()); + + NLog.write(b_sev::info, ""); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + NLog.write(b_sev::info, "{}", x1.ToString()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1 << i; + NLog.write(b_sev::info, "{}", x2.ToString()); + } + + NLog.write(b_sev::info, ""); + NLog.write(b_sev::info, "{}", x1.ToString()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1; + x2 >>= i; + NLog.write(b_sev::info, "{}", x2.ToString()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) >> i); + NLog.write(b_sev::info, "{}", k.ToString()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) << i); + NLog.write(b_sev::info, "{}", k.ToString()); + } + + return (0); +} + +#endif + +#endif diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index d2fea0be5..dfdcee9b3 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -1,27 +1,203 @@ #include "utils.h" -namespace ledger::utils { -int bytes_to_int(const std::vector& bytes) -{ - int value = 0; - for (uint8_t byte : bytes) { - value = (value << 8) + byte; - } - return value; -} - -std::vector int_to_bytes(uint32_t n, uint32_t length) -{ - std::vector bytes; - bytes.reserve(length); - for (auto i = 0; i < length; i++) { - bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); - } - return bytes; -} - -uint32_t hardened(uint32_t n) +#include +#include +#include +#include +#include + +namespace ledger::utils { - return n | 0x80000000; -} + std::tuple DeserializeVarint(const bytes &data, uint32_t offset) + { + if (data[offset] < 0xfd) + { + return {data[offset], 1}; + } + + if (data[offset] == 0xfd) + { + return {(data[offset + 2] << 8) + data[offset + 1], 3}; + } + + if (data[offset] == 0xfe) + { + return { + (data[offset + 4] << 24) + + (data[offset + 3] << 16) + + (data[offset + 2] << 8) + + data[offset + 1], + 5, + }; + } + } + + bytes CreateVarint(uint32_t value) + { + bytes data; + if (value < 0xfd) + { + data.push_back(value); + } + else if (value <= 0xffff) + { + data.push_back(0xfd); + data.push_back(value & 0xff); + data.push_back((value >> 8) & 0xff); + } + else + { + data.push_back(0xfd); + data.push_back(value & 0xff); + data.push_back((value >> 8) & 0xff); + data.push_back((value >> 16) & 0xff); + data.push_back((value >> 24) & 0xff); + } + + return data; + } + + std::string BytesToHex(const bytes &vec) + { + std::stringstream ss; + for (int i = 0; i < vec.size(); i++) + { + ss << std::hex << std::setfill('0') << std::setw(2) << (int)vec[i]; + } + + return ss.str(); + } + + void PrintHex(const bytes &vec) + { + for (int i = 0; i < vec.size(); i++) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) << (int)vec[i]; + } + + std::cout << std::dec << std::endl; + } + + bytes HexToBytes(const std::string &data) + { + std::stringstream ss; + ss << data; + + bytes resBytes; + size_t count = 0; + const auto len = data.size(); + while (ss.good() && count < len) + { + unsigned short num; + char hexNum[2]; + ss.read(hexNum, 2); + sscanf(hexNum, "%2hX", &num); + resBytes.push_back(num); + count += 2; + } + return resBytes; + } + + uint64_t BytesToUint64(const bytes &_bytes, bool littleEndian) + { + auto bytesToConvert = _bytes; + if (littleEndian) + { + bytesToConvert = bytes(bytesToConvert.rbegin(), bytesToConvert.rend()); + } + + uint64_t value = 0; + for (const uint8_t &byte : bytesToConvert) + { + value = (value << 8) + byte; + } + return value; + } + + int BytesToInt(const bytes &_bytes, bool littleEndian) + { + auto bytesToConvert = _bytes; + if (littleEndian) + { + bytesToConvert = bytes(bytesToConvert.rbegin(), bytesToConvert.rend()); + } + + int value = 0; + for (const uint8_t &byte : bytesToConvert) + { + value = (value << 8) + byte; + } + return value; + } + + bytes IntToBytes(uint32_t n, uint32_t length, bool littleEndian) + { + bytes bytes; + bytes.reserve(length); + for (auto i = 0; i < length; i++) + { + bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); + } + + if (littleEndian) + { + std::reverse(bytes.begin(), bytes.end()); + } + + return bytes; + } + + void AppendUint32(bytes &vector, uint32_t n, bool littleEndian) + { + AppendVector(vector, IntToBytes(n, 4, littleEndian)); + } + + bytes Uint64ToBytes(uint64_t n, uint32_t length, bool littleEndian) + { + bytes bytes; + bytes.reserve(length); + for (auto i = 0; i < length; i++) + { + bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); + } + + if (littleEndian) + { + std::reverse(bytes.begin(), bytes.end()); + } + + return bytes; + } + + void AppendUint64(bytes &vector, uint64_t n, bool littleEndian) + { + AppendVector(vector, Uint64ToBytes(n, 8, littleEndian)); + } + + bytes Splice(const bytes &vec, int start, int length) + { + bytes result(length); + copy(vec.begin() + start, vec.begin() + start + length, result.begin()); + + return result; + } + + bytes CompressPubKey(const bytes &pubKey) + { + if (pubKey.size() != 65) + { + throw std::runtime_error("Invalid public key length"); + } + + if (pubKey[0] != 0x04) + { + throw std::runtime_error("Invalid public key format"); + } + + bytes compressedPubKey(33); + compressedPubKey[0] = pubKey[64] & 1 ? 0x03 : 0x02; + copy(pubKey.begin() + 1, pubKey.begin() + 33, compressedPubKey.begin() + 1); + + return compressedPubKey; + } } // namespace ledger::utils diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index ff9322bb1..29c0874e9 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -1,18 +1,32 @@ #ifndef _LEDGER_UTILS #define _LEDGER_UTILS 1 +#include "bytes.h" + #include #include +#include -namespace ledger::utils { -int bytes_to_int(const std::vector& bytes); -std::vector int_to_bytes(unsigned int n, unsigned int length); -template -void append_vector(std::vector& destination, std::vector source) +namespace ledger::utils { - destination.insert(destination.end(), source.begin(), source.end()); -} -uint32_t hardened(uint32_t n); + std::tuple DeserializeVarint(const bytes &data, uint32_t offset); + bytes CreateVarint(uint32_t value); + std::string BytesToHex(const bytes &vec); + void PrintHex(const bytes &vec); + bytes HexToBytes(const std::string &data); + uint64_t BytesToUint64(const bytes &bytes, bool littleEndian = false); + int BytesToInt(const bytes &bytes, bool littleEndian = false); + bytes IntToBytes(unsigned int n, unsigned int length, bool littleEndian = false); + bytes Uint64ToBytes(uint64_t n, unsigned int length, bool littleEndian = false); + template + void AppendVector(std::vector &destination, const std::vector &source) + { + destination.insert(destination.end(), source.begin(), source.end()); + } + void AppendUint32(bytes &vector, uint32_t n, bool littleEndian = false); + void AppendUint64(bytes &vector, uint64_t n, bool littleEndian = false); + bytes Splice(const bytes &vec, int start, int length); + bytes CompressPubKey(const bytes &pubKey); const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; const uint32_t MAX_RECOMMENDED_INDEX = 50000; diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 3ad4501a3..97be8623b 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -1,6 +1,7 @@ #include "addresstablemodel.h" #include "guiutil.h" #include "ledger/ledger.h" +#include "ledger/utils.h" #include "walletmodel.h" #include "base58.h" @@ -396,27 +397,16 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } else if (type == ReceiveLedger) { ledger::Ledger l; auto e = l.open(); - if (e != ledger::Error::SUCCESS) { - // TODO GK - handle error - return QString(); - } - - auto result = l.get_public_key(ledgerAccount, ledgerIndex, true); - if (std::get<0>(result) != ledger::Error::SUCCESS) { - // TODO GK - handle error - return QString(); - } - auto resultData = std::get<1>(result); - - auto pubKeyLen = (int)resultData[0] * 16; - auto pubKeyStart = resultData.begin(); - auto pubKeyEnd = pubKeyStart + pubKeyLen + 1; + std::stringstream pathSS; + pathSS << "m/44'/146'/" << ledgerAccount << "/0/" << ledgerIndex; + auto path = pathSS.str(); - std::vector pubKey(pubKeyLen + 1); - copy(pubKeyStart, pubKeyEnd, pubKey.begin()); + auto result = l.GetPublicKey(path, true); + + l.close(); - auto myKeyStr = std::string(pubKey.begin(), pubKey.end()); + auto pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); CPubKey cpubkey(pubKey); CLedgerKey ledgerKey(cpubkey, ledgerAccount, ledgerIndex); diff --git a/wallet/wallet.pri b/wallet/wallet.pri index 4d91178d7..ab041887c 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -1,11 +1,17 @@ # Input DEPENDPATH += . json qt HEADERS += qt/bitcoingui.h \ + ledger/base58.h \ + ledger/bip32.h \ + ledger/bytes.h \ ledger/comm.h \ ledger/error.h \ + ledger/hash.h \ ledger/hid_device.h \ ledger/ledger.h \ ledger/transport.h \ + ledger/tx.h \ + ledger/uint256.h \ ledger/utils.h \ qt/transactiontablemodel.h \ qt/addresstablemodel.h \ @@ -206,10 +212,12 @@ HEADERS += \ SOURCES += qt/bitcoin.cpp \ + ledger/bip32.cpp \ ledger/error.cpp \ ledger/hid_device.cpp \ ledger/ledger.cpp \ ledger/transport.cpp \ + ledger/tx.cpp \ ledger/utils.cpp \ qt/bitcoingui.cpp \ qt/transactiontablemodel.cpp \ From 8026b01eee63a5ed1ebc14112cee0f40e3c33192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 15 Mar 2023 08:01:16 +0100 Subject: [PATCH 014/129] Fix getPublicKey path --- wallet/qt/addresstablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 97be8623b..95a9ff5fb 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -399,7 +399,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con auto e = l.open(); std::stringstream pathSS; - pathSS << "m/44'/146'/" << ledgerAccount << "/0/" << ledgerIndex; + pathSS << "m/44'/146'/" << ledgerAccount << "'/0/" << ledgerIndex; auto path = pathSS.str(); auto result = l.GetPublicKey(path, true); From 8cce89e25d30b882cd55a71a56a612e898e92c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 15 Mar 2023 08:03:13 +0100 Subject: [PATCH 015/129] Add speculos support --- clean.sh | 2 +- wallet/ledger/ledger.cpp | 2 +- wallet/ledger/ledger.h | 2 +- wallet/ledger/speculos.cpp | 111 ++++++++++++++++++++++++++++++++++++ wallet/ledger/speculos.h | 28 +++++++++ wallet/ledger/transport.cpp | 4 ++ wallet/ledger/transport.h | 1 + wallet/wallet.pri | 2 + 8 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 wallet/ledger/speculos.cpp create mode 100644 wallet/ledger/speculos.h diff --git a/clean.sh b/clean.sh index 097063fd1..3d3995f65 100755 --- a/clean.sh +++ b/clean.sh @@ -9,7 +9,7 @@ mkdir ~/.neblio cat < ~/.neblio/neblio.conf regtest=1 server=1 -port=9999 +port=10000 rpcport=6326 listenonion=0 rpcuser=user diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index a8ebdfcee..34e085550 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -11,7 +11,7 @@ namespace ledger { - Ledger::Ledger() { this->transport_ = std::unique_ptr(new Transport(Transport::TransportType::HID)); } + Ledger::Ledger(Transport::TransportType transportType) { this->transport_ = std::unique_ptr(new Transport(transportType)); } Ledger::~Ledger() { transport_->close(); } diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index c65954f39..40274bcea 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -21,7 +21,7 @@ namespace ledger }; public: - Ledger(); + Ledger(Transport::TransportType transportType = Transport::TransportType::HID); ~Ledger(); Error open(); diff --git a/wallet/ledger/speculos.cpp b/wallet/ledger/speculos.cpp new file mode 100644 index 000000000..bf19927a6 --- /dev/null +++ b/wallet/ledger/speculos.cpp @@ -0,0 +1,111 @@ +#include "error.h" +#include "speculos.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ledger +{ + Error Speculos::open() + { + if (!opened_) + { + auto _sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (_sockfd < 0) + return Error::DEVICE_OPEN_FAIL; + + auto server = "localhost"; + auto port = "9999"; + + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + serv_addr.sin_port = htons(9999); + if (connect(_sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) + return Error::DEVICE_OPEN_FAIL; + + this->sockfd = _sockfd; + opened_ = true; + } + + return Error::SUCCESS; + } + + int Speculos::send(const bytes &data) + { + bytes d; + utils::AppendUint32(d, data.size()); + utils::AppendVector(d, data); + auto result = write(sockfd, d.data(), d.size()); + if (result == -1) + return -1; + return result; + } + + int Speculos::recv(bytes &rdata) + { + uint8_t lengthB[4]; + if (read(sockfd, lengthB, sizeof(lengthB)) == -1) + return -1; + + auto length = utils::BytesToInt(bytes(lengthB, lengthB + sizeof(lengthB))); + uint8_t d[length]; + if (read(sockfd, d, sizeof(d)) == -1) + return -1; + + uint8_t sw[2]; + if (read(sockfd, sw, sizeof(sw)) == -1) + return -1; + + rdata = bytes(d, d + sizeof(d)); + return utils::BytesToInt(bytes(sw, sw + sizeof(sw))); + } + + void Speculos::close() noexcept + { + if (opened_) + { + ::close(sockfd); + opened_ = false; + } + } + + bool Speculos::is_open() const + { + return opened_; + } + + std::vector Speculos::enumerate_devices(unsigned short vendor_id) noexcept + { + std::vector devices; + + struct hid_device_info *devs, *cur_dev; + + devs = hid_enumerate(vendor_id, 0x0); + cur_dev = devs; + while (cur_dev) + { + if (cur_dev->interface_number == 0 || + // MacOS specific + cur_dev->usage_page == 0xffa0) + { + devices.emplace_back(cur_dev->path); + } + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); + + return devices; + } +} // namespace ledger diff --git a/wallet/ledger/speculos.h b/wallet/ledger/speculos.h new file mode 100644 index 000000000..196206b03 --- /dev/null +++ b/wallet/ledger/speculos.h @@ -0,0 +1,28 @@ +#pragma once + +#include "comm.h" + +#include "hidapi/hidapi.h" + +namespace ledger +{ + class Speculos final : public Comm + { + public: + Error open() override; + int send(const bytes &data) override; + int recv(bytes &rdata) override; + void close() noexcept override; + [[nodiscard]] bool is_open() const override; + + private: + static std::vector enumerate_devices(unsigned short vendor_id = 0x2c97) noexcept; + + int sockfd = -1; + hid_device *device_ = nullptr; + std::string path_ = {}; + bool opened_ = false; + const int timeout_ms_ = 60 * 1000; + unsigned short vendor_id_ = 0x2c97; // Ledger Vendor ID + }; +} // namespace ledger diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index d53bcc08b..2ab72ec64 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -1,5 +1,6 @@ #include "error.h" #include "hid_device.h" +#include "speculos.h" #include "transport.h" #include "utils.h" @@ -12,6 +13,9 @@ namespace ledger case TransportType::HID: comm_ = std::unique_ptr(new HID()); break; + case TransportType::SPECULOS: + comm_ = std::unique_ptr(new Speculos()); + break; } } diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index 1264f64d4..b45220f92 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -13,6 +13,7 @@ namespace ledger enum class TransportType : int { HID = 0, + SPECULOS = 1, }; Transport(TransportType type); diff --git a/wallet/wallet.pri b/wallet/wallet.pri index ab041887c..ff70732c6 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -9,6 +9,7 @@ HEADERS += qt/bitcoingui.h \ ledger/hash.h \ ledger/hid_device.h \ ledger/ledger.h \ + ledger/speculos.h \ ledger/transport.h \ ledger/tx.h \ ledger/uint256.h \ @@ -216,6 +217,7 @@ SOURCES += qt/bitcoin.cpp \ ledger/error.cpp \ ledger/hid_device.cpp \ ledger/ledger.cpp \ + ledger/speculos.cpp \ ledger/transport.cpp \ ledger/tx.cpp \ ledger/utils.cpp \ From 897cd90ac0178a6688bc3d55ad57042326f84367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Thu, 16 Mar 2023 15:21:18 +0100 Subject: [PATCH 016/129] Add working tx signing prototype --- wallet/ledger/ledger.cpp | 180 ++++++++------------------------------- wallet/ledger/ledger.h | 14 +-- wallet/ledger/tx.cpp | 164 ++++++++++++++++++++--------------- wallet/ledger/tx.h | 12 ++- wallet/ledgerBridge.cpp | 87 +++++++++++++++++++ wallet/ledgerBridge.h | 28 ++++++ wallet/script.cpp | 4 + wallet/wallet.cpp | 38 ++++++++- wallet/wallet.pri | 6 +- 9 files changed, 305 insertions(+), 228 deletions(-) create mode 100644 wallet/ledgerBridge.cpp create mode 100644 wallet/ledgerBridge.h diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 34e085550..4c84bcd80 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -7,6 +7,7 @@ #include "tx.h" #include +#include #include namespace ledger @@ -21,7 +22,8 @@ namespace ledger auto openError = transport_->open(); if (openError != ledger::Error::SUCCESS) { - throw ledger::error_message(openError); + // TODO GK - what should we be throwing? (in the whole file) + throw openError; } std::cout << "Ledger connection opened." << std::endl; } @@ -59,46 +61,24 @@ namespace ledger return {pubKey, std::string(address.begin(), address.end()), chainCode}; } - std::tuple Ledger::GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &transactionData) + bytes Ledger::GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &transactionData) { + // TODO GK - refactor transport to throw instead of returning error auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); auto err = std::get<0>(result); auto buffer = std::get<1>(result); if (err != Error::SUCCESS) - return {err, {}}; + throw err; - return {err, bytes(buffer.begin(), buffer.end())}; + return buffer; } - std::tuple Ledger::GetTrustedInput(uint32_t indexLookup, Tx tx) + bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) { - bytes serializedTransaction; - utils::AppendUint32(serializedTransaction, tx.version, true); - utils::AppendUint32(serializedTransaction, tx.time, true); - - utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.inputs.size())); - for (auto input : tx.inputs) - { - utils::AppendVector(serializedTransaction, input.prevout); - utils::AppendVector(serializedTransaction, utils::CreateVarint(input.script.size())); - utils::AppendVector(serializedTransaction, input.script); - utils::AppendUint32(serializedTransaction, input.sequence); - } - - utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.outputs.size())); - for (auto output : tx.outputs) - { - utils::AppendUint64(serializedTransaction, output.amount, true); - utils::AppendVector(serializedTransaction, utils::CreateVarint(output.script.size())); - utils::AppendVector(serializedTransaction, output.script); - } - - utils::AppendUint32(serializedTransaction, tx.locktime); - - return GetTrustedInput(indexLookup, serializedTransaction); + return GetTrustedInput(SerializeTransaction(utxoTx), indexLookup); } - std::tuple Ledger::GetTrustedInput(uint32_t indexLookup, const bytes &serializedTransaction) + bytes Ledger::GetTrustedInput(const bytes &serializedTransaction, uint32_t indexLookup) { auto MAX_CHUNK_SIZE = 255; std::vector chunks; @@ -119,21 +99,15 @@ namespace ledger auto isFirst = true; bytes finalResults; for (auto &chunk : chunks) - { - auto result = GetTrustedInputRaw(isFirst, 0, chunk); - if (std::get<0>(result) != Error::SUCCESS) - { - return {std::get<0>(result), {}}; - } - - isFirst = false; - finalResults = std::get<1>(result); + { + finalResults = GetTrustedInputRaw(isFirst, 0, chunk); + isFirst = false; } - return {Error::SUCCESS, finalResults}; + return finalResults; } - void Ledger::UntrustedHashTxInputFinalize(Tx tx, const std::string &changePath) + void Ledger::UntrustedHashTxInputFinalize(const Tx &tx, const std::string &changePath) { auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE; auto p2 = 0x00; @@ -187,11 +161,11 @@ namespace ledger } } - void Ledger::UntrustedHashTxInputStart(Tx tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction) + void Ledger::UntrustedHashTxInputStart(const Tx &tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction) { auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START; auto p1 = 0x00; - auto p2 = isNewTransaction ? 0x02 : 0x80; + auto p2 = isNewTransaction ? 0x00 : 0x80; bytes data; utils::AppendUint32(data, tx.version, true); @@ -224,7 +198,7 @@ namespace ledger bytes scriptData; utils::AppendVector(scriptData, _script); - utils::AppendUint32(scriptData, 0xfffffffd, true); + utils::AppendUint32(scriptData, 0xffffffff, true); result = transport_->exchange(APDU::CLA, ins, p1, p2, scriptData); err = std::get<0>(result); @@ -234,30 +208,10 @@ namespace ledger } } - std::vector> Ledger::SignTransaction(const std::string &address, uint64_t amount, uint64_t fees, const std::string &changePath, const std::vector &signPaths, const std::vector> &rawUtxos, uint32_t locktime) - { - Tx tx; - tx.version = 2; - tx.time = 0; - tx.locktime = locktime; - - // build UTxOs and count amount available - std::vector utxos; - uint64_t amountAvailable = 0; - for (const auto &rawUtxo : rawUtxos) - { - Utxo utxo; - utxo.raw = std::get<0>(rawUtxo); - utxo.index = std::get<1>(rawUtxo); - - auto utxoTx = ledger::DeserializeTransaction(utxo.raw); - utxo.tx = utxoTx; - - utxos.push_back(utxo); - - auto amount = utxoTx.outputs[utxo.index].amount; - amountAvailable += amount; - } + std::vector> Ledger::SignTransaction(const Tx &tx, const std::string& changePath, const std::vector &signPaths, const std::vector &utxos) + { + assert(tx.inputs.size() == signPaths.size()); + assert(tx.inputs.size() == utxos.size()); // get trusted inputs std::vector trustedInputs; @@ -265,97 +219,33 @@ namespace ledger { const auto &utxo = utxos[i]; - const auto serializedTrustedInputResult = GetTrustedInput(utxo.index, utxo.tx); - auto trustedInput = ledger::DeserializeTrustedInput(std::get<1>(serializedTrustedInputResult)); - - TxInput txInput; - txInput.prevout = trustedInput.prevTxId; + const auto serializedTrustedInput = GetTrustedInput(utxo.tx, utxo.outputIndex); + const auto trustedInput = ledger::DeserializeTrustedInput(serializedTrustedInput); - auto publicKeyResult = GetPublicKey(signPaths[i], false); - auto publicKey = utils::CompressPubKey(std::get<0>(publicKeyResult)); - - auto pubKeyHash = Hash160(publicKey); - bytes pubKeyHashVector(pubKeyHash.begin(), pubKeyHash.end()); - - bytes finalScriptPubKey; - finalScriptPubKey.push_back(0x76); - finalScriptPubKey.push_back(0xa9); - finalScriptPubKey.push_back(0x14); - utils::AppendVector(finalScriptPubKey, pubKeyHashVector); - finalScriptPubKey.push_back(0x88); - finalScriptPubKey.push_back(0xac); - - txInput.script = finalScriptPubKey; - txInput.sequence = 0xfffffffd; + assert(trustedInput.prevTxId == tx.inputs[i].prevout.hash); trustedInputs.push_back(trustedInput); - tx.inputs.push_back(txInput); - } - - // create change output - if (amountAvailable - fees > amount) - { - auto publicKeyResult = GetPublicKey(changePath, false); - auto publicKey = utils::CompressPubKey(std::get<0>(publicKeyResult)); - auto publicKeyHash = Hash160(publicKey); - - // TODO GK - other key structures? - bytes changeScriptPublicKey; - changeScriptPublicKey.push_back(0x76); - changeScriptPublicKey.push_back(0xa9); - changeScriptPublicKey.push_back(0x14); - utils::AppendVector(changeScriptPublicKey, bytes(publicKeyHash.begin(), publicKeyHash.end())); - changeScriptPublicKey.push_back(0x88); - changeScriptPublicKey.push_back(0xac); - - TxOutput txChangeOutput; - // TODO GK - fix amount - txChangeOutput.amount = amount - fees; - txChangeOutput.script = changeScriptPublicKey; - tx.outputs.push_back(txChangeOutput); - } - - // create output to address - // TODO GK - other key structures? - bytes scriptPublicKey; - scriptPublicKey.push_back(0x76); - scriptPublicKey.push_back(0xa9); - scriptPublicKey.push_back(0x14); - auto addressDecoded = Base58Decode(address); - utils::AppendVector(scriptPublicKey, bytes(addressDecoded.begin() + 1, addressDecoded.end() - 4)); - scriptPublicKey.push_back(0x88); - scriptPublicKey.push_back(0xac); - - TxOutput txOutput; - txOutput.amount = amount; - txOutput.script = scriptPublicKey; - tx.outputs.push_back(txOutput); - - for (auto i = 0; i < tx.inputs.size(); i++) - { - UntrustedHashTxInputStart(tx, trustedInputs, i, tx.inputs[i].script, i == 0); } - UntrustedHashTxInputFinalize(tx, changePath); - - std::vector> signatures; + std::vector> signatures; for (auto i = 0; i < tx.inputs.size(); i++) { - UntrustedHashTxInputStart(tx, {trustedInputs[i]}, 0, tx.inputs[i].script, false); + auto &script = utxos[i].tx.outputs[utxos[i].outputIndex].script; + UntrustedHashTxInputStart(tx, trustedInputs, i, script, i == 0); - auto amount = tx.outputs[i].amount; + UntrustedHashTxInputFinalize(tx, changePath); auto ins = INS_UNTRUSTED_HASH_SIGN; auto p1 = 0x00; auto p2 = 0x00; - auto serializedChangePath = bip32::ParseHDKeypath(signPaths[i]); + auto serializedSignPath = bip32::ParseHDKeypath(signPaths[i]); bytes data; - data.push_back(serializedChangePath.size() / 4); - utils::AppendVector(data, serializedChangePath); + data.push_back(serializedSignPath.size() / 4); + utils::AppendVector(data, serializedSignPath); data.push_back(0x00); - utils::AppendUint32(data, locktime); + utils::AppendUint32(data, tx.locktime); data.push_back(0x01); auto result = transport_->exchange(APDU::CLA, ins, p1, p2, data); @@ -369,11 +259,11 @@ namespace ledger bytes data; data.push_back(0x30); utils::AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); - signatures.push_back({{1}, data}); + signatures.push_back({1, data}); } else { - signatures.push_back({{0}, buffer}); + signatures.push_back({0, buffer}); } } diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 40274bcea..99ace0caa 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -27,19 +27,19 @@ namespace ledger Error open(); std::tuple GetPublicKey(const std::string &path, bool confirm); - std::vector> SignTransaction(const std::string &address, uint64_t amount, uint64_t fees, const std::string &changePath, const std::vector &signPaths, const std::vector> &rawUtxos, uint32_t locktime); + std::vector> SignTransaction(const Tx &tx,const std::string& changePath, const std::vector &signPaths, const std::vector &utxos); void close(); private: std::unique_ptr transport_; - std::tuple ProcessScriptBlocks(const bytes &script, uint32_t sequence); - std::tuple GetTrustedInput(uint32_t indexLookup, Tx tx); - std::tuple GetTrustedInput(uint32_t indexLookup, const bytes &serializedTransaction); - std::tuple GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &data); - void UntrustedHashTxInputFinalize(Tx tx, const std::string &changePath); - void UntrustedHashTxInputStart(Tx tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction); + bytes ProcessScriptBlocks(const bytes &script, uint32_t sequence); + bytes GetTrustedInput(const Tx &utxoTx, uint32_t indexLookup); + bytes GetTrustedInput(const bytes &serializedTransaction, uint32_t indexLookup); + bytes GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &data); + void UntrustedHashTxInputFinalize(const Tx &tx, const std::string &changePath); + void UntrustedHashTxInputStart(const Tx &tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction); TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); }; } diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index d29a1feec..2d290ab42 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -3,99 +3,127 @@ namespace ledger { - Tx DeserializeTransaction(const bytes &transaction) +bytes SerializeTransaction(const Tx& tx) { + bytes serializedTransaction; + utils::AppendUint32(serializedTransaction, tx.version, true); + utils::AppendUint32(serializedTransaction, tx.time, true); + + utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.inputs.size())); + for (auto input : tx.inputs) { - Tx tx; - tx.inputs = std::vector(); - tx.outputs = std::vector(); + utils::AppendVector(serializedTransaction, input.prevout.hash); + utils::AppendUint32(serializedTransaction, input.prevout.index); + utils::AppendVector(serializedTransaction, utils::CreateVarint(input.script.size())); + utils::AppendVector(serializedTransaction, input.script); + utils::AppendUint32(serializedTransaction, input.sequence); + } - auto offset = 0; + utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.outputs.size())); + for (auto output : tx.outputs) + { + utils::AppendUint64(serializedTransaction, output.amount, true); + utils::AppendVector(serializedTransaction, utils::CreateVarint(output.script.size())); + utils::AppendVector(serializedTransaction, output.script); + } - tx.version = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); - offset += 4; + utils::AppendUint32(serializedTransaction, tx.locktime); + return serializedTransaction; +} - tx.time = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); - offset += 4; +Tx DeserializeTransaction(const bytes& transaction) +{ + Tx tx; + tx.inputs = std::vector(); + tx.outputs = std::vector(); - auto varint = utils::DeserializeVarint(transaction, offset); - auto inputsCount = std::get<0>(varint); - offset += std::get<1>(varint); + auto offset = 0; - auto flags = 0; - if (inputsCount == 0) - { - flags = utils::BytesToInt(utils::Splice(transaction, offset, 1)); - offset += 1; + tx.version = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); + offset += 4; - varint = utils::DeserializeVarint(transaction, offset); - inputsCount = std::get<0>(varint); - offset += std::get<1>(varint); - } + tx.time = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); + offset += 4; - for (auto i = 0; i < inputsCount; i++) - { - TxInput input; + auto varint = utils::DeserializeVarint(transaction, offset); + auto inputsCount = std::get<0>(varint); + offset += std::get<1>(varint); - input.prevout = utils::Splice(transaction, offset, 36); - offset += 36; + auto flags = 0; + if (inputsCount == 0) { + flags = utils::BytesToInt(utils::Splice(transaction, offset, 1)); + offset += 1; - varint = utils::DeserializeVarint(transaction, offset); - offset += std::get<1>(varint); - input.script = utils::Splice(transaction, offset, std::get<0>(varint)); + varint = utils::DeserializeVarint(transaction, offset); + inputsCount = std::get<0>(varint); + offset += std::get<1>(varint); + } - offset += std::get<0>(varint); - input.sequence = utils::BytesToInt(utils::Splice(transaction, offset, 4)); - offset += 4; + for (auto i = 0; i < inputsCount; i++) { + TxInput input; - tx.inputs.push_back(input); - } + TxPrevout prevout; + prevout.hash = utils::Splice(transaction, offset, 32); + offset += 32; + prevout.index = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + offset += 4; + + input.prevout = prevout; varint = utils::DeserializeVarint(transaction, offset); - auto numberOutputs = std::get<0>(varint); offset += std::get<1>(varint); + input.script = utils::Splice(transaction, offset, std::get<0>(varint)); - for (auto i = 0; i < numberOutputs; i++) - { - TxOutput output; + offset += std::get<0>(varint); + input.sequence = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + offset += 4; - output.amount = utils::BytesToUint64(utils::Splice(transaction, offset, 8), true); - offset += 8; + tx.inputs.push_back(input); + } - varint = utils::DeserializeVarint(transaction, offset); - offset += std::get<1>(varint); + varint = utils::DeserializeVarint(transaction, offset); + auto numberOutputs = std::get<0>(varint); + offset += std::get<1>(varint); - output.script = utils::Splice(transaction, offset, std::get<0>(varint)); - offset += std::get<0>(varint); + for (auto i = 0; i < numberOutputs; i++) { + TxOutput output; - tx.outputs.push_back(output); - } + output.amount = utils::BytesToUint64(utils::Splice(transaction, offset, 8), true); + offset += 8; + + varint = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(varint); - if (flags != 0) - { - TxWitness txWitness; - for (auto i = 0; i < inputsCount; i++) - { - auto numberOfWitnesses = utils::DeserializeVarint(transaction, offset); - offset += std::get<1>(numberOfWitnesses); - - TxInWitness txInWitness; - ScriptWitness scriptWitness; - for (auto j = 0; j < std::get<0>(numberOfWitnesses); j++) - { - auto scriptWitnessSize = utils::DeserializeVarint(transaction, offset); - offset += std::get<1>(scriptWitnessSize); - scriptWitness.stack.push_back(bytes(transaction.begin() + offset, transaction.begin() + offset + std::get<0>(scriptWitnessSize))); - offset += std::get<0>(scriptWitnessSize); - } - - txInWitness.scriptWitness = scriptWitness; - txWitness.txInWitnesses.push_back(txInWitness); + output.script = utils::Splice(transaction, offset, std::get<0>(varint)); + offset += std::get<0>(varint); + + tx.outputs.push_back(output); + } + + if (flags != 0) { + TxWitness txWitness; + for (auto i = 0; i < inputsCount; i++) { + auto numberOfWitnesses = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(numberOfWitnesses); + + TxInWitness txInWitness; + ScriptWitness scriptWitness; + for (auto j = 0; j < std::get<0>(numberOfWitnesses); j++) { + auto scriptWitnessSize = utils::DeserializeVarint(transaction, offset); + offset += std::get<1>(scriptWitnessSize); + scriptWitness.stack.push_back( + bytes(transaction.begin() + offset, + transaction.begin() + offset + std::get<0>(scriptWitnessSize))); + offset += std::get<0>(scriptWitnessSize); } + + txInWitness.scriptWitness = scriptWitness; + txWitness.txInWitnesses.push_back(txInWitness); } + } - tx.locktime = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + tx.locktime = utils::BytesToInt(utils::Splice(transaction, offset, 4)); - return tx; + return tx; } TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput) diff --git a/wallet/ledger/tx.h b/wallet/ledger/tx.h index 5256072f8..63b7c0d83 100644 --- a/wallet/ledger/tx.h +++ b/wallet/ledger/tx.h @@ -5,9 +5,15 @@ namespace ledger { + struct TxPrevout + { + bytes hash; + uint32_t index; + }; + struct TxInput { - bytes prevout; + TxPrevout prevout; bytes script; uint32_t sequence; }; @@ -55,11 +61,11 @@ namespace ledger struct Utxo { - bytes raw; - uint32_t index; Tx tx; + uint32_t outputIndex; }; + bytes SerializeTransaction(const Tx &tx); Tx DeserializeTransaction(const bytes &transaction); TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); } diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp new file mode 100644 index 000000000..2a69b3ccc --- /dev/null +++ b/wallet/ledgerBridge.cpp @@ -0,0 +1,87 @@ +#include "ledger/ledger.h" +#include "ledger/tx.h" +#include "ledgerBridge.h" + +#include +#include + +namespace ledgerbridge +{ + LedgerBridge::LedgerBridge() {} + + LedgerBridge::~LedgerBridge() {} + + void LedgerBridge::SignTransaction(CWalletTx &wtxNew, const std::vector &utxos) { + // TODO GK - proper paths + auto changePath = "44'/146'/0'/0/0"; + std::vector sigPaths = {changePath}; + + // transform wallet tx + ledger::Tx tx = ToLedgerTx(wtxNew); + // transform UTxOs + std::vector ledgerUtxos; + for (const auto &utxo : utxos){ + ledgerUtxos.push_back({ToLedgerTx(utxo.transaction), utxo.outputIndex}); + } + + ledger::Ledger ledger(ledger::Transport::TransportType::SPECULOS); + ledger.open(); + + // sign tx + auto signTxResults = ledger.SignTransaction(tx, changePath, sigPaths, ledgerUtxos); + + // add signatures to tx and verify + for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { + auto signature = std::get<1>(signTxResults[sigIndex]); + + auto pubKeyResult = ledger.GetPublicKey(sigPaths[sigIndex], false); + auto pubKey = CPubKey(ledger::utils::CompressPubKey(std::get<0>(pubKeyResult))); + + // hash type + signature.push_back(0x01); + + auto txIn = &wtxNew.vin[sigIndex]; + txIn->scriptSig << signature; + txIn->scriptSig << pubKey; + + if (!VerifyScript(txIn->scriptSig, utxos[sigIndex].outputPubKey, wtxNew, sigIndex, true, false, 0).isOk()) { + throw std::exception(); + } + } + + ledger.close(); + } + + ledger::Tx LedgerBridge::ToLedgerTx(const CTransaction &tx) + { + ledger::Tx ledgerTx; + ledgerTx.version = tx.nVersion; + ledgerTx.time = tx.nTime; + + for (auto i = 0; i < tx.vin.size();i++){ + const auto& input = tx.vin[i]; + + ledger::TxPrevout prevout = { + .hash = ledger::bytes(input.prevout.hash.begin(), input.prevout.hash.end()), + .index = input.prevout.n + }; + + ledgerTx.inputs.push_back({ + prevout, + .script = input.scriptSig, + .sequence = input.nSequence + }); + } + + for (const auto& output: tx.vout) { + ledgerTx.outputs.push_back({ + .amount = (uint64_t) output.nValue, + .script = output.scriptPubKey + }); + } + + ledgerTx.locktime = tx.nLockTime; + + return ledgerTx; + } +} diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h new file mode 100644 index 000000000..7265a865e --- /dev/null +++ b/wallet/ledgerBridge.h @@ -0,0 +1,28 @@ +#include "ledger/tx.h" +#include "ledger/bytes.h" + +#include "wallet.h" +#include "script.h" + +#include + +namespace ledgerbridge +{ + struct LedgerBridgeUtxo + { + CTransaction transaction; + uint32_t outputIndex; + CScript outputPubKey; + }; + + class LedgerBridge + { + public: + LedgerBridge(); + ~LedgerBridge(); + + void SignTransaction(CWalletTx &wtxNew, const std::vector &utxos); + private: + ledger::Tx ToLedgerTx(const CTransaction& tx); + }; +} diff --git a/wallet/script.cpp b/wallet/script.cpp index cf8116f24..e12d71d62 100644 --- a/wallet/script.cpp +++ b/wallet/script.cpp @@ -1660,6 +1660,8 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) if (!Solver(CTxDB(), scriptPubKey, whichType, vSolutions)) return isminetype::ISMINE_NO; + // TODO GK - refactor + const CWallet *wallet = (CWallet*) &keystore; CKeyID keyID; switch (whichType) { case TX_NONSTANDARD: @@ -1674,6 +1676,8 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return isminetype::ISMINE_SPENDABLE; + if (wallet->HaveLedgerKey(keyID)) + return isminetype::ISMINE_LEDGER; break; case TX_SCRIPTHASH: { CScript subscript; diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index a72d27d23..f45f52956 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2233,6 +2233,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0 || ntp1TokenChangeExists) { // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so @@ -2333,6 +2334,16 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey), ISMINE_LEDGER)) { + isLedgerTx = true; + break; + } + } + + std::vector ledgerBridgeUtxos; + // Sign for (const PAIRTYPE(const CWalletTx*, unsigned int) & coin : setCoins) { // find the output from the set in the list of inputs of the new tx @@ -2349,8 +2360,29 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey}); + } else { + if (SignSignature(*this, *coin.first, wtxNew, nIn) != SignatureState::Verified) { + CreateErrorMsg(errorMsg, "Error while signing transactions inputs."); + return false; + } + } + } + + if (isLedgerTx) { + // TODO GK - ledger fee + if (nFeeRet == 0) { + nFeeRet = 20000; + continue; + } + + try { + ledgerbridge::LedgerBridge ledgerBridge; + ledgerBridge.SignTransaction(wtxNew, ledgerBridgeUtxos); + } catch (const std::exception& ex) { + CreateErrorMsg(errorMsg, "Error while signing Ledger transaction."); return false; } } @@ -2370,7 +2402,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector Date: Fri, 17 Mar 2023 08:43:09 +0100 Subject: [PATCH 017/129] Add missing includes --- wallet/ledgerBridge.cpp | 1 + wallet/wallet.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 2a69b3ccc..cb98da769 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -1,5 +1,6 @@ #include "ledger/ledger.h" #include "ledger/tx.h" +#include "ledger/utils.h" #include "ledgerBridge.h" #include diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index f45f52956..5bc135b6f 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -18,6 +18,9 @@ #include #include #include +#include "ledger/ledger.h" +#include "ledger/utils.h" +#include "ledgerBridge.h" using namespace std; From ad7b8033195f48940c000dbee5c915803cf117d3 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 17 Mar 2023 10:36:44 +0100 Subject: [PATCH 018/129] Validate ledger account and index --- wallet/qt/addresstablemodel.cpp | 27 +++++++++++++++++++++++---- wallet/qt/addresstablemodel.h | 18 +++++++++++------- wallet/qt/editaddressdialog.cpp | 26 ++++++++++++++++++++++---- wallet/qt/guiutil.cpp | 7 ++++++- wallet/qt/guiutil.h | 2 ++ wallet/qt/ui_editaddressdialog.h | 6 ------ 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 95a9ff5fb..69333a9d9 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -361,7 +361,7 @@ void AddressTableModel::updateEntry(const QString& address, const QString& label } QString AddressTableModel::addRow(const QString& type, const QString& label, const QString& address, - uint32_t ledgerAccount, uint32_t ledgerIndex) + const QString& ledgerAccount, const QString& ledgerIndex) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); @@ -395,21 +395,35 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } strAddress = CBitcoinAddress(newKey.GetID()).ToString(); } else if (type == ReceiveLedger) { + bool accountOk = true; + uint32_t account = ledgerAccount.toUInt(&accountOk); + if (!accountOk || !validateLedgerPathItem(account, ledger::utils::MAX_RECOMMENDED_ACCOUNT)) { + editStatus = INVALID_LEDGER_ACCOUNT; + return QString(); + } + + bool indexOk = true; + uint32_t index = ledgerIndex.toUInt(&indexOk); + if (!indexOk || !validateLedgerPathItem(index, ledger::utils::MAX_RECOMMENDED_INDEX)) { + editStatus = INVALID_LEDGER_INDEX; + return QString(); + } + ledger::Ledger l; auto e = l.open(); std::stringstream pathSS; - pathSS << "m/44'/146'/" << ledgerAccount << "'/0/" << ledgerIndex; + pathSS << "m/44'/146'/" << account << "'/0/" << index; auto path = pathSS.str(); auto result = l.GetPublicKey(path, true); - + l.close(); auto pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); CPubKey cpubkey(pubKey); - CLedgerKey ledgerKey(cpubkey, ledgerAccount, ledgerIndex); + CLedgerKey ledgerKey(cpubkey, account, index); wallet->AddLedgerKey(ledgerKey); strAddress = CBitcoinAddress(cpubkey.GetID()).ToString(); } else { @@ -470,3 +484,8 @@ void AddressTableModel::emitDataChanged(int idx) { emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length() - 1, QModelIndex())); } + +bool AddressTableModel::validateLedgerPathItem(uint32_t value, uint32_t top) const +{ + return 0 <= value && value <= top; +} diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index ef79b6a3e..f6d6ee0b6 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -36,12 +36,14 @@ class AddressTableModel : public QAbstractTableModel /** Return status of edit/insert operation */ enum EditStatus { - OK, /**< Everything ok */ - NO_CHANGES, /**< No changes were made during edit operation */ - INVALID_ADDRESS, /**< Unparseable address */ - DUPLICATE_ADDRESS, /**< Address already in address book */ - WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ - KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ + OK, /**< Everything ok */ + NO_CHANGES, /**< No changes were made during edit operation */ + INVALID_ADDRESS, /**< Unparseable address */ + DUPLICATE_ADDRESS, /**< Address already in address book */ + INVALID_LEDGER_ACCOUNT, /**< Ledger account outside of recommended range */ + INVALID_LEDGER_INDEX, /**< Ledger index outside of recommended range */ + WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ + KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ }; static const QString Send; /**< Specifies send address */ @@ -63,7 +65,7 @@ class AddressTableModel : public QAbstractTableModel /* Add an address to the model. Returns the added address on success, and an empty string otherwise. */ - QString addRow(const QString& type, const QString& label, const QString& address, const uint32_t ledgerAccount, const uint32_t ledgerIndex); + QString addRow(const QString& type, const QString& label, const QString& address, const QString& ledgerAccount, const QString& ledgerIndex); /* Look up label for address in address book, if not found return empty string. */ @@ -90,6 +92,8 @@ class AddressTableModel : public QAbstractTableModel /** Notify listeners that data changed. */ void emitDataChanged(int index); + bool validateLedgerPathItem(uint32_t value, uint32_t top) const; + signals: void defaultAddressChanged(const QString& address); diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 53d9fb9ff..9f7f12697 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -2,9 +2,11 @@ #include "ui_editaddressdialog.h" #include "addresstablemodel.h" #include "guiutil.h" +#include "ledger/utils.h" #include #include +#include EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : QDialog(parent), @@ -48,6 +50,12 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : // Ledger submenu collapsed by default ui->ledgerWidget->setVisible(false); ui->ledgerInfoLabel->setVisible(false); + + // Ledger account and index defaults and validators + ui->ledgerAccountEdit->setText("0"); + ui->ledgerIndexEdit->setText("0"); + GUIUtil::setupIntWidget(ui->ledgerAccountEdit, this, 0, ledger::utils::MAX_RECOMMENDED_ACCOUNT); + GUIUtil::setupIntWidget(ui->ledgerIndexEdit, this, 0, ledger::utils::MAX_RECOMMENDED_INDEX); } EditAddressDialog::~EditAddressDialog() @@ -87,8 +95,8 @@ bool EditAddressDialog::saveCurrentRow() isLedger ? AddressTableModel::ReceiveLedger : AddressTableModel::Receive, ui->labelEdit->text(), ui->addressEdit->text(), - ui->ledgerAccountEdit->text().toUInt(), - ui->ledgerIndexEdit->text().toUInt() + ui->ledgerAccountEdit->text(), + ui->ledgerIndexEdit->text() ); break; case NewSendingAddress: @@ -96,8 +104,8 @@ bool EditAddressDialog::saveCurrentRow() AddressTableModel::Send, ui->labelEdit->text(), ui->addressEdit->text(), - ui->ledgerAccountEdit->text().toUInt(), - ui->ledgerIndexEdit->text().toUInt() + ui->ledgerAccountEdit->text(), + ui->ledgerIndexEdit->text() ); break; case EditReceivingAddress: @@ -136,6 +144,16 @@ void EditAddressDialog::accept() tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); break; + case AddressTableModel::INVALID_LEDGER_ACCOUNT: + QMessageBox::warning(this, windowTitle(), + tr("The entered Ledger account \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerAccountEdit->text()).arg(ledger::utils::MAX_RECOMMENDED_ACCOUNT), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::INVALID_LEDGER_INDEX: + QMessageBox::warning(this, windowTitle(), + tr("The entered Ledger address index \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerIndexEdit->text()).arg(ledger::utils::MAX_RECOMMENDED_INDEX), + QMessageBox::Ok, QMessageBox::Ok); + break; case AddressTableModel::WALLET_UNLOCK_FAILURE: QMessageBox::critical(this, windowTitle(), tr("Could not unlock wallet."), diff --git a/wallet/qt/guiutil.cpp b/wallet/qt/guiutil.cpp index 6004831ab..9aeec9909 100644 --- a/wallet/qt/guiutil.cpp +++ b/wallet/qt/guiutil.cpp @@ -79,6 +79,12 @@ void setupAmountWidget(QLineEdit *widget, QWidget *parent) widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); } +void setupIntWidget(QLineEdit *widget, QWidget *parent, int bottom, int top) +{ + QIntValidator *intValidator = new QIntValidator(bottom, top, parent); + widget->setValidator(intValidator); +} + bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { // NovaCoin: check prefix @@ -460,4 +466,3 @@ void HelpMessageBox::showOrPrint() } } // namespace GUIUtil - diff --git a/wallet/qt/guiutil.h b/wallet/qt/guiutil.h index d84e63481..539794690 100644 --- a/wallet/qt/guiutil.h +++ b/wallet/qt/guiutil.h @@ -30,6 +30,8 @@ namespace GUIUtil void setupAddressWidget(QLineEdit *widget, QWidget *parent); void setupAmountWidget(QLineEdit *widget, QWidget *parent); + void setupIntWidget(QLineEdit *widget, QWidget *parent, int bottom, int top); + // Parse "neblio:" URI into recipient object, return true on successful parsing // See Bitcoin URI definition discussion here: https://bitcointalk.org/index.php?topic=33490.0 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out); diff --git a/wallet/qt/ui_editaddressdialog.h b/wallet/qt/ui_editaddressdialog.h index 6c56fd774..a76bd4a02 100644 --- a/wallet/qt/ui_editaddressdialog.h +++ b/wallet/qt/ui_editaddressdialog.h @@ -21,8 +21,6 @@ #include #include #include -#include -#include "ledger/utils.h" QT_BEGIN_NAMESPACE @@ -99,8 +97,6 @@ class Ui_EditAddressDialog ledgerAccountEdit = new QLineEdit(EditAddressDialog); ledgerAccountEdit->setObjectName(QStringLiteral("ledgerAccountEdit")); - ledgerAccountEdit->setValidator(new QIntValidator(0, ledger::utils::MAX_RECOMMENDED_ACCOUNT)); - ledgerAccountEdit->setText("0"); ledgerFormLayout->setWidget(0, QFormLayout::FieldRole, ledgerAccountEdit); ledgerIndexLabel = new QLabel(EditAddressDialog); @@ -109,8 +105,6 @@ class Ui_EditAddressDialog ledgerIndexEdit = new QLineEdit(EditAddressDialog); ledgerIndexEdit->setObjectName(QStringLiteral("ledgerIndexEdit")); - ledgerIndexEdit->setValidator(new QIntValidator(0, ledger::utils::MAX_RECOMMENDED_INDEX)); - ledgerIndexEdit->setText("0"); ledgerFormLayout->setWidget(1, QFormLayout::FieldRole, ledgerIndexEdit); verticalLayout->addWidget(ledgerWidget); From 77e8bb65fb9f3b9126ec883ade52441f0a206ead Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 17 Mar 2023 14:27:32 +0100 Subject: [PATCH 019/129] Add GetBip32Path util function --- wallet/ledger/utils.cpp | 7 +++++++ wallet/ledger/utils.h | 1 + wallet/ledgerBridge.cpp | 16 ++++++++-------- wallet/qt/addresstablemodel.cpp | 7 ++----- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index dfdcee9b3..2f6dbd062 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -200,4 +200,11 @@ namespace ledger::utils return compressedPubKey; } + + std::string GetBip32Path(uint32_t account, uint32_t index) + { + std::stringstream ss; + ss << "m/44'/146'/" << account << "'/0/" << index; + return ss.str(); + } } // namespace ledger::utils diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index 29c0874e9..e17a38352 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -27,6 +27,7 @@ namespace ledger::utils void AppendUint64(bytes &vector, uint64_t n, bool littleEndian = false); bytes Splice(const bytes &vec, int start, int length); bytes CompressPubKey(const bytes &pubKey); + std::string GetBip32Path(uint32_t account, uint32_t index); const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; const uint32_t MAX_RECOMMENDED_INDEX = 50000; diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index cb98da769..d2f5587ab 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -9,12 +9,12 @@ namespace ledgerbridge { LedgerBridge::LedgerBridge() {} - + LedgerBridge::~LedgerBridge() {} void LedgerBridge::SignTransaction(CWalletTx &wtxNew, const std::vector &utxos) { // TODO GK - proper paths - auto changePath = "44'/146'/0'/0/0"; + std::string changePath = ledger::utils::GetBip32Path(0, 0); std::vector sigPaths = {changePath}; // transform wallet tx @@ -34,13 +34,13 @@ namespace ledgerbridge // add signatures to tx and verify for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { auto signature = std::get<1>(signTxResults[sigIndex]); - + auto pubKeyResult = ledger.GetPublicKey(sigPaths[sigIndex], false); auto pubKey = CPubKey(ledger::utils::CompressPubKey(std::get<0>(pubKeyResult))); // hash type signature.push_back(0x01); - + auto txIn = &wtxNew.vin[sigIndex]; txIn->scriptSig << signature; txIn->scriptSig << pubKey; @@ -58,14 +58,14 @@ namespace ledgerbridge ledger::Tx ledgerTx; ledgerTx.version = tx.nVersion; ledgerTx.time = tx.nTime; - + for (auto i = 0; i < tx.vin.size();i++){ const auto& input = tx.vin[i]; - + ledger::TxPrevout prevout = { - .hash = ledger::bytes(input.prevout.hash.begin(), input.prevout.hash.end()), + .hash = ledger::bytes(input.prevout.hash.begin(), input.prevout.hash.end()), .index = input.prevout.n - }; + }; ledgerTx.inputs.push_back({ prevout, diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 69333a9d9..0f5f5e6ec 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -410,12 +410,9 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } ledger::Ledger l; - auto e = l.open(); - - std::stringstream pathSS; - pathSS << "m/44'/146'/" << account << "'/0/" << index; - auto path = pathSS.str(); + auto e = l.open(); + std::string path = ledger::utils::GetBip32Path(account, index); auto result = l.GetPublicKey(path, true); l.close(); From b355baf0ff4b08b4597557a934e6c108279ce8c8 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 17 Mar 2023 14:28:22 +0100 Subject: [PATCH 020/129] Display Ledger Path in address book table --- wallet/qt/addressbookpage.cpp | 9 ++++++++- wallet/qt/addresstablemodel.cpp | 24 +++++++++++++++++++----- wallet/qt/addresstablemodel.h | 1 + wallet/qt/guiutil.cpp | 4 ++-- wallet/qt/guiutil.h | 2 +- wallet/qt/sendcoinsdialog.cpp | 2 +- wallet/qt/signverifymessagedialog.cpp | 4 ++-- 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index ce1bddc37..70a0c5793 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -133,11 +133,18 @@ void AddressBookPage::setModel(AddressTableModel *modelIn) ui->tableView->setModel(proxyModel); ui->tableView->sortByColumn(0, Qt::AscendingOrder); + // Hide unwanted columns + ui->tableView->hideColumn(AddressTableModel::IsLedger); + ui->tableView->hideColumn(AddressTableModel::LedgerAccount); + ui->tableView->hideColumn(AddressTableModel::LedgerIndex); + // Set column widths + ui->tableView->horizontalHeader()->setSectionResizeMode( + AddressTableModel::Label, QHeaderView::Stretch); ui->tableView->horizontalHeader()->resizeSection( AddressTableModel::Address, 320); ui->tableView->horizontalHeader()->setSectionResizeMode( - AddressTableModel::Label, QHeaderView::Stretch); + AddressTableModel::LedgerPath, QHeaderView::ResizeToContents); connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged())); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 0f5f5e6ec..250cdb859 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -183,7 +183,7 @@ class AddressTablePriv AddressTableModel::AddressTableModel(CWallet* walletIn, WalletModel* parent) : QAbstractTableModel(parent), walletModel(parent), wallet(walletIn), priv(0) { - columns << tr("Label") << tr("Address") << tr("Is Ledger") << tr("Account") << tr("Index"); + columns << tr("Label") << tr("Address") << tr("Is Ledger") << tr("Ledger Account") << tr("Ledger Index") << tr("Ledger Path"); priv = new AddressTablePriv(walletIn, this); priv->refreshAddressTable(); } @@ -221,18 +221,32 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const case Address: return rec->address; case IsLedger: - return isLedger ? "Yes" : ""; + return isLedger ? tr("Yes") : ""; case LedgerAccount: return isLedger ? QString::number(rec->ledgerAccount) : ""; case LedgerIndex: return isLedger ? QString::number(rec->ledgerIndex) : ""; + case LedgerPath: + { + // IsLedger, LedgerAccount and LedgerIndex are hidden, we display the info here + // (however, they still need to be above due to EditAddressDialog data mapping) + std::string path = ledger::utils::GetBip32Path(rec->ledgerAccount, rec->ledgerIndex); + return isLedger ? QString::fromStdString(path) : "-"; + } } } else if (role == Qt::FontRole) { - QFont font; if (index.column() == Address) { - font = GUIUtil::bitcoinAddressFont(); + return GUIUtil::monospaceFont(); + } + if (index.column() == LedgerPath) { + return GUIUtil::monospaceFont(); } - return font; + return QVariant(); + } else if (role == Qt::TextAlignmentRole) { + if (index.column() == LedgerPath) { + return Qt::AlignCenter; + } + return QVariant(); } else if (role == TypeRole) { switch (rec->type) { case AddressTableEntry::Sending: diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index f6d6ee0b6..3653f6f12 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -26,6 +26,7 @@ class AddressTableModel : public QAbstractTableModel IsLedger = 2, LedgerAccount = 3, LedgerIndex = 4, + LedgerPath = 5, }; enum RoleIndex diff --git a/wallet/qt/guiutil.cpp b/wallet/qt/guiutil.cpp index 9aeec9909..1813207df 100644 --- a/wallet/qt/guiutil.cpp +++ b/wallet/qt/guiutil.cpp @@ -52,7 +52,7 @@ QString dateTimeStr(qint64 nTime) return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); } -QFont bitcoinAddressFont() +QFont monospaceFont() { QFont font("Monospace"); #if QT_VERSION >= 0x040800 @@ -67,7 +67,7 @@ void setupAddressWidget(QLineEdit *widget, QWidget *parent) { widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); widget->setValidator(new BitcoinAddressValidator(parent)); - widget->setFont(bitcoinAddressFont()); + widget->setFont(monospaceFont()); } void setupAmountWidget(QLineEdit *widget, QWidget *parent) diff --git a/wallet/qt/guiutil.h b/wallet/qt/guiutil.h index 539794690..b0a2beaea 100644 --- a/wallet/qt/guiutil.h +++ b/wallet/qt/guiutil.h @@ -24,7 +24,7 @@ namespace GUIUtil QString dateTimeStr(qint64 nTime); // Render Bitcoin addresses in monospace font - QFont bitcoinAddressFont(); + QFont monospaceFont(); // Set up widgets for address and amounts void setupAddressWidget(QLineEdit *widget, QWidget *parent); diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 3f449879b..60552a5e7 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -52,7 +52,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget* parent) connect(ui->clearButton, &QPushButton::clicked, this, &SendCoinsDialog::clear); // Coin Control - ui->lineEditCoinControlChange->setFont(GUIUtil::bitcoinAddressFont()); + ui->lineEditCoinControlChange->setFont(GUIUtil::monospaceFont()); connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); diff --git a/wallet/qt/signverifymessagedialog.cpp b/wallet/qt/signverifymessagedialog.cpp index 5e859168f..d87ea3988 100644 --- a/wallet/qt/signverifymessagedialog.cpp +++ b/wallet/qt/signverifymessagedialog.cpp @@ -41,8 +41,8 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : ui->messageIn_VM->installEventFilter(this); ui->signatureIn_VM->installEventFilter(this); - ui->signatureOut_SM->setFont(GUIUtil::bitcoinAddressFont()); - ui->signatureIn_VM->setFont(GUIUtil::bitcoinAddressFont()); + ui->signatureOut_SM->setFont(GUIUtil::monospaceFont()); + ui->signatureIn_VM->setFont(GUIUtil::monospaceFont()); } SignVerifyMessageDialog::~SignVerifyMessageDialog() From 07bd6a24a4a9922194769d970b1585af251a8ab3 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 17 Mar 2023 16:47:26 +0100 Subject: [PATCH 021/129] Display Ledger path in edit address dialog --- wallet/ledger/utils.cpp | 9 +++++++-- wallet/ledger/utils.h | 1 + wallet/qt/editaddressdialog.cpp | 15 +++++++++++++++ wallet/qt/editaddressdialog.h | 2 ++ wallet/qt/ui_editaddressdialog.h | 5 +++++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index 2f6dbd062..e02372ead 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -201,10 +201,15 @@ namespace ledger::utils return compressedPubKey; } - std::string GetBip32Path(uint32_t account, uint32_t index) + std::string GetBip32Path(const std::string &account, const std::string &index) { std::stringstream ss; - ss << "m/44'/146'/" << account << "'/0/" << index; + ss << "m/44'/146'/" << account << "/0/" << index; return ss.str(); } + + std::string GetBip32Path(uint32_t account, uint32_t index) + { + return GetBip32Path(std::to_string(account), std::to_string(index)); + } } // namespace ledger::utils diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index e17a38352..2976619d6 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -27,6 +27,7 @@ namespace ledger::utils void AppendUint64(bytes &vector, uint64_t n, bool littleEndian = false); bytes Splice(const bytes &vec, int start, int length); bytes CompressPubKey(const bytes &pubKey); + std::string GetBip32Path(const std::string &account, const std::string &index); std::string GetBip32Path(uint32_t account, uint32_t index); const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 9f7f12697..09fe999bc 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -49,6 +49,7 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : // Ledger submenu collapsed by default ui->ledgerWidget->setVisible(false); + ui->ledgerPathLabel->setVisible(false); ui->ledgerInfoLabel->setVisible(false); // Ledger account and index defaults and validators @@ -56,6 +57,10 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : ui->ledgerIndexEdit->setText("0"); GUIUtil::setupIntWidget(ui->ledgerAccountEdit, this, 0, ledger::utils::MAX_RECOMMENDED_ACCOUNT); GUIUtil::setupIntWidget(ui->ledgerIndexEdit, this, 0, ledger::utils::MAX_RECOMMENDED_INDEX); + + connect(this->ui->ledgerAccountEdit, SIGNAL(textChanged(QString)), this, SLOT(updateLedgerPathLabel())); + connect(this->ui->ledgerIndexEdit, SIGNAL(textChanged(QString)), this, SLOT(updateLedgerPathLabel())); + updateLedgerPathLabel(); } EditAddressDialog::~EditAddressDialog() @@ -171,9 +176,19 @@ void EditAddressDialog::accept() QDialog::accept(); } +void EditAddressDialog::updateLedgerPathLabel() +{ + std::string path = ledger::utils::GetBip32Path( + ui->ledgerAccountEdit->text().toStdString(), + ui->ledgerIndexEdit->text().toStdString() + ); + ui->ledgerPathLabel->setText(tr("Ledger path: %1").arg(QString::fromStdString(path))); +} + void EditAddressDialog::on_ledgerCheckBox_toggled(bool checked) { ui->ledgerWidget->setVisible(checked); + ui->ledgerPathLabel->setVisible(checked); ui->ledgerInfoLabel->setVisible(checked); // reset dialog height after hiding ledger items diff --git a/wallet/qt/editaddressdialog.h b/wallet/qt/editaddressdialog.h index e30c760a9..0ff55e888 100644 --- a/wallet/qt/editaddressdialog.h +++ b/wallet/qt/editaddressdialog.h @@ -38,6 +38,8 @@ class EditAddressDialog : public QDialog public slots: void accept(); + void updateLedgerPathLabel(); + void on_ledgerCheckBox_toggled(bool checked); private: diff --git a/wallet/qt/ui_editaddressdialog.h b/wallet/qt/ui_editaddressdialog.h index a76bd4a02..68c0fddc2 100644 --- a/wallet/qt/ui_editaddressdialog.h +++ b/wallet/qt/ui_editaddressdialog.h @@ -42,6 +42,7 @@ class Ui_EditAddressDialog QLineEdit *ledgerAccountEdit; QLabel *ledgerIndexLabel; QLineEdit *ledgerIndexEdit; + QLabel *ledgerPathLabel; QLabel *ledgerInfoLabel; void setupUi(QDialog *EditAddressDialog) @@ -109,6 +110,10 @@ class Ui_EditAddressDialog verticalLayout->addWidget(ledgerWidget); + ledgerPathLabel = new QLabel(EditAddressDialog); + ledgerPathLabel->setObjectName(QStringLiteral("ledgerPathLabel")); + verticalLayout->addWidget(ledgerPathLabel); + ledgerInfoLabel = new QLabel(EditAddressDialog); ledgerInfoLabel->setObjectName(QStringLiteral("ledgerInfoLabel")); ledgerInfoLabel->setWordWrap(true); From f027e89aade9cb0b1ed0ae83c1a8fdc6bcf857a1 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 17 Mar 2023 20:45:38 +0100 Subject: [PATCH 022/129] Add ledger confirm dialog, ledger error handling --- wallet/ledger/error.cpp | 1 + wallet/ledger/error.h | 1 + wallet/ledger/ledger.cpp | 8 ++--- wallet/ledger/ledger.h | 2 +- wallet/qt/addresstablemodel.cpp | 32 +++++++++++++------ wallet/qt/addresstablemodel.h | 7 ++++- wallet/qt/editaddressdialog.cpp | 56 +++++++++++++++++++++++++++++++-- wallet/qt/editaddressdialog.h | 20 ++++++++++-- wallet/qt/transactionview.cpp | 2 +- 9 files changed, 107 insertions(+), 22 deletions(-) diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index 14eb53b21..7c9549bee 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -18,6 +18,7 @@ namespace ledger return "Failed to receive data from Ledger"; case Error::APDU_INVALID_CMD: return "Invalid Ledger data"; + case Error::UNRECOGNIZED_ERROR: default: return "Unrecognized error"; } diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index ab62f5722..08b8d29c5 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -10,6 +10,7 @@ namespace ledger { DEVICE_DATA_SEND_FAIL, DEVICE_DATA_RECV_FAIL, APDU_INVALID_CMD, + UNRECOGNIZED_ERROR = 999, }; std::string error_message(Error code); diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 4c84bcd80..345e7d4e3 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -16,7 +16,7 @@ namespace ledger Ledger::~Ledger() { transport_->close(); } - Error Ledger::open() + void Ledger::open() { std::cout << "Opening Ledger connection." << std::endl; auto openError = transport_->open(); @@ -40,7 +40,7 @@ namespace ledger auto err = std::get<0>(result); auto buffer = std::get<1>(result); if (err != Error::SUCCESS) - throw error_message(err); + throw err; auto offset = 1; auto pubKeyLen = (int)buffer[offset] * 16 + 1; @@ -56,7 +56,7 @@ namespace ledger offset += 32; if (offset != buffer.size()) - throw "Something went wrong"; + throw Error::UNRECOGNIZED_ERROR; return {pubKey, std::string(address.begin(), address.end()), chainCode}; } @@ -209,7 +209,7 @@ namespace ledger } std::vector> Ledger::SignTransaction(const Tx &tx, const std::string& changePath, const std::vector &signPaths, const std::vector &utxos) - { + { assert(tx.inputs.size() == signPaths.size()); assert(tx.inputs.size() == utxos.size()); diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 99ace0caa..4de1ec2c8 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -24,7 +24,7 @@ namespace ledger Ledger(Transport::TransportType transportType = Transport::TransportType::HID); ~Ledger(); - Error open(); + void open(); std::tuple GetPublicKey(const std::string &path, bool confirm); std::vector> SignTransaction(const Tx &tx,const std::string& changePath, const std::vector &signPaths, const std::vector &utxos); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 250cdb859..3e6fc3e09 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -1,5 +1,6 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "ledger/error.h" #include "ledger/ledger.h" #include "ledger/utils.h" #include "walletmodel.h" @@ -423,15 +424,20 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con return QString(); } - ledger::Ledger l; - auto e = l.open(); - - std::string path = ledger::utils::GetBip32Path(account, index); - auto result = l.GetPublicKey(path, true); - - l.close(); - - auto pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); + ledger::bytes pubKey; + + try { + ledger::Ledger l; + l.open(); + std::string path = ledger::utils::GetBip32Path(account, index); + auto result = l.GetPublicKey(path, true); + l.close(); + auto pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); + } catch (const ledger::Error& e) { + editStatus = LEDGER_ERROR; + ledgerError = e; + return QString(); + } CPubKey cpubkey(pubKey); CLedgerKey ledgerKey(cpubkey, account, index); @@ -491,6 +497,14 @@ int AddressTableModel::lookupAddress(const QString& address) const } } +std::string AddressTableModel::getLedgerErrorMessage() const { + if (editStatus != LEDGER_ERROR) + return ""; + if (ledgerError == ledger::Error::SUCCESS) + return "Unknown error"; + return ledger::error_message(ledgerError); +} + void AddressTableModel::emitDataChanged(int idx) { emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length() - 1, QModelIndex())); diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index 3653f6f12..917fe98f6 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -3,6 +3,7 @@ #include #include +#include "ledger/error.h" #include "wallet_ismine.h" class AddressTablePriv; @@ -44,7 +45,8 @@ class AddressTableModel : public QAbstractTableModel INVALID_LEDGER_ACCOUNT, /**< Ledger account outside of recommended range */ INVALID_LEDGER_INDEX, /**< Ledger index outside of recommended range */ WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ - KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */ + KEY_GENERATION_FAILURE, /**< Generating a new public key for a receiving address failed */ + LEDGER_ERROR /**< Ledger operation error */ }; static const QString Send; /**< Specifies send address */ @@ -79,6 +81,8 @@ class AddressTableModel : public QAbstractTableModel EditStatus getEditStatus() const { return editStatus; } + std::string getLedgerErrorMessage() const; + std::string purposeForAddress(const std::string& address) const; bool isWhitelisted(const std::string& address) const; @@ -89,6 +93,7 @@ class AddressTableModel : public QAbstractTableModel AddressTablePriv* priv; QStringList columns; EditStatus editStatus; + ledger::Error ledgerError; /** Notify listeners that data changed. */ void emitDataChanged(int index); diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 09fe999bc..1cda89cd2 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -7,6 +7,21 @@ #include #include #include +#include +#include + +void AddLedgerRowWorker::addRow(Ui::EditAddressDialog *ui, AddressTableModel *model, QSharedPointer workerPtr) { + auto address = model->addRow( + AddressTableModel::ReceiveLedger, + ui->labelEdit->text(), + ui->addressEdit->text(), + ui->ledgerAccountEdit->text(), + ui->ledgerIndexEdit->text() + ); + + emit resultReady(address); + workerPtr.reset(); +} EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : QDialog(parent), @@ -96,13 +111,38 @@ bool EditAddressDialog::saveCurrentRow() switch(mode) { case NewReceivingAddress: - address = model->addRow( - isLedger ? AddressTableModel::ReceiveLedger : AddressTableModel::Receive, + if (isLedger) + { + QThread addRowThread; + QSharedPointer worker = QSharedPointer::create(); + worker->moveToThread(&addRowThread); + + connect(worker.data(), SIGNAL(resultReady(QString)), &addRowThread, SLOT(quit())); + connect(&addRowThread, SIGNAL(finished()), &addRowThread, SLOT(deleteLater())); + + QMessageBox msgBox(this); + msgBox.setIcon(QMessageBox::Icon::Information); + msgBox.setWindowTitle(windowTitle()); + msgBox.setText("Please confirm or cancel the action on your Ledger device."); + msgBox.setStandardButtons(QMessageBox::StandardButton::NoButton); + + connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(setAddress(QString))); + connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(accept())); + + addRowThread.start(); + QTimer::singleShot(0, worker.data(), [this, worker]() { worker->addRow(ui, model, worker); }); + msgBox.exec(); + + addRowThread.wait(); // to make sure that the thread is finished + } else { + address = model->addRow( + AddressTableModel::Receive, ui->labelEdit->text(), ui->addressEdit->text(), ui->ledgerAccountEdit->text(), ui->ledgerIndexEdit->text() ); + } break; case NewSendingAddress: address = model->addRow( @@ -169,6 +209,11 @@ void EditAddressDialog::accept() tr("New key generation failed."), QMessageBox::Ok, QMessageBox::Ok); break; + case AddressTableModel::LEDGER_ERROR: + QMessageBox::critical(this, windowTitle(), + tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected and the Neblio app is opened on the device.").arg(QString::fromStdString(model->getLedgerErrorMessage())), + QMessageBox::Ok, QMessageBox::Ok); + break; } return; @@ -185,6 +230,11 @@ void EditAddressDialog::updateLedgerPathLabel() ui->ledgerPathLabel->setText(tr("Ledger path: %1").arg(QString::fromStdString(path))); } +void EditAddressDialog::setAddress(QString addressIn) +{ + this->address = addressIn; +} + void EditAddressDialog::on_ledgerCheckBox_toggled(bool checked) { ui->ledgerWidget->setVisible(checked); @@ -200,7 +250,7 @@ QString EditAddressDialog::getAddress() const return address; } -void EditAddressDialog::setAddress(const QString &addressIn) +void EditAddressDialog::setAddressEditValue(const QString &addressIn) { this->address = addressIn; ui->addressEdit->setText(addressIn); diff --git a/wallet/qt/editaddressdialog.h b/wallet/qt/editaddressdialog.h index 0ff55e888..308d32c84 100644 --- a/wallet/qt/editaddressdialog.h +++ b/wallet/qt/editaddressdialog.h @@ -12,6 +12,21 @@ namespace Ui { } class AddressTableModel; +/* Object for adding a Ledger address row in a separate thread. +*/ +class AddLedgerRowWorker : public QObject +{ + Q_OBJECT + +public slots: + // we use the shared pointer argument to ensure that workerPtr will be deleted after doing the + // retrieval + void addRow(Ui::EditAddressDialog *ui, AddressTableModel *model, QSharedPointer workerPtr); + +signals: + void resultReady(QString); +}; + /** Dialog for editing an address and associated information. */ class EditAddressDialog : public QDialog @@ -33,13 +48,12 @@ class EditAddressDialog : public QDialog void loadRow(int row); QString getAddress() const; - void setAddress(const QString &addressIn); + void setAddressEditValue(const QString &addressIn); public slots: void accept(); - void updateLedgerPathLabel(); - + void setAddress(QString addressIn); void on_ledgerCheckBox_toggled(bool checked); private: diff --git a/wallet/qt/transactionview.cpp b/wallet/qt/transactionview.cpp index c97b7b0e4..b70066ab9 100644 --- a/wallet/qt/transactionview.cpp +++ b/wallet/qt/transactionview.cpp @@ -397,7 +397,7 @@ void TransactionView::editLabel() // Add sending address EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, this); dlg.setModel(addressBook); - dlg.setAddress(address); + dlg.setAddressEditValue(address); dlg.exec(); } } From a5d3b19af320d5dc85026b0f998e4e60d2114b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 13:48:10 +0100 Subject: [PATCH 023/129] Add `ISMINE_SPENDABLE_AVAILABLE` --- wallet/qt/transactiondesc.cpp | 10 +++++----- wallet/qt/transactionrecord.cpp | 4 ++-- wallet/rpcwallet.cpp | 6 +++--- wallet/wallet.cpp | 4 ++-- wallet/wallet_ismine.h | 1 + 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/wallet/qt/transactiondesc.cpp b/wallet/qt/transactiondesc.cpp index d8f48993d..966c3456b 100644 --- a/wallet/qt/transactiondesc.cpp +++ b/wallet/qt/transactiondesc.cpp @@ -95,7 +95,7 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle if (wallet->IsMine(txout) != isminetype::ISMINE_NO) { CTxDestination address; if (ExtractDestination(txdb, txout.scriptPubKey, address) && - IsMineCheck(IsMine(*wallet, address), isminetype::ISMINE_SPENDABLE)) { + IsMineCheck(IsMine(*wallet, address), isminetype::ISMINE_SPENDABLE_AVAILABLE)) { if (const auto entry = wallet->mapAddressBook.get(address)) { strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; strHTML += "" + tr("To") + ": "; @@ -195,7 +195,7 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle } else { bool fAllFromMe = true; for (const CTxIn& txin : wtx.vin) { - fAllFromMe = fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE); + fAllFromMe = fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE_AVAILABLE); } bool fAllToMe = true; @@ -203,7 +203,7 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle if (NTP1Transaction::IsTxOutputOpRet(&txout)) { continue; } - fAllToMe = fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE); + fAllToMe = fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE); } if (fAllFromMe) { @@ -215,7 +215,7 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle if (NTP1Transaction::IsTxOutputOpRet(&txout)) { continue; } - if (IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE)) + if (IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE)) continue; if (!wtx.mapValue.count("to") || wtx.mapValue.at("to").empty()) { @@ -388,7 +388,7 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, vout.nValue); strHTML = strHTML + " IsMine=" + - (IsMineCheck(wallet->IsMine(vout), isminetype::ISMINE_SPENDABLE) ? tr("true") + (IsMineCheck(wallet->IsMine(vout), isminetype::ISMINE_SPENDABLE_AVAILABLE) ? tr("true") : tr("false")) + ""; strHTML = strHTML + " IsWatchOnly=" + diff --git a/wallet/qt/transactionrecord.cpp b/wallet/qt/transactionrecord.cpp index af84a25e5..6fedaf0c2 100644 --- a/wallet/qt/transactionrecord.cpp +++ b/wallet/qt/transactionrecord.cpp @@ -107,7 +107,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet* } else { bool fAllFromMe = true; for (const CTxIn& txin : wtx.vin) { - fAllFromMe = fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE); + fAllFromMe = fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE_AVAILABLE); } bool fAllToMe = true; @@ -116,7 +116,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet* if (NTP1Transaction::IsTxOutputOpRet(&txout, nullptr)) { continue; } - fAllToMe = fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE); + fAllToMe = fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE); } if (fAllFromMe && fAllToMe) { diff --git a/wallet/rpcwallet.cpp b/wallet/rpcwallet.cpp index f067918ab..f4aba6375 100644 --- a/wallet/rpcwallet.cpp +++ b/wallet/rpcwallet.cpp @@ -1276,7 +1276,7 @@ Value getbalance(const Array& params, bool fHelp) nMinDepth = params[1].get_int(); if (params.size() > 1) nMinDepth = params[1].get_int(); - isminefilter filter = static_cast(isminetype::ISMINE_SPENDABLE); + isminefilter filter = static_cast(isminetype::ISMINE_SPENDABLE_AVAILABLE); if (params.size() > 2 && params[2].get_bool()) filter = filter | static_cast(isminetype::ISMINE_WATCH_ONLY); if (!(params.size() > 3) || params[3].get_bool()) @@ -1973,7 +1973,7 @@ Value listtransactions(const Array& params, bool fHelp) if (params.size() > 2) nFrom = params[2].get_int(); - isminefilter filter = static_cast(isminetype::ISMINE_SPENDABLE); + isminefilter filter = static_cast(isminetype::ISMINE_SPENDABLE_AVAILABLE); if (params.size() > 3 && params[3].get_bool()) filter = filter | static_cast(isminetype::ISMINE_WATCH_ONLY); if (!(params.size() > 4) || params[4].get_bool()) @@ -2039,7 +2039,7 @@ Value listaccounts(const Array& params, bool fHelp) if (params.size() > 0) nMinDepth = params[0].get_int(); - isminefilter includeWatchonly = static_cast(isminetype::ISMINE_SPENDABLE); + isminefilter includeWatchonly = static_cast(isminetype::ISMINE_SPENDABLE_AVAILABLE); if (params.size() > 1 && params[1].get_bool()) includeWatchonly = includeWatchonly | static_cast(isminetype::ISMINE_WATCH_ONLY); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 5bc135b6f..4f262b4d2 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1018,7 +1018,7 @@ void CWalletTx::GetAmounts(const ITxDB& txdb, list if (pwallet->IsChange(txdb, txout)) continue; fIsMine = pwallet->IsMine(txout); - } else if (!IsMineCheck((fIsMine = pwallet->IsMine(txout)), ISMINE_SPENDABLE)) + } else if (!IsMineCheck((fIsMine = pwallet->IsMine(txout)), ISMINE_SPENDABLE_AVAILABLE)) continue; // In either case, we need to get the destination address @@ -3351,7 +3351,7 @@ bool CWalletTx::IsTrusted(const ITxDB& txdb, const uint256& bestBlockHash) const if (parent == nullptr) return false; const CTxOut& parentOut = parent->vout[txin.prevout.n]; - if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE_AVAILABLE) return false; } diff --git a/wallet/wallet_ismine.h b/wallet/wallet_ismine.h index d789114b0..3032edd90 100644 --- a/wallet/wallet_ismine.h +++ b/wallet/wallet_ismine.h @@ -31,6 +31,7 @@ enum isminetype : uint //! Indicates that this wallet belongs to the Ledger HW wallet ISMINE_LEDGER = 32, ISMINE_SPENDABLE_ALL = ISMINE_SPENDABLE_DELEGATED | ISMINE_SPENDABLE | ISMINE_LEDGER, + ISMINE_SPENDABLE_AVAILABLE = ISMINE_SPENDABLE | ISMINE_LEDGER, ISMINE_SPENDABLE_STAKEABLE = ISMINE_SPENDABLE_DELEGATED | ISMINE_COLD, // TODO GK - Ledger?? ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE | ISMINE_COLD | ISMINE_SPENDABLE_DELEGATED | ISMINE_LEDGER }; From 74b4b2c6fe93b92af279ff9cace799a0e557b846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 13:50:49 +0100 Subject: [PATCH 024/129] Switch `input.prevout.index` serialization to little endian --- wallet/ledger/tx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index 2d290ab42..1af2447b5 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -12,7 +12,7 @@ bytes SerializeTransaction(const Tx& tx) { for (auto input : tx.inputs) { utils::AppendVector(serializedTransaction, input.prevout.hash); - utils::AppendUint32(serializedTransaction, input.prevout.index); + utils::AppendUint32(serializedTransaction, input.prevout.index, true); utils::AppendVector(serializedTransaction, utils::CreateVarint(input.script.size())); utils::AppendVector(serializedTransaction, input.script); utils::AppendUint32(serializedTransaction, input.sequence); From 671517d2fb286cb57e4bec3b9742d9af812d1e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 13:53:16 +0100 Subject: [PATCH 025/129] Add `ISMINE_LEDGER` where needed --- wallet/script.cpp | 2 ++ wallet/wallet.cpp | 25 +++++++++++++++++++++++++ wallet/wallet.h | 4 +++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/wallet/script.cpp b/wallet/script.cpp index e12d71d62..a8d102efb 100644 --- a/wallet/script.cpp +++ b/wallet/script.cpp @@ -1671,6 +1671,8 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) keyID = CPubKey(vSolutions[0]).GetID(); if (keystore.HaveKey(keyID)) return isminetype::ISMINE_SPENDABLE; + if (wallet->HaveLedgerKey(keyID)) + return isminetype::ISMINE_LEDGER; break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 4f262b4d2..645558209 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -3410,6 +3410,14 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const debit += *c_DelegatedDebitCached; } } + if (filter & ISMINE_LEDGER) { + if (c_LedgerDebitCached) + debit += *c_LedgerDebitCached; + else { + c_LedgerDebitCached = pwallet->GetDebit(*this, ISMINE_LEDGER); + debit += *c_LedgerDebitCached; + } + } return debit; } @@ -3437,6 +3445,9 @@ void CWalletTx::Init(const CWallet* pwalletIn) c_DelegatedDebitCached = boost::none; c_DelegatedCreditCached = boost::none; + c_LedgerDebitCached = boost::none; + c_LedgerCreditCached = boost::none; + c_DebitCached = boost::none; c_CreditCached = boost::none; c_AvailableCreditCached = boost::none; @@ -3487,6 +3498,10 @@ CAmount CWalletTx::GetUnspentCredit(const ITxDB& txdb, const uint256& bestBlockH const auto f = ISMINE_SPENDABLE_DELEGATED; credit += pwallet->GetCredit(bestBlockHash, txdb, *this, f, true); } + if (filter & ISMINE_LEDGER) { + const auto f = ISMINE_LEDGER; + credit += pwallet->GetCredit(bestBlockHash, txdb, *this, f, true); + } return credit; } @@ -3559,6 +3574,14 @@ CAmount CWalletTx::GetCredit(const uint256& bestBlockHash, const ITxDB& txdb, credit += *c_DelegatedCreditCached; } } + if (filter & ISMINE_LEDGER) { + if (c_LedgerCreditCached) + credit += *c_LedgerCreditCached; + else { + c_LedgerCreditCached = pwallet->GetCredit(bestBlockHash, txdb, *this, ISMINE_LEDGER, false); + credit += *c_LedgerCreditCached; + } + } return credit; } @@ -3576,6 +3599,8 @@ void CWalletTx::MarkDirty() c_ColdCreditCached = boost::none; c_DelegatedDebitCached = boost::none; c_DelegatedCreditCached = boost::none; + c_LedgerDebitCached = boost::none; + c_LedgerCreditCached = boost::none; } void CWalletTx::BindWallet(CWallet* pwalletIn) diff --git a/wallet/wallet.h b/wallet/wallet.h index 0c5fd7cfd..36adf635c 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -573,6 +573,8 @@ class CWalletTx : public CMerkleTx mutable boost::optional c_DelegatedDebitCached; mutable boost::optional c_DelegatedCreditCached; mutable boost::optional c_ImmatureCreditCached; + mutable boost::optional c_LedgerCreditCached; + mutable boost::optional c_LedgerDebitCached; CWalletTx() { Init(nullptr); } From 85e3a4dcf8e479ea79758a867540775d059d21e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 14:31:53 +0100 Subject: [PATCH 026/129] Select coins based on `strFromAccount` --- wallet/script.cpp | 15 +++++++++++++++ wallet/script.h | 1 + wallet/wallet.cpp | 20 ++++++++++++++++---- wallet/wallet.h | 4 ++-- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/wallet/script.cpp b/wallet/script.cpp index a8d102efb..b57c8ee16 100644 --- a/wallet/script.cpp +++ b/wallet/script.cpp @@ -1741,6 +1741,21 @@ bool ExtractDestination(const ITxDB& txdb, const CScript& scriptPubKey, CTxDesti return false; } +bool ExtractKeyID(const ITxDB& txdb, const CScript& scriptPubKey, CKeyID& addressRet) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(txdb, scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEYHASH) { + addressRet = CKeyID(uint160(vSolutions[0])); + return true; + } + + return false; +} + class CAffectedKeysVisitor : public boost::static_visitor { private: diff --git a/wallet/script.h b/wallet/script.h index 0417e752f..f3e1c2d66 100644 --- a/wallet/script.h +++ b/wallet/script.h @@ -694,6 +694,7 @@ void ExtractAffectedKeys(const ITxDB& txdb, const CKeyStore& keystore, const CSc std::vector& vKeys); bool ExtractDestination(const ITxDB& txdb, const CScript& scriptPubKey, CTxDestination& addressRet, bool fColdStake = false); +bool ExtractKeyID(const ITxDB& txdb, const CScript& scriptPubKey, CKeyID& addressRet); bool ExtractDestinations(const ITxDB& txdb, const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); SignatureState SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 645558209..87b0a12cd 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -21,6 +21,7 @@ #include "ledger/ledger.h" #include "ledger/utils.h" #include "ledgerBridge.h" +#include "script.h" using namespace std; @@ -1357,7 +1358,7 @@ CAmount CWallet::GetImmatureBalance(const ITxDB& txdb) const // populate vCoins with vector of spendable COutputs void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fOnlyConfirmed, bool fIncludeColdStaking, bool fIncludeDelegated, - const CCoinControl* coinControl) const + const CCoinControl* coinControl, const std::string& addressFrom) const { vCoins.clear(); @@ -1413,6 +1414,17 @@ void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fO if (mine == ISMINE_SPENDABLE_STAKEABLE && !fIncludeColdStaking && !fIncludeDelegated) continue; + // if addressFrom is set skip coins from other accounts + if (addressFrom != "") { + auto scriptPubKey = pcoin->vout[i].scriptPubKey; + + CTxDestination destination; + if (ExtractDestination(txdb, scriptPubKey, destination)) { + const auto entry = this->mapAddressBook.get(destination); + if (!entry || !entry.is_initialized() || entry->name != addressFrom) + continue; + } + } // bool fIsValid = // (((mine & // ISMINE_SPENDABLE) != @@ -1810,10 +1822,10 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n, const ITxDB& txdb, bool CWallet::SelectCoins(const ITxDB& txdb, CAmount nTargetValue, unsigned int nSpendTime, set>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, bool fIncludeColdStaking, - bool fIncludeDelegated, bool avoidNTP1Outputs) const + bool fIncludeDelegated, bool avoidNTP1Outputs, const std::string& accountFrom) const { vector vCoins; - AvailableCoins(txdb, vCoins, true, fIncludeColdStaking, fIncludeDelegated, coinControl); + AvailableCoins(txdb, vCoins, true, fIncludeColdStaking, fIncludeDelegated, coinControl, accountFrom); // coin control -> return all selected outputs (we want all selected to go into the transaction for // sure) @@ -2152,7 +2164,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector> setCoins; CAmount nValueIn = 0; if (!SelectCoins(txdb, nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl, false, - fIncludeDelegated, isNTP1Issuance)) { + fIncludeDelegated, isNTP1Issuance, wtxNew.strFromAccount)) { CreateErrorMsg(errorMsg, "Failed to collect nebls for the transaction. You may have chosen to " "spend balance you do not have. For example, you chose to spend " diff --git a/wallet/wallet.h b/wallet/wallet.h index 36adf635c..acf2deee8 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -86,7 +86,7 @@ class CWallet : public CCryptoKeyStore std::set>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl = nullptr, bool fIncludeColdStaking = false, bool fIncludeDelegated = true, - bool avoidNTP1Outputs = false) const; + bool avoidNTP1Outputs = false, const std::string& addressFrom = "") const; CWalletDB* pwalletdbEncryption; @@ -194,7 +194,7 @@ class CWallet : public CCryptoKeyStore bool fIncludeDelegated = false) const; void AvailableCoins(const ITxDB& txdb, std::vector& vCoins, bool fOnlyConfirmed = true, bool fIncludeColdStaking = false, bool fIncludeDelegated = true, - const CCoinControl* coinControl = nullptr) const; + const CCoinControl* coinControl = nullptr, const std::string& addressFrom = "") const; // Get available p2cs utxo bool GetAvailableP2CSCoins(const ITxDB& txdb, std::vector& vCoins) const; From 16f6f90ff77e6698128c047d12561085a98b9403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 14:35:55 +0100 Subject: [PATCH 027/129] Adjust `IsChange` function for Ledger outputs --- wallet/qt/walletmodel.cpp | 2 +- wallet/wallet.cpp | 33 +++++++++++++++++++++++++++------ wallet/wallet.h | 4 ++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/wallet/qt/walletmodel.cpp b/wallet/qt/walletmodel.cpp index aa3ddfc29..251d99197 100644 --- a/wallet/qt/walletmodel.cpp +++ b/wallet/qt/walletmodel.cpp @@ -572,7 +572,7 @@ void WalletModel::listCoins(std::map>& mapCoins) c for (const COutput& out : vCoins) { COutput cout = out; - while (wallet->IsChange(txdb, cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && + while (wallet->IsChange(txdb, (CTransaction&) cout.tx, cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && IsMineCheck(wallet->IsMine(cout.tx->vin[0]), isminetype::ISMINE_SPENDABLE_ALL)) { if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 87b0a12cd..5cbbcabd2 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -839,7 +839,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons return ((IsMine(txout) & filter) ? txout.nValue : 0); } -bool CWallet::IsChange(const ITxDB& txdb, const CTxOut& txout) const +bool CWallet::IsChange(const ITxDB& txdb, const CTransaction& tx, const CTxOut& txout) const { CTxDestination address; @@ -853,15 +853,36 @@ bool CWallet::IsChange(const ITxDB& txdb, const CTxOut& txout) const if (ExtractDestination(txdb, txout.scriptPubKey, address) && ::IsMine(*this, address) != ISMINE_NO) { if (!mapAddressBook.exists(address)) return true; + } + + // for Ledger consider any outputs sent to the same address as change + { + LOCK(cs_wallet); + + for (const auto& txin : tx.vin) { + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) { + const CWalletTx& prev = (*mi).second; + auto previousOutput = prev.vout[txin.prevout.n]; + + CTxDestination outputAddress; + if (ExtractDestination(txdb, previousOutput.scriptPubKey, outputAddress) + && ::IsMine(*this, outputAddress) == ISMINE_LEDGER + && outputAddress == address) { + return true; + } + } + } } + return false; } -CAmount CWallet::GetChange(const ITxDB& txdb, const CTxOut& txout) const +CAmount CWallet::GetChange(const ITxDB& txdb, const CTransaction& tx, const CTxOut& txout) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetChange() : value out of range"); - return (IsChange(txdb, txout) ? txout.nValue : 0); + return (IsChange(txdb, tx, txout) ? txout.nValue : 0); } bool CWallet::IsMine(const CTransaction& tx) const @@ -907,7 +928,7 @@ CAmount CWallet::GetChange(const ITxDB& txdb, const CTransaction& tx) const { CAmount nChange = 0; for (const CTxOut& txout : tx.vout) { - nChange += GetChange(txdb, txout); + nChange += GetChange(txdb, tx, txout); if (!MoneyRange(nChange)) throw std::runtime_error("CWallet::GetChange() : value out of range"); } @@ -1016,7 +1037,7 @@ void CWalletTx::GetAmounts(const ITxDB& txdb, list // 2) the output is to us (received) if (nDebit > 0) { // Don't report 'change' txouts - if (pwallet->IsChange(txdb, txout)) + if (pwallet->IsChange(txdb, *this, txout)) continue; fIsMine = pwallet->IsMine(txout); } else if (!IsMineCheck((fIsMine = pwallet->IsMine(txout)), ISMINE_SPENDABLE_AVAILABLE)) @@ -3080,7 +3101,7 @@ set> CWallet::GetAddressGroupings(const ITxDB& txdb) // group change with input addresses if (any_mine) { for (CTxOut txout : pcoin->vout) - if (IsChange(txdb, txout)) { + if (IsChange(txdb, (CTransaction&) pcoin, txout)) { CWalletTx tx = mapWallet.at(pcoin->vin[0].prevout.hash); CTxDestination txoutAddr; if (!ExtractDestination(txdb, txout.scriptPubKey, txoutAddr)) diff --git a/wallet/wallet.h b/wallet/wallet.h index acf2deee8..48bfeebb1 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -322,8 +322,8 @@ class CWallet : public CCryptoKeyStore CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; isminetype IsMine(const CTxOut& txout) const; CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; - bool IsChange(const ITxDB& txdb, const CTxOut& txout) const; - CAmount GetChange(const ITxDB& txdb, const CTxOut& txout) const; + bool IsChange(const ITxDB& txdb, const CTransaction& tx, const CTxOut& txout) const; + CAmount GetChange(const ITxDB& txdb, const CTransaction& tx, const CTxOut& txout) const; bool IsMine(const CTransaction& tx) const; bool IsFromMe(const CTransaction& tx) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; From 39b0d60765e8eff2c751908214264cc6bfa28482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 15:53:30 +0100 Subject: [PATCH 028/129] Move `GetBip32Path` functions to bip32 file --- wallet/ledger/bip32.cpp | 12 ++++++++++++ wallet/ledger/bip32.h | 2 ++ wallet/ledger/utils.cpp | 12 ------------ wallet/ledger/utils.h | 2 -- wallet/qt/addresstablemodel.cpp | 5 +++-- wallet/qt/editaddressdialog.cpp | 3 ++- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index 377567956..0e13f2771 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -55,4 +55,16 @@ namespace ledger::bip32 } return keypath; } + + std::string GetBip32Path(const std::string &account, const std::string &index) + { + std::stringstream ss; + ss << "m/44'/146'/" << account << "/0/" << index; + return ss.str(); + } + + std::string GetBip32Path(uint32_t account, uint32_t index) + { + return GetBip32Path(std::to_string(account), std::to_string(index)); + } } // namespace ledger::utils diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 97e992154..8851cf9ab 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -11,6 +11,8 @@ namespace ledger::bip32 { uint32_t Harden(uint32_t n); bytes ParseHDKeypath(const std::string &keypath_str); + std::string GetBip32Path(const std::string &account, const std::string &index); + std::string GetBip32Path(uint32_t account, uint32_t index); } #endif \ No newline at end of file diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index e02372ead..dfdcee9b3 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -200,16 +200,4 @@ namespace ledger::utils return compressedPubKey; } - - std::string GetBip32Path(const std::string &account, const std::string &index) - { - std::stringstream ss; - ss << "m/44'/146'/" << account << "/0/" << index; - return ss.str(); - } - - std::string GetBip32Path(uint32_t account, uint32_t index) - { - return GetBip32Path(std::to_string(account), std::to_string(index)); - } } // namespace ledger::utils diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index 2976619d6..29c0874e9 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -27,8 +27,6 @@ namespace ledger::utils void AppendUint64(bytes &vector, uint64_t n, bool littleEndian = false); bytes Splice(const bytes &vec, int start, int length); bytes CompressPubKey(const bytes &pubKey); - std::string GetBip32Path(const std::string &account, const std::string &index); - std::string GetBip32Path(uint32_t account, uint32_t index); const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; const uint32_t MAX_RECOMMENDED_INDEX = 50000; diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 3e6fc3e09..e055497e2 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -1,5 +1,6 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "ledger/bip32.h" #include "ledger/error.h" #include "ledger/ledger.h" #include "ledger/utils.h" @@ -231,7 +232,7 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const { // IsLedger, LedgerAccount and LedgerIndex are hidden, we display the info here // (however, they still need to be above due to EditAddressDialog data mapping) - std::string path = ledger::utils::GetBip32Path(rec->ledgerAccount, rec->ledgerIndex); + std::string path = ledger::bip32::GetBip32Path(rec->ledgerAccount, rec->ledgerIndex); return isLedger ? QString::fromStdString(path) : "-"; } } @@ -429,7 +430,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con try { ledger::Ledger l; l.open(); - std::string path = ledger::utils::GetBip32Path(account, index); + std::string path = ledger::bip32::GetBip32Path(account, index); auto result = l.GetPublicKey(path, true); l.close(); auto pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 1cda89cd2..ba0ba2c95 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -2,6 +2,7 @@ #include "ui_editaddressdialog.h" #include "addresstablemodel.h" #include "guiutil.h" +#include "ledger/bip32.h" #include "ledger/utils.h" #include @@ -223,7 +224,7 @@ void EditAddressDialog::accept() void EditAddressDialog::updateLedgerPathLabel() { - std::string path = ledger::utils::GetBip32Path( + std::string path = ledger::bip32::GetBip32Path( ui->ledgerAccountEdit->text().toStdString(), ui->ledgerIndexEdit->text().toStdString() ); From 40cadf02d02f725865feb87ea831d91ff339c901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 21 Mar 2023 15:46:22 +0100 Subject: [PATCH 029/129] Make multiple changes to Ledger tx signing - fix change outputs - fix transactions with multiple inputs (inputs could have different order than UTxOs) - properly gather signature paths for all inputs (although there can only be one signature path) - change/signature paths are not hardcoded anymore - transactions should work properly --- wallet/ledgerBridge.cpp | 50 ++++++++++++++++++++++++++++------------- wallet/ledgerBridge.h | 3 ++- wallet/wallet.cpp | 39 ++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index d2f5587ab..92867643f 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -1,10 +1,14 @@ +#include "ledger/bip32.h" #include "ledger/ledger.h" #include "ledger/tx.h" #include "ledger/utils.h" #include "ledgerBridge.h" +#include "key.h" +#include "script.h" #include #include +#include namespace ledgerbridge { @@ -12,30 +16,46 @@ namespace ledgerbridge LedgerBridge::~LedgerBridge() {} - void LedgerBridge::SignTransaction(CWalletTx &wtxNew, const std::vector &utxos) { - // TODO GK - proper paths - std::string changePath = ledger::utils::GetBip32Path(0, 0); - std::vector sigPaths = {changePath}; + void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos) + { + std::vector signaturePaths; + + // transform wallet tx to ledger tx + ledger::Tx tx = ToLedgerTx(wtxNew); - // transform wallet tx - ledger::Tx tx = ToLedgerTx(wtxNew); - // transform UTxOs + // transform UTxOs and build signature paths std::vector ledgerUtxos; - for (const auto &utxo : utxos){ + for (const auto &utxo : utxos) + { ledgerUtxos.push_back({ToLedgerTx(utxo.transaction), utxo.outputIndex}); + + CKeyID keyID; + if (!ExtractKeyID(txdb, utxo.outputPubKey, keyID)) { + throw "Invalid key in script"; + } + + CLedgerKey ledgerKey; + auto keyFound = wallet.GetLedgerKey(keyID, ledgerKey); + if (!keyFound) { + throw "Ledger key was not found in wallet"; + } + + signaturePaths.push_back(ledger::bip32::GetBip32Path(ledgerKey.account, ledgerKey.index)); } + auto changePath = signaturePaths[0]; + ledger::Ledger ledger(ledger::Transport::TransportType::SPECULOS); ledger.open(); // sign tx - auto signTxResults = ledger.SignTransaction(tx, changePath, sigPaths, ledgerUtxos); + auto signTxResults = ledger.SignTransaction(tx, changePath, signaturePaths, ledgerUtxos); // add signatures to tx and verify for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { auto signature = std::get<1>(signTxResults[sigIndex]); - - auto pubKeyResult = ledger.GetPublicKey(sigPaths[sigIndex], false); + + auto pubKeyResult = ledger.GetPublicKey(signaturePaths[sigIndex], false); auto pubKey = CPubKey(ledger::utils::CompressPubKey(std::get<0>(pubKeyResult))); // hash type @@ -58,10 +78,8 @@ namespace ledgerbridge ledger::Tx ledgerTx; ledgerTx.version = tx.nVersion; ledgerTx.time = tx.nTime; - - for (auto i = 0; i < tx.vin.size();i++){ - const auto& input = tx.vin[i]; - + + for (const auto& input : tx.vin){ ledger::TxPrevout prevout = { .hash = ledger::bytes(input.prevout.hash.begin(), input.prevout.hash.end()), .index = input.prevout.n @@ -74,7 +92,7 @@ namespace ledgerbridge }); } - for (const auto& output: tx.vout) { + for (const auto& output : tx.vout) { ledgerTx.outputs.push_back({ .amount = (uint64_t) output.nValue, .script = output.scriptPubKey diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 7265a865e..17a05d047 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -1,6 +1,7 @@ #include "ledger/tx.h" #include "ledger/bytes.h" +#include "itxdb.h" #include "wallet.h" #include "script.h" @@ -21,7 +22,7 @@ namespace ledgerbridge LedgerBridge(); ~LedgerBridge(); - void SignTransaction(CWalletTx &wtxNew, const std::vector &utxos); + void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos); private: ledger::Tx ToLedgerTx(const CTransaction& tx); }; diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 5cbbcabd2..e6057e329 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1543,6 +1543,7 @@ void CWallet::AvailableCoinsForStaking(const ITxDB& txdb, vector& vCoin if (IsSpent(pcoin->GetHash(), i, txdb, bestBlockHash)) continue; + // TODO GK: ledger? if (!(mine & ISMINE_SPENDABLE_STAKEABLE) && !(mine & ISMINE_SPENDABLE)) continue; @@ -2193,11 +2194,20 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector ledgerKeys; + auto isLedgerTx = false; for (PAIRTYPE(const CWalletTx*, unsigned int) pcoin : setCoins) { CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(txdb, bestBlockHash); + + if (IsMineCheck(::IsMine(*this, pcoin.first->vout[pcoin.second].scriptPubKey), ISMINE_LEDGER)) { + isLedgerTx = true; + ledgerKeys.push_back(pcoin.first->vout[pcoin.second].scriptPubKey); + } } + // TODO GK - assert that if ledger transaction then only ledger inputs are used + // select NTP1 tokens to determine change (this may be superfluous the first time this is // called since it's already called before this whole function, but that's fine; it's // necessary to call this after adding any new inputs to check whether any new inputs @@ -2267,9 +2277,10 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0 || ntp1TokenChangeExists) { // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so @@ -2280,8 +2291,15 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(&coinControl->destChange)) { scriptChange.SetDestination(coinControl->destChange); changeKeyID = boost::get(coinControl->destChange); + } else if (isLedgerTx) { + CTxDestination destChange; + if (!ExtractDestination(txdb, ledgerKeys[0], destChange)) { + // TODO GK - log? + return false; + } + scriptChange.SetDestination(destChange); + changeKeyID = boost::get(destChange); } - // no coin control: send change to newly generated address else { // Note: We use a new key here to keep it from being obvious which side is the @@ -2370,15 +2388,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey), ISMINE_LEDGER)) { - isLedgerTx = true; - break; - } - } - - std::vector ledgerBridgeUtxos; + std::vector ledgerBridgeUtxos(wtxNew.vin.size()); // Sign for (const PAIRTYPE(const CWalletTx*, unsigned int) & coin : setCoins) { @@ -2397,8 +2407,9 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey}); + if (isLedgerTx) { + // assign to a specific position in the vector since inputs and utxos need to be aligned for Ledger + ledgerBridgeUtxos[nIn] = {*coin.first, coin.second, coin.first->vout[coin.second].scriptPubKey}; } else { if (SignSignature(*this, *coin.first, wtxNew, nIn) != SignatureState::Verified) { CreateErrorMsg(errorMsg, "Error while signing transactions inputs."); @@ -2416,7 +2427,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector Date: Wed, 22 Mar 2023 07:53:14 +0100 Subject: [PATCH 030/129] Fix `GetBip32Path` function - harden account --- wallet/ledger/bip32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index 0e13f2771..5c360e02a 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -59,7 +59,7 @@ namespace ledger::bip32 std::string GetBip32Path(const std::string &account, const std::string &index) { std::stringstream ss; - ss << "m/44'/146'/" << account << "/0/" << index; + ss << "m/44'/146'/" << account << "'/0/" << index; return ss.str(); } From 369dabdf6ae1f80e2bb715d495dbd8c5315a8906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 22 Mar 2023 10:36:33 +0100 Subject: [PATCH 031/129] Add Ledger tx validation - inputs can come from only one key (address) - ntp1 tokens are not allowed in Ledger transactions --- wallet/wallet.cpp | 53 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index e6057e329..92ec0218f 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2194,19 +2194,51 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector ledgerKeys; - auto isLedgerTx = false; for (PAIRTYPE(const CWalletTx*, unsigned int) pcoin : setCoins) { CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(txdb, bestBlockHash); + } + + auto hasNonLedgerInputKeys = false; + boost::optional ledgerInputKey; + for (PAIRTYPE(const CWalletTx*, unsigned int) pcoin : setCoins) { + if (!IsMineCheck(::IsMine(*this, pcoin.first->vout[pcoin.second].scriptPubKey), ISMINE_LEDGER)) { + hasNonLedgerInputKeys = true; + continue; + } - if (IsMineCheck(::IsMine(*this, pcoin.first->vout[pcoin.second].scriptPubKey), ISMINE_LEDGER)) { - isLedgerTx = true; - ledgerKeys.push_back(pcoin.first->vout[pcoin.second].scriptPubKey); + if (!ledgerInputKey) { + ledgerInputKey = pcoin.first->vout[pcoin.second].scriptPubKey; + continue; + } + + if (ledgerInputKey != pcoin.first->vout[pcoin.second].scriptPubKey) { + NLog.write(b_sev::err, + "Ledger transactions can only contain inputs belonging to one address."); + CreateErrorMsg(errorMsg, + "Ledger transactions can only contain inputs belonging to one address."); + return false; } } - // TODO GK - assert that if ledger transaction then only ledger inputs are used + auto isLedgerTx = !!ledgerInputKey; + if (isLedgerTx) { + if (hasNonLedgerInputKeys) { + NLog.write(b_sev::err, + "Ledger transactions can not contain non-ledger inputs."); + CreateErrorMsg(errorMsg, + "Ledger transactions can not contain non-ledger inputs."); + return false; + } + + if (ntp1TxData.getTotalTokensInInputs().size()) { + NLog.write(b_sev::err, + "Ledger transactions can not contain NTP1 tokens."); + CreateErrorMsg(errorMsg, + "Ledger transactions can not contain NTP1 tokens."); + return false; + } + } // select NTP1 tokens to determine change (this may be superfluous the first time this is // called since it's already called before this whole function, but that's fine; it's @@ -2277,8 +2309,6 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0 || ntp1TokenChangeExists) { @@ -2293,8 +2323,11 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(coinControl->destChange); } else if (isLedgerTx) { CTxDestination destChange; - if (!ExtractDestination(txdb, ledgerKeys[0], destChange)) { - // TODO GK - log? + if (!ExtractDestination(txdb, ledgerInputKey.get(), destChange)) { + NLog.write(b_sev::err, + "Invalid Ledger destination."); + CreateErrorMsg(errorMsg, + "Invalid Ledger destination."); return false; } scriptChange.SetDestination(destChange); From 1061eca3f266e4e601b93bc19335188cba3a80c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 22 Mar 2023 11:19:30 +0100 Subject: [PATCH 032/129] Fix get Ledger public key --- wallet/qt/addresstablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index e055497e2..e8df9087b 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -433,7 +433,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con std::string path = ledger::bip32::GetBip32Path(account, index); auto result = l.GetPublicKey(path, true); l.close(); - auto pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); + pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); } catch (const ledger::Error& e) { editStatus = LEDGER_ERROR; ledgerError = e; From 70a8e121c1b5809df5239f53b67dcc703719ab9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 22 Mar 2023 12:08:26 +0100 Subject: [PATCH 033/129] Fix Ledger transaction fees --- wallet/transaction.cpp | 9 ++++++++- wallet/transaction.h | 2 +- wallet/wallet.cpp | 31 +++++++++++++------------------ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/wallet/transaction.cpp b/wallet/transaction.cpp index 99d0d4c74..1e563595a 100644 --- a/wallet/transaction.cpp +++ b/wallet/transaction.cpp @@ -313,11 +313,18 @@ Result CTransaction::CheckTransaction(const ITxDB& txdb } CAmount CTransaction::GetMinFee(const ITxDB& txdb, unsigned int nBlockSize, enum GetMinFee_mode mode, - unsigned int nBytes) const + unsigned int nBytes, bool sigsIncluded) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE CAmount nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; + // for Ledger the transaction doesn't contain signatures when calculating the fee + // so we need to add the size of the signatures to nBytes + if (!sigsIncluded) { + const int SCRIPT_SIG_MAX_SIZE = 108; + nBytes += SCRIPT_SIG_MAX_SIZE * vin.size(); + } + unsigned int nNewBlockSize = nBlockSize + nBytes; CAmount nMinFee = (1 + (CAmount)nBytes / 1000) * nBaseFee; diff --git a/wallet/transaction.h b/wallet/transaction.h index 423ed0f37..a575c9bbd 100644 --- a/wallet/transaction.h +++ b/wallet/transaction.h @@ -126,7 +126,7 @@ class CTransaction int64_t GetValueIn(const MapPrevTx& mapInputs) const; int64_t GetMinFee(const ITxDB& txdb, unsigned int nBlockSize = 1, - enum GetMinFee_mode mode = GMF_BLOCK, unsigned int nBytes = 0) const; + enum GetMinFee_mode mode = GMF_BLOCK, unsigned int nBytes = 0, bool sigsIncluded = true) const; bool ReadFromDisk(CDiskTxPos pos, const ITxDB& txdb); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 92ec0218f..411efebd0 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2440,6 +2440,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey}; @@ -2451,22 +2452,6 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector Date: Thu, 23 Mar 2023 12:38:08 +0100 Subject: [PATCH 034/129] Split GetTrustedInput APDUs by tx elements Previously we chunked the whole serialized transaction into chunks of 255 bytes. Ledger would fail if a transaction element would span over multiple chunks. See [docs](https://github.com/NeblioTeam/app-neblio/blob/master/doc/btc.asc#get-trusted-input) for more info. --- wallet/ledger/ledger.cpp | 56 +++++++++++++++++++++++----------------- wallet/ledger/ledger.h | 3 +-- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 345e7d4e3..ae2c36eac 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -61,7 +61,7 @@ namespace ledger return {pubKey, std::string(address.begin(), address.end()), chainCode}; } - bytes Ledger::GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &transactionData) + bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes &transactionData) { // TODO GK - refactor transport to throw instead of returning error auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); @@ -75,36 +75,46 @@ namespace ledger bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) { - return GetTrustedInput(SerializeTransaction(utxoTx), indexLookup); - } + bytes firstRoundData; + utils::AppendUint32(firstRoundData, indexLookup); + utils::AppendUint32(firstRoundData, utxoTx.version, true); + utils::AppendUint32(firstRoundData, utxoTx.time, true); + utils::AppendVector(firstRoundData, utils::CreateVarint(utxoTx.inputs.size())); - bytes Ledger::GetTrustedInput(const bytes &serializedTransaction, uint32_t indexLookup) - { - auto MAX_CHUNK_SIZE = 255; - std::vector chunks; - auto offset = 0; + GetTrustedInputRaw(true, firstRoundData); - bytes data; - utils::AppendUint32(data, indexLookup); + for (auto input : utxoTx.inputs) + { + bytes inputData; + utils::AppendVector(inputData, input.prevout.hash); + utils::AppendUint32(inputData, input.prevout.index, true); + utils::AppendVector(inputData, utils::CreateVarint(input.script.size())); - utils::AppendVector(data, serializedTransaction); + GetTrustedInputRaw(false, inputData); - while (offset != data.size()) - { - auto chunkSize = data.size() - offset > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : data.size() - offset; - chunks.push_back(utils::Splice(data, offset, chunkSize)); - offset += chunkSize; + bytes inputScriptData; + utils::AppendVector(inputScriptData, input.script); + utils::AppendUint32(inputScriptData, input.sequence); + + GetTrustedInputRaw(false, inputScriptData); } - auto isFirst = true; - bytes finalResults; - for (auto &chunk : chunks) - { - finalResults = GetTrustedInputRaw(isFirst, 0, chunk); - isFirst = false; + GetTrustedInputRaw(false, utils::CreateVarint(utxoTx.outputs.size())); + + for (auto output : utxoTx.outputs) + { + bytes outputData; + utils::AppendUint64(outputData, output.amount, true); + utils::AppendVector(outputData, utils::CreateVarint(output.script.size())); + GetTrustedInputRaw(false, outputData); + + bytes outputScriptData; + utils::AppendVector(outputScriptData, output.script); + + GetTrustedInputRaw(false, outputScriptData); } - return finalResults; + return GetTrustedInputRaw(false, utils::IntToBytes(utxoTx.locktime, 4)); } void Ledger::UntrustedHashTxInputFinalize(const Tx &tx, const std::string &changePath) diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 4de1ec2c8..eecff957c 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -36,8 +36,7 @@ namespace ledger bytes ProcessScriptBlocks(const bytes &script, uint32_t sequence); bytes GetTrustedInput(const Tx &utxoTx, uint32_t indexLookup); - bytes GetTrustedInput(const bytes &serializedTransaction, uint32_t indexLookup); - bytes GetTrustedInputRaw(bool firstRound, uint32_t indexLookup, const bytes &data); + bytes GetTrustedInputRaw(bool firstRound, const bytes &data); void UntrustedHashTxInputFinalize(const Tx &tx, const std::string &changePath); void UntrustedHashTxInputStart(const Tx &tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction); TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); From 7a4248c7314a4d4f56d423fd15aa5c2c896e4cc0 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 23 Mar 2023 20:34:07 +0100 Subject: [PATCH 035/129] Fix initial address book buttons visibility --- wallet/qt/addressbookpage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index 70a0c5793..a427f5a1a 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -54,10 +54,12 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : ui->labelExplanation->setVisible(false); ui->deleteButton->setVisible(true); ui->signMessage->setVisible(false); + ui->verifyMessage->setVisible(true); break; case ReceivingTab: ui->deleteButton->setVisible(false); ui->signMessage->setVisible(true); + ui->verifyMessage->setVisible(false); break; } From ed0bbcef57743859e8427fb95a3a485726fc2dc6 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 23 Mar 2023 22:04:19 +0100 Subject: [PATCH 036/129] Add UI for spending from a Ledger address --- wallet/qt/addressbookpage.cpp | 33 ++++++---- wallet/qt/addressbookpage.h | 13 ++-- wallet/qt/sendcoinsdialog.cpp | 35 ++++++++++- wallet/qt/sendcoinsdialog.h | 2 + wallet/qt/sendcoinsentry.cpp | 2 +- wallet/qt/signverifymessagedialog.cpp | 4 +- wallet/qt/ui_sendcoinsdialog.h | 86 +++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 20 deletions(-) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index a427f5a1a..ae0dbab5a 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -61,6 +61,11 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : ui->signMessage->setVisible(true); ui->verifyMessage->setVisible(false); break; + case LedgerTab: + ui->deleteButton->setVisible(false); + ui->signMessage->setVisible(false); + ui->verifyMessage->setVisible(false); + break; } // Context menu actions @@ -131,6 +136,11 @@ void AddressBookPage::setModel(AddressTableModel *modelIn) proxyModel->setFilterRole(AddressTableModel::TypeRole); proxyModel->setFilterFixedString(AddressTableModel::Send); break; + case LedgerTab: + // Ledger filter + proxyModel->setFilterRole(AddressTableModel::TypeRole); + proxyModel->setFilterFixedString(AddressTableModel::ReceiveLedger); + break; } ui->tableView->setModel(proxyModel); ui->tableView->sortByColumn(0, Qt::AscendingOrder); @@ -262,22 +272,22 @@ void AddressBookPage::selectionChanged() case SendingTab: // In sending tab, allow deletion of selection ui->deleteButton->setEnabled(true); - ui->deleteButton->setVisible(true); deleteAction->setEnabled(true); ui->signMessage->setEnabled(false); - ui->signMessage->setVisible(false); ui->verifyMessage->setEnabled(true); - ui->verifyMessage->setVisible(true); break; case ReceivingTab: // Deleting receiving addresses, however, is not allowed ui->deleteButton->setEnabled(false); - ui->deleteButton->setVisible(false); deleteAction->setEnabled(false); ui->signMessage->setEnabled(true); - ui->signMessage->setVisible(true); ui->verifyMessage->setEnabled(false); - ui->verifyMessage->setVisible(false); + break; + case LedgerTab: + ui->deleteButton->setEnabled(false); + deleteAction->setEnabled(false); + ui->signMessage->setEnabled(false); + ui->verifyMessage->setEnabled(false); break; } ui->copyToClipboard->setEnabled(true); @@ -303,15 +313,16 @@ void AddressBookPage::done(int retval) return; // Figure out which address was selected, and return it - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + QModelIndexList addressIndexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + QModelIndexList labelIndexes = table->selectionModel()->selectedRows(AddressTableModel::Label); - foreach (QModelIndex index, indexes) + if (!addressIndexes.isEmpty()) { - QVariant address = table->model()->data(index); - returnValue = address.toString(); + returnAddress = addressIndexes.at(0).data().toString(); + returnLabel = labelIndexes.at(0).data().toString(); } - if(returnValue.isEmpty()) + if(returnAddress.isEmpty()) { // If no address entry selected, return rejected retval = Rejected; diff --git a/wallet/qt/addressbookpage.h b/wallet/qt/addressbookpage.h index 6524dbad5..a439f9f8b 100644 --- a/wallet/qt/addressbookpage.h +++ b/wallet/qt/addressbookpage.h @@ -25,12 +25,13 @@ class AddressBookPage : public QDialog public: enum Tabs { - SendingTab = 0, - ReceivingTab = 1 + SendingTab = 0, /**< Tab for destination addresses */ + ReceivingTab = 1, /**< Tab for addresses controlled by the app or Ledger device */ + LedgerTab = 2, /**< Tab for source addresses controlled by Ledger device */ }; enum Mode { - ForSending, /**< Open address book to pick address for sending */ + ForSending, /**< Open address book to pick a destination address */ ForEditing /**< Open address book for editing */ }; @@ -39,7 +40,8 @@ class AddressBookPage : public QDialog void setModel(AddressTableModel *modelIn); void setOptionsModel(OptionsModel *optionsModelIn); - const QString &getReturnValue() const { return returnValue; } + const QString &getReturnAddress() const { return returnAddress; } + const QString &getReturnLabel() const { return returnLabel; } public slots: void done(int retval); @@ -51,7 +53,8 @@ public slots: OptionsModel *optionsModel; Mode mode; Tabs tab; - QString returnValue; + QString returnAddress; + QString returnLabel; QSortFilterProxyModel *proxyModel; QMenu *contextMenu; QAction *deleteAction; diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 60552a5e7..8306e49b7 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -6,7 +6,6 @@ #include "init.h" #include "walletmodel.h" -#include "addressbookpage.h" #include "askpassphrasedialog.h" #include "bitcoinunits.h" #include "guiutil.h" @@ -86,6 +85,10 @@ SendCoinsDialog::SendCoinsDialog(QWidget* parent) ui->labelCoinControlChange->addAction(clipboardChangeAction); fNewRecipientAllowed = true; + + // Ledger submenu + ui->ledgerWidget->setVisible(false); + GUIUtil::setupAddressWidget(ui->ledgerPayFromAddressEdit, this); } void SendCoinsDialog::setModel(WalletModel* modelIn) @@ -120,8 +123,38 @@ void SendCoinsDialog::setModel(WalletModel* modelIn) SendCoinsDialog::~SendCoinsDialog() { delete ui; } +void SendCoinsDialog::on_ledgerCheckBox_toggled(bool checked) +{ + if (checked) { + ui->ledgerWidget->setVisible(true); + ui->editMetadataButton->setDisabled(true); + ui->sendButton->setText(tr("Sign and s&end")); + } else { + ui->ledgerWidget->setVisible(false); + ui->editMetadataButton->setDisabled(false); + ui->sendButton->setText(tr("S&end")); + } +} + +void SendCoinsDialog::on_ledgerAddressBookButton_clicked() +{ + if (!model) + return; + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::LedgerTab, this); + dlg.setModel(model->getAddressTableModel()); + if (dlg.exec()) { + ui->ledgerPayFromAddressEdit->setText(dlg.getReturnAddress()); + ui->ledgerPayFromLabelEdit->setText(dlg.getReturnLabel()); + } +} + void SendCoinsDialog::on_sendButton_clicked() { + // TODO Ledger + if (ui->ledgerCheckBox->isChecked()) { + throw "Spending from Ledger not implemented"; + } + QList recipients; bool valid = true; const bool fSpendDelegatedOutputs = ui->allowSpendingDelegatedCoins->isChecked(); diff --git a/wallet/qt/sendcoinsdialog.h b/wallet/qt/sendcoinsdialog.h index 5ee87ebfe..a388f6872 100644 --- a/wallet/qt/sendcoinsdialog.h +++ b/wallet/qt/sendcoinsdialog.h @@ -53,6 +53,8 @@ public slots: bool fNewRecipientAllowed; private slots: + void on_ledgerCheckBox_toggled(bool checked); + void on_ledgerAddressBookButton_clicked(); void on_sendButton_clicked(); void removeEntry(SendCoinsEntry* entry); void updateDisplayUnit(); diff --git a/wallet/qt/sendcoinsentry.cpp b/wallet/qt/sendcoinsentry.cpp index 3335182b6..6e9eb57cd 100644 --- a/wallet/qt/sendcoinsentry.cpp +++ b/wallet/qt/sendcoinsentry.cpp @@ -47,7 +47,7 @@ void SendCoinsEntry::on_addressBookButton_clicked() AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { - ui->payTo->setText(dlg.getReturnValue()); + ui->payTo->setText(dlg.getReturnAddress()); ui->payAmount->setFocus(); } } diff --git a/wallet/qt/signverifymessagedialog.cpp b/wallet/qt/signverifymessagedialog.cpp index d87ea3988..52d7794ab 100644 --- a/wallet/qt/signverifymessagedialog.cpp +++ b/wallet/qt/signverifymessagedialog.cpp @@ -90,7 +90,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { - setAddress_SM(dlg.getReturnValue()); + setAddress_SM(dlg.getReturnAddress()); } } } @@ -179,7 +179,7 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { - setAddress_VM(dlg.getReturnValue()); + setAddress_VM(dlg.getReturnAddress()); } } } diff --git a/wallet/qt/ui_sendcoinsdialog.h b/wallet/qt/ui_sendcoinsdialog.h index af8181780..a777581e8 100644 --- a/wallet/qt/ui_sendcoinsdialog.h +++ b/wallet/qt/ui_sendcoinsdialog.h @@ -9,6 +9,7 @@ #ifndef UI_SENDCOINSDIALOG_H #define UI_SENDCOINSDIALOG_H +#include "qvalidatedlineedit.h" #include "ntp1/ntp1createmetadatadialog.h" #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -89,6 +91,16 @@ class Ui_SendCoinsDialog QPushButton* sendButton; NTP1CreateMetadataDialog* editMetadataDialog; + QCheckBox* ledgerCheckBox; + QWidget* ledgerWidget; + QGridLayout* ledgerGridLayout; + QLabel* ledgerPayFromLabel; + QHBoxLayout* ledgerPayFromLayout; + QValidatedLineEdit* ledgerPayFromAddressEdit; + QToolButton* ledgerAddressBookButton; + QLabel* ledgerPayFromLabel2; + QValidatedLineEdit* ledgerPayFromLabelEdit; + void setupUi(QDialog* SendCoinsDialog) { if (SendCoinsDialog->objectName().isEmpty()) @@ -400,6 +412,66 @@ class Ui_SendCoinsDialog verticalLayout->addWidget(frameCoinControl); + ledgerCheckBox = new QCheckBox(); + ledgerCheckBox->setObjectName(QStringLiteral("ledgerCheckBox")); + ledgerCheckBox->setText("&Pay from a Ledger address"); + + verticalLayout->addWidget(ledgerCheckBox); + + ledgerWidget = new QWidget(); + ledgerWidget->setObjectName(QStringLiteral("ledgerWidget")); + + verticalLayout->addWidget(ledgerWidget); + + ledgerGridLayout = new QGridLayout(); + ledgerGridLayout->setObjectName(QStringLiteral("ledgerGridLayout")); + ledgerGridLayout->setSpacing(12); + + ledgerWidget->setLayout(ledgerGridLayout); + + ledgerPayFromLabel = new QLabel(); + ledgerPayFromLabel->setObjectName(QStringLiteral("ledgerPayFromLabel")); + ledgerPayFromLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); + + ledgerGridLayout->addWidget(ledgerPayFromLabel, 0, 0, 1, 1); + + ledgerPayFromLayout = new QHBoxLayout(); + ledgerPayFromLayout->setObjectName(QStringLiteral("ledgerPayFromLayout")); + ledgerPayFromLayout->setSpacing(0); + + ledgerGridLayout->addLayout(ledgerPayFromLayout, 0, 1, 1, 1); + + ledgerPayFromAddressEdit = new QValidatedLineEdit(); + ledgerPayFromAddressEdit->setObjectName(QStringLiteral("ledgerPayFromAddressEdit")); + ledgerPayFromAddressEdit->setMaxLength(34); + ledgerPayFromAddressEdit->setDisabled(true); // should not be typed in manually + ledgerPayFromLabel->setBuddy(ledgerPayFromAddressEdit); + + ledgerPayFromLayout->addWidget(ledgerPayFromAddressEdit); + + ledgerAddressBookButton = new QToolButton(); + ledgerAddressBookButton->setObjectName(QStringLiteral("ledgerAddressBookButton")); + QIcon icon10; + icon10.addFile(QStringLiteral(":/icons/address-book"), QSize(), QIcon::Normal, QIcon::Off); + ledgerAddressBookButton->setIcon(icon10); + + ledgerPayFromLayout->addWidget(ledgerAddressBookButton); + + ledgerPayFromLabel2 = new QLabel(); + ledgerPayFromLabel2->setObjectName(QStringLiteral("ledgerPayFromLabel2")); + ledgerPayFromLabel2->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); + + ledgerGridLayout->addWidget(ledgerPayFromLabel2, 1, 0, 1, 1); + + ledgerPayFromLabelEdit = new QValidatedLineEdit(); + ledgerPayFromLabelEdit->setObjectName(QStringLiteral("ledgerPayFromLabelEdit")); + ledgerPayFromLabelEdit->setDisabled(true); // should not be typed in manually + ledgerPayFromLabel2->setBuddy(ledgerPayFromLabelEdit); + + ledgerGridLayout->addWidget(ledgerPayFromLabelEdit, 1, 1, 1, 1); + + verticalLayout->addSpacing(10); + scrollArea = new QScrollArea(SendCoinsDialog); scrollArea->setObjectName(QStringLiteral("scrollArea")); scrollArea->setWidgetResizable(true); @@ -555,6 +627,20 @@ class Ui_SendCoinsDialog checkBoxCoinControlChange->setText( QApplication::translate("SendCoinsDialog", "custom change address", Q_NULLPTR)); labelCoinControlChangeLabel->setText(QString()); + ledgerCheckBox->setText( + QApplication::translate("SendCoinsDialog", "&Pay from a Ledger address", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + ledgerCheckBox->setToolTip( + QApplication::translate("SendCoinsDialog", "If you choose to pay from a Ledger address, only a single source address will be used.", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + ledgerPayFromLabel->setText( + QApplication::translate("SendCoinsDialog", "Pay from:", Q_NULLPTR)); + ledgerPayFromAddressEdit->setPlaceholderText( + QApplication::translate("SendCoinsDialog", "Enter a neblio address owned by your Ledger device", Q_NULLPTR)); + ledgerPayFromLabel2->setText( + QApplication::translate("SendCoinsDialog", "Label:", Q_NULLPTR)); + ledgerPayFromLabelEdit->setPlaceholderText( + QApplication::translate("SendCoinsDialog", "A label for the Ledger address", Q_NULLPTR)); #ifndef QT_NO_TOOLTIP addButton->setToolTip(QApplication::translate("SendCoinsDialog", "Send to multiple recipients at once", Q_NULLPTR)); From bfe1c7695240ed176aa6fee94065b7c20e3dd917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Mar 2023 10:37:40 +0100 Subject: [PATCH 037/129] Enable sending Ledger transactions from UI --- wallet/qt/sendcoinsdialog.cpp | 21 ++++++++++----------- wallet/qt/walletmodel.cpp | 7 +++++-- wallet/qt/walletmodel.h | 3 ++- wallet/wallet.cpp | 8 ++++---- wallet/wallet.h | 2 +- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 8306e49b7..3d3332b69 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -150,11 +150,6 @@ void SendCoinsDialog::on_ledgerAddressBookButton_clicked() void SendCoinsDialog::on_sendButton_clicked() { - // TODO Ledger - if (ui->ledgerCheckBox->isChecked()) { - throw "Spending from Ledger not implemented"; - } - QList recipients; bool valid = true; const bool fSpendDelegatedOutputs = ui->allowSpendingDelegatedCoins->isChecked(); @@ -259,13 +254,17 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - WalletModel::SendCoinsReturn sendstatus; - if (!model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) - sendstatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs); - else - sendstatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, - CoinControlDialog::coinControl); + CCoinControl* coinControl = nullptr; + if (model->getOptionsModel() && model->getOptionsModel()->getCoinControlFeatures()) + coinControl = CoinControlDialog::coinControl; + + std::string accountFrom = ""; + if (ui->ledgerCheckBox->isChecked()) + accountFrom = ui->ledgerPayFromLabelEdit->text().toStdString(); + + auto sendstatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, + coinControl, accountFrom); switch (sendstatus.status) { case WalletModel::InvalidAddress: diff --git a/wallet/qt/walletmodel.cpp b/wallet/qt/walletmodel.cpp index 251d99197..ea8c5ee4c 100644 --- a/wallet/qt/walletmodel.cpp +++ b/wallet/qt/walletmodel.cpp @@ -217,7 +217,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList boost::shared_ptr ntp1wallet, const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, - const CCoinControl* coinControl) + const CCoinControl* coinControl, + const std::string& accountFrom) { qint64 total = 0; QString hex; @@ -257,7 +258,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList int64_t nBalance = 0; std::vector vCoins; - wallet->AvailableCoins(txdb, vCoins, true, coinControl); + wallet->AvailableCoins(txdb, vCoins, true, false, false, coinControl, accountFrom); for (const COutput& out : vCoins) { nBalance += out.tx->vout[out.i].nValue; @@ -314,6 +315,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList } CWalletTx wtx; + wtx.strFromAccount = accountFrom; + CReserveKey keyChange(wallet); int64_t nFeeRequired = 0; std::string errorMsg; diff --git a/wallet/qt/walletmodel.h b/wallet/qt/walletmodel.h index 395f8ac4a..164b41d9d 100644 --- a/wallet/qt/walletmodel.h +++ b/wallet/qt/walletmodel.h @@ -119,7 +119,8 @@ class WalletModel : public QObject SendCoinsReturn sendCoins(QList recipients, boost::shared_ptr ntp1wallet, const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, - const CCoinControl* coinControl = nullptr); + const CCoinControl* coinControl = nullptr, + const std::string& accountFrom = ""); // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString& passphrase); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 411efebd0..2220efa26 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1379,7 +1379,7 @@ CAmount CWallet::GetImmatureBalance(const ITxDB& txdb) const // populate vCoins with vector of spendable COutputs void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fOnlyConfirmed, bool fIncludeColdStaking, bool fIncludeDelegated, - const CCoinControl* coinControl, const std::string& addressFrom) const + const CCoinControl* coinControl, const std::string& accountFrom) const { vCoins.clear(); @@ -1435,14 +1435,14 @@ void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fO if (mine == ISMINE_SPENDABLE_STAKEABLE && !fIncludeColdStaking && !fIncludeDelegated) continue; - // if addressFrom is set skip coins from other accounts - if (addressFrom != "") { + // if accountFrom is set skip coins from other accounts + if (accountFrom != "") { auto scriptPubKey = pcoin->vout[i].scriptPubKey; CTxDestination destination; if (ExtractDestination(txdb, scriptPubKey, destination)) { const auto entry = this->mapAddressBook.get(destination); - if (!entry || !entry.is_initialized() || entry->name != addressFrom) + if (!entry || !entry.is_initialized() || entry->name != accountFrom) continue; } } diff --git a/wallet/wallet.h b/wallet/wallet.h index 48bfeebb1..f213186c5 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -194,7 +194,7 @@ class CWallet : public CCryptoKeyStore bool fIncludeDelegated = false) const; void AvailableCoins(const ITxDB& txdb, std::vector& vCoins, bool fOnlyConfirmed = true, bool fIncludeColdStaking = false, bool fIncludeDelegated = true, - const CCoinControl* coinControl = nullptr, const std::string& addressFrom = "") const; + const CCoinControl* coinControl = nullptr, const std::string& accountFrom = "") const; // Get available p2cs utxo bool GetAvailableP2CSCoins(const ITxDB& txdb, std::vector& vCoins) const; From dbbbc4d41dc7123285258e90b290a6174a6e9190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 24 Mar 2023 12:13:34 +0100 Subject: [PATCH 038/129] Add cleanWithTxs.sh script --- cleanWithTxs.sh | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100755 cleanWithTxs.sh diff --git a/cleanWithTxs.sh b/cleanWithTxs.sh new file mode 100755 index 000000000..89e966ab3 --- /dev/null +++ b/cleanWithTxs.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +echo "Are you sure you want to delete the neblio data directory?" +read -p "Close neblio app and press enter to continue... " + +[ -e ~/.neblio ] && rm -r ~/.neblio +mkdir ~/.neblio + +cat < ~/.neblio/neblio.conf +regtest=1 +server=1 +port=10000 +rpcport=6326 +listenonion=0 +rpcuser=user +rpcpassword=password +enableaccounts=1 +staking=0 +EOT + +read -p "Start neblio and paste your address here: " ADDR + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" + +sleep 0.2 + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" + +sleep 0.2 + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" + +LEDGER_ADDR="TSf9z6pmeg5FyVGuQbBBU1iZKUM5ijhXue" +# read -p "Start neblio and paste your second address here: " LEDGER_ADDR + +for i in {1..20} +do + curl --location '127.0.0.1:6326' \ + --header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ + --header 'Content-Type: application/json' \ + --data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"sendfrom\", + \"params\":[\"First\", \"$LEDGER_ADDR\", 1] + }" + sleep 0.2 +done + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" + +sleep 0.2 + +for i in {1..20} +do + curl --location '127.0.0.1:6326' \ + --header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ + --header 'Content-Type: application/json' \ + --data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"sendfrom\", + \"params\":[\"First\", \"$LEDGER_ADDR\", 1] + }" + sleep 0.2 +done + +curl --location '127.0.0.1:6326' \ +--header 'Authorization: Basic dXNlcjpwYXNzd29yZA==' \ +--header 'Content-Type: application/json' \ +--data "{ + \"jsonrpc\": \"2.0\", + \"id\": 2, + \"method\": \"generatetoaddress\", + \"params\":[10, \"$ADDR\"] +}" \ No newline at end of file From 92acb0daa35d37e390213c805c5c9186364fa72b Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 24 Mar 2023 17:48:26 +0100 Subject: [PATCH 039/129] Fix address format on device --- wallet/ledger/ledger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index ae2c36eac..ee3d43d91 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -36,7 +36,8 @@ namespace ledger payload.push_back(pathBytes.size() / 4); utils::AppendVector(payload, pathBytes); - auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x02, payload); + // 0x00 = P2_LEGACY (base58) + auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); auto err = std::get<0>(result); auto buffer = std::get<1>(result); if (err != Error::SUCCESS) From ea25d8ea85179e7afa17b0900d52cec3fcac3129 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 24 Mar 2023 19:37:44 +0100 Subject: [PATCH 040/129] Catch ledger errors when siging txs --- wallet/wallet.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 2220efa26..510b01f52 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -18,6 +18,7 @@ #include #include #include +#include "ledger/error.h" #include "ledger/ledger.h" #include "ledger/utils.h" #include "ledgerBridge.h" @@ -853,7 +854,7 @@ bool CWallet::IsChange(const ITxDB& txdb, const CTransaction& tx, const CTxOut& if (ExtractDestination(txdb, txout.scriptPubKey, address) && ::IsMine(*this, address) != ISMINE_NO) { if (!mapAddressBook.exists(address)) return true; - } + } // for Ledger consider any outputs sent to the same address as change { @@ -1438,9 +1439,9 @@ void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fO // if accountFrom is set skip coins from other accounts if (accountFrom != "") { auto scriptPubKey = pcoin->vout[i].scriptPubKey; - + CTxDestination destination; - if (ExtractDestination(txdb, scriptPubKey, destination)) { + if (ExtractDestination(txdb, scriptPubKey, destination)) { const auto entry = this->mapAddressBook.get(destination); if (!entry || !entry.is_initialized() || entry->name != accountFrom) continue; @@ -2222,7 +2223,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey}; } else { if (SignSignature(*this, *coin.first, wtxNew, nIn) != SignatureState::Verified) { @@ -2472,12 +2473,12 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector Date: Sat, 25 Mar 2023 14:50:07 +0100 Subject: [PATCH 041/129] Improve naming of ledger UI elements --- wallet/qt/sendcoinsdialog.cpp | 6 ++-- wallet/qt/ui_sendcoinsdialog.h | 54 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 3d3332b69..bc836f3f3 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -144,7 +144,7 @@ void SendCoinsDialog::on_ledgerAddressBookButton_clicked() dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { ui->ledgerPayFromAddressEdit->setText(dlg.getReturnAddress()); - ui->ledgerPayFromLabelEdit->setText(dlg.getReturnLabel()); + ui->ledgerPayFromNameEdit->setText(dlg.getReturnLabel()); } } @@ -261,8 +261,8 @@ void SendCoinsDialog::on_sendButton_clicked() std::string accountFrom = ""; if (ui->ledgerCheckBox->isChecked()) - accountFrom = ui->ledgerPayFromLabelEdit->text().toStdString(); - + accountFrom = ui->ledgerPayFromNameEdit->text().toStdString(); + auto sendstatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, coinControl, accountFrom); diff --git a/wallet/qt/ui_sendcoinsdialog.h b/wallet/qt/ui_sendcoinsdialog.h index a777581e8..edaa6de46 100644 --- a/wallet/qt/ui_sendcoinsdialog.h +++ b/wallet/qt/ui_sendcoinsdialog.h @@ -94,12 +94,12 @@ class Ui_SendCoinsDialog QCheckBox* ledgerCheckBox; QWidget* ledgerWidget; QGridLayout* ledgerGridLayout; - QLabel* ledgerPayFromLabel; - QHBoxLayout* ledgerPayFromLayout; + QLabel* ledgerPayFromAddressLabel; + QHBoxLayout* ledgerPayFromAddressLayout; QValidatedLineEdit* ledgerPayFromAddressEdit; QToolButton* ledgerAddressBookButton; - QLabel* ledgerPayFromLabel2; - QValidatedLineEdit* ledgerPayFromLabelEdit; + QLabel* ledgerPayFromNameLabel; + QValidatedLineEdit* ledgerPayFromNameEdit; void setupUi(QDialog* SendCoinsDialog) { @@ -429,25 +429,25 @@ class Ui_SendCoinsDialog ledgerWidget->setLayout(ledgerGridLayout); - ledgerPayFromLabel = new QLabel(); - ledgerPayFromLabel->setObjectName(QStringLiteral("ledgerPayFromLabel")); - ledgerPayFromLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); + ledgerPayFromAddressLabel = new QLabel(); + ledgerPayFromAddressLabel->setObjectName(QStringLiteral("ledgerPayFromAddressLabel")); + ledgerPayFromAddressLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - ledgerGridLayout->addWidget(ledgerPayFromLabel, 0, 0, 1, 1); + ledgerGridLayout->addWidget(ledgerPayFromAddressLabel, 0, 0, 1, 1); - ledgerPayFromLayout = new QHBoxLayout(); - ledgerPayFromLayout->setObjectName(QStringLiteral("ledgerPayFromLayout")); - ledgerPayFromLayout->setSpacing(0); + ledgerPayFromAddressLayout = new QHBoxLayout(); + ledgerPayFromAddressLayout->setObjectName(QStringLiteral("ledgerPayFromAddressLayout")); + ledgerPayFromAddressLayout->setSpacing(0); - ledgerGridLayout->addLayout(ledgerPayFromLayout, 0, 1, 1, 1); + ledgerGridLayout->addLayout(ledgerPayFromAddressLayout, 0, 1, 1, 1); ledgerPayFromAddressEdit = new QValidatedLineEdit(); ledgerPayFromAddressEdit->setObjectName(QStringLiteral("ledgerPayFromAddressEdit")); ledgerPayFromAddressEdit->setMaxLength(34); ledgerPayFromAddressEdit->setDisabled(true); // should not be typed in manually - ledgerPayFromLabel->setBuddy(ledgerPayFromAddressEdit); + ledgerPayFromAddressLabel->setBuddy(ledgerPayFromAddressEdit); - ledgerPayFromLayout->addWidget(ledgerPayFromAddressEdit); + ledgerPayFromAddressLayout->addWidget(ledgerPayFromAddressEdit); ledgerAddressBookButton = new QToolButton(); ledgerAddressBookButton->setObjectName(QStringLiteral("ledgerAddressBookButton")); @@ -455,20 +455,20 @@ class Ui_SendCoinsDialog icon10.addFile(QStringLiteral(":/icons/address-book"), QSize(), QIcon::Normal, QIcon::Off); ledgerAddressBookButton->setIcon(icon10); - ledgerPayFromLayout->addWidget(ledgerAddressBookButton); + ledgerPayFromAddressLayout->addWidget(ledgerAddressBookButton); - ledgerPayFromLabel2 = new QLabel(); - ledgerPayFromLabel2->setObjectName(QStringLiteral("ledgerPayFromLabel2")); - ledgerPayFromLabel2->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); + ledgerPayFromNameLabel = new QLabel(); + ledgerPayFromNameLabel->setObjectName(QStringLiteral("ledgerPayFromNameLabel")); + ledgerPayFromNameLabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - ledgerGridLayout->addWidget(ledgerPayFromLabel2, 1, 0, 1, 1); + ledgerGridLayout->addWidget(ledgerPayFromNameLabel, 1, 0, 1, 1); - ledgerPayFromLabelEdit = new QValidatedLineEdit(); - ledgerPayFromLabelEdit->setObjectName(QStringLiteral("ledgerPayFromLabelEdit")); - ledgerPayFromLabelEdit->setDisabled(true); // should not be typed in manually - ledgerPayFromLabel2->setBuddy(ledgerPayFromLabelEdit); + ledgerPayFromNameEdit = new QValidatedLineEdit(); + ledgerPayFromNameEdit->setObjectName(QStringLiteral("ledgerPayFromNameEdit")); + ledgerPayFromNameEdit->setDisabled(true); // should not be typed in manually + ledgerPayFromNameLabel->setBuddy(ledgerPayFromNameEdit); - ledgerGridLayout->addWidget(ledgerPayFromLabelEdit, 1, 1, 1, 1); + ledgerGridLayout->addWidget(ledgerPayFromNameEdit, 1, 1, 1, 1); verticalLayout->addSpacing(10); @@ -633,13 +633,13 @@ class Ui_SendCoinsDialog ledgerCheckBox->setToolTip( QApplication::translate("SendCoinsDialog", "If you choose to pay from a Ledger address, only a single source address will be used.", Q_NULLPTR)); #endif // QT_NO_TOOLTIP - ledgerPayFromLabel->setText( + ledgerPayFromAddressLabel->setText( QApplication::translate("SendCoinsDialog", "Pay from:", Q_NULLPTR)); ledgerPayFromAddressEdit->setPlaceholderText( QApplication::translate("SendCoinsDialog", "Enter a neblio address owned by your Ledger device", Q_NULLPTR)); - ledgerPayFromLabel2->setText( + ledgerPayFromNameLabel->setText( QApplication::translate("SendCoinsDialog", "Label:", Q_NULLPTR)); - ledgerPayFromLabelEdit->setPlaceholderText( + ledgerPayFromNameEdit->setPlaceholderText( QApplication::translate("SendCoinsDialog", "A label for the Ledger address", Q_NULLPTR)); #ifndef QT_NO_TOOLTIP addButton->setToolTip(QApplication::translate("SendCoinsDialog", From e04713f5c97b5edaea09d75540d2f7fc9fb2898e Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 27 Mar 2023 13:21:16 +0200 Subject: [PATCH 042/129] Fix coin control dialog crash --- wallet/qt/walletmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/qt/walletmodel.cpp b/wallet/qt/walletmodel.cpp index ea8c5ee4c..7c1ca6b96 100644 --- a/wallet/qt/walletmodel.cpp +++ b/wallet/qt/walletmodel.cpp @@ -575,7 +575,7 @@ void WalletModel::listCoins(std::map>& mapCoins) c for (const COutput& out : vCoins) { COutput cout = out; - while (wallet->IsChange(txdb, (CTransaction&) cout.tx, cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && + while (wallet->IsChange(txdb, *cout.tx, cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && IsMineCheck(wallet->IsMine(cout.tx->vin[0]), isminetype::ISMINE_SPENDABLE_ALL)) { if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; From 760933667a660a1039f755f9437bc3937d59cd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 28 Mar 2023 12:53:32 +0200 Subject: [PATCH 043/129] Make it so that Ledger and non-Ledger inputs won't be used in the same tx --- wallet/qt/sendcoinsdialog.cpp | 10 ++++--- wallet/qt/walletmodel.cpp | 8 ++++-- wallet/qt/walletmodel.h | 2 +- wallet/rpcwallet.cpp | 4 +++ wallet/transaction.cpp | 4 +-- wallet/transaction.h | 2 +- wallet/wallet.cpp | 52 ++++++++++++++++++++++------------- wallet/wallet.h | 7 +++-- 8 files changed, 57 insertions(+), 32 deletions(-) diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index bc836f3f3..663bec95c 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -259,12 +259,14 @@ void SendCoinsDialog::on_sendButton_clicked() if (model->getOptionsModel() && model->getOptionsModel()->getCoinControlFeatures()) coinControl = CoinControlDialog::coinControl; - std::string accountFrom = ""; - if (ui->ledgerCheckBox->isChecked()) - accountFrom = ui->ledgerPayFromNameEdit->text().toStdString(); + auto fLedgerTx = ui->ledgerCheckBox->isChecked(); + std::string strFromAccount = ""; + if (fLedgerTx) + strFromAccount = ui->ledgerPayFromNameEdit->text().toStdString(); + auto sendstatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, - coinControl, accountFrom); + coinControl, strFromAccount, fLedgerTx); switch (sendstatus.status) { case WalletModel::InvalidAddress: diff --git a/wallet/qt/walletmodel.cpp b/wallet/qt/walletmodel.cpp index 7c1ca6b96..2af9761ff 100644 --- a/wallet/qt/walletmodel.cpp +++ b/wallet/qt/walletmodel.cpp @@ -218,7 +218,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, const CCoinControl* coinControl, - const std::string& accountFrom) + const std::string& strFromAccount, + bool fLedgerTx) { qint64 total = 0; QString hex; @@ -258,7 +259,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList int64_t nBalance = 0; std::vector vCoins; - wallet->AvailableCoins(txdb, vCoins, true, false, false, coinControl, accountFrom); + wallet->AvailableCoins(txdb, vCoins, true, false, false, coinControl, strFromAccount, fLedgerTx); for (const COutput& out : vCoins) { nBalance += out.tx->vout[out.i].nValue; @@ -315,7 +316,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList } CWalletTx wtx; - wtx.strFromAccount = accountFrom; + wtx.strFromAccount = strFromAccount; + wtx.fLedgerTx = fLedgerTx; CReserveKey keyChange(wallet); int64_t nFeeRequired = 0; diff --git a/wallet/qt/walletmodel.h b/wallet/qt/walletmodel.h index 164b41d9d..b9e2ea34b 100644 --- a/wallet/qt/walletmodel.h +++ b/wallet/qt/walletmodel.h @@ -120,7 +120,7 @@ class WalletModel : public QObject boost::shared_ptr ntp1wallet, const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, const CCoinControl* coinControl = nullptr, - const std::string& accountFrom = ""); + const std::string& strFromAccount = "", bool fLedgerTx = false); // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString& passphrase); diff --git a/wallet/rpcwallet.cpp b/wallet/rpcwallet.cpp index f4aba6375..0eb87569a 100644 --- a/wallet/rpcwallet.cpp +++ b/wallet/rpcwallet.cpp @@ -1520,6 +1520,10 @@ Value sendfrom(const Array& params, bool fHelp) if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) wtx.mapValue["to"] = params[5].get_str(); + set accountAddresses; + GetAccountAddresses(strAccount, accountAddresses); + wtx.fLedgerTx = ::IsMine(*pwalletMain, *accountAddresses.begin()) == ISMINE_LEDGER; + EnsureWalletIsUnlocked(); // Check funds diff --git a/wallet/transaction.cpp b/wallet/transaction.cpp index 1e563595a..e7c190b76 100644 --- a/wallet/transaction.cpp +++ b/wallet/transaction.cpp @@ -313,14 +313,14 @@ Result CTransaction::CheckTransaction(const ITxDB& txdb } CAmount CTransaction::GetMinFee(const ITxDB& txdb, unsigned int nBlockSize, enum GetMinFee_mode mode, - unsigned int nBytes, bool sigsIncluded) const + unsigned int nBytes, bool fSigsIncluded) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE CAmount nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; // for Ledger the transaction doesn't contain signatures when calculating the fee // so we need to add the size of the signatures to nBytes - if (!sigsIncluded) { + if (!fSigsIncluded) { const int SCRIPT_SIG_MAX_SIZE = 108; nBytes += SCRIPT_SIG_MAX_SIZE * vin.size(); } diff --git a/wallet/transaction.h b/wallet/transaction.h index a575c9bbd..12c8a6e49 100644 --- a/wallet/transaction.h +++ b/wallet/transaction.h @@ -126,7 +126,7 @@ class CTransaction int64_t GetValueIn(const MapPrevTx& mapInputs) const; int64_t GetMinFee(const ITxDB& txdb, unsigned int nBlockSize = 1, - enum GetMinFee_mode mode = GMF_BLOCK, unsigned int nBytes = 0, bool sigsIncluded = true) const; + enum GetMinFee_mode mode = GMF_BLOCK, unsigned int nBytes = 0, bool fSigsIncluded = true) const; bool ReadFromDisk(CDiskTxPos pos, const ITxDB& txdb); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 510b01f52..bd1e69906 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1380,7 +1380,8 @@ CAmount CWallet::GetImmatureBalance(const ITxDB& txdb) const // populate vCoins with vector of spendable COutputs void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fOnlyConfirmed, bool fIncludeColdStaking, bool fIncludeDelegated, - const CCoinControl* coinControl, const std::string& accountFrom) const + const CCoinControl* coinControl, const std::string& strFromAccount, + bool fIncludeLedgerCoins) const { vCoins.clear(); @@ -1435,15 +1436,18 @@ void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fO // skip auto-delegated coins if (mine == ISMINE_SPENDABLE_STAKEABLE && !fIncludeColdStaking && !fIncludeDelegated) continue; + // skip Ledger coins for non-ledger transactions + if (mine == ISMINE_LEDGER && !fIncludeLedgerCoins) + continue; // if accountFrom is set skip coins from other accounts - if (accountFrom != "") { + if (strFromAccount != "") { auto scriptPubKey = pcoin->vout[i].scriptPubKey; CTxDestination destination; if (ExtractDestination(txdb, scriptPubKey, destination)) { const auto entry = this->mapAddressBook.get(destination); - if (!entry || !entry.is_initialized() || entry->name != accountFrom) + if (!entry || !entry.is_initialized() || entry->name != strFromAccount) continue; } } @@ -1845,10 +1849,11 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n, const ITxDB& txdb, bool CWallet::SelectCoins(const ITxDB& txdb, CAmount nTargetValue, unsigned int nSpendTime, set>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, bool fIncludeColdStaking, - bool fIncludeDelegated, bool avoidNTP1Outputs, const std::string& accountFrom) const + bool fIncludeDelegated, bool avoidNTP1Outputs, const std::string& strFromAccount, + bool fIncludeLedgerCoins) const { vector vCoins; - AvailableCoins(txdb, vCoins, true, fIncludeColdStaking, fIncludeDelegated, coinControl, accountFrom); + AvailableCoins(txdb, vCoins, true, fIncludeColdStaking, fIncludeDelegated, coinControl, strFromAccount, fIncludeLedgerCoins); // coin control -> return all selected outputs (we want all selected to go into the transaction for // sure) @@ -1925,10 +1930,11 @@ std::size_t CWallet::getWalletTxsCount() } void AddCoinsToInputsSet(const ITxDB& txdb, set>& setInputs, - const NTP1OutPoint& input) + const NTP1OutPoint& input, const std::string& strFromAccount, + bool fIncludeLedgerCoins) { std::vector coins; - pwalletMain->AvailableCoins(txdb, coins); + pwalletMain->AvailableCoins(txdb, coins, true, false, true, nullptr, strFromAccount, fIncludeLedgerCoins); auto itCoin = std::find_if(coins.begin(), coins.end(), [&input](const COutput& o) { return (o.tx->GetHash() == input.getHash() && o.i == (int)input.getIndex()); }); @@ -2100,12 +2106,12 @@ int CWallet::AddNTP1TokenOutputsToTx(CTransaction& wtxNew, const NTP1SendTxData& return tokenOutputsOffset; } -uint64_t GetTotalNeblsInInputs(const ITxDB& txdb, const std::vector& inputs) +uint64_t GetTotalNeblsInInputs(const ITxDB& txdb, const std::vector& inputs, const std::string &strFromAccount, bool fIncludeLedgerCoins = false) { uint64_t total = 0; std::vector avOutputs; - pwalletMain->AvailableCoins(txdb, avOutputs); + pwalletMain->AvailableCoins(txdb, avOutputs, true, false, true, nullptr, strFromAccount, fIncludeLedgerCoins); for (const auto& input : inputs) { // find the output (now input) in the list of available coins @@ -2187,7 +2193,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector> setCoins; CAmount nValueIn = 0; if (!SelectCoins(txdb, nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl, false, - fIncludeDelegated, isNTP1Issuance, wtxNew.strFromAccount)) { + fIncludeDelegated, isNTP1Issuance, wtxNew.strFromAccount, wtxNew.fLedgerTx)) { CreateErrorMsg(errorMsg, "Failed to collect nebls for the transaction. You may have chosen to " "spend balance you do not have. For example, you chose to spend " @@ -2222,8 +2228,15 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector usedInputs = ntp1TxData.getUsedInputs(); - nValueIn = GetTotalNeblsInInputs(txdb, usedInputs); + nValueIn = GetTotalNeblsInInputs(txdb, usedInputs, wtxNew.strFromAccount, wtxNew.fLedgerTx); } catch (std::exception& ex) { NLog.write(b_sev::err, "Failed to select NTP1 tokens with error: {}", ex.what()); @@ -2322,7 +2335,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(&coinControl->destChange)) { scriptChange.SetDestination(coinControl->destChange); changeKeyID = boost::get(coinControl->destChange); - } else if (isLedgerTx) { + } else if (wtxNew.fLedgerTx) { CTxDestination destChange; if (!ExtractDestination(txdb, ledgerInputKey.get(), destChange)) { NLog.write(b_sev::err, @@ -2387,7 +2400,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey}; } else { if (SignSignature(*this, *coin.first, wtxNew, nIn) != SignatureState::Verified) { @@ -2466,14 +2479,14 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl = nullptr, bool fIncludeColdStaking = false, bool fIncludeDelegated = true, - bool avoidNTP1Outputs = false, const std::string& addressFrom = "") const; + bool avoidNTP1Outputs = false, const std::string& strFromAccount = "", + bool fIncludeLedgerCoins = false) const; CWalletDB* pwalletdbEncryption; @@ -194,7 +195,8 @@ class CWallet : public CCryptoKeyStore bool fIncludeDelegated = false) const; void AvailableCoins(const ITxDB& txdb, std::vector& vCoins, bool fOnlyConfirmed = true, bool fIncludeColdStaking = false, bool fIncludeDelegated = true, - const CCoinControl* coinControl = nullptr, const std::string& accountFrom = "") const; + const CCoinControl* coinControl = nullptr, const std::string& strFromAccount = "", + bool fIncludeLedgerCoins = false) const; // Get available p2cs utxo bool GetAvailableP2CSCoins(const ITxDB& txdb, std::vector& vCoins) const; @@ -555,6 +557,7 @@ class CWalletTx : public CMerkleTx std::string strFromAccount; std::vector vfSpent; // which outputs are already spent int64_t nOrderPos; // position in ordered transaction list + bool fLedgerTx; // memory only mutable boost::optional c_WatchDebitCached; From 991f72dbd241f01135ecca23552d3af99eef7904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 29 Mar 2023 12:28:47 +0200 Subject: [PATCH 044/129] Add Ledger keys to wallet import --- wallet/rpcdump.cpp | 95 +++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/wallet/rpcdump.cpp b/wallet/rpcdump.cpp index 7f1a3715a..dd1470e34 100644 --- a/wallet/rpcdump.cpp +++ b/wallet/rpcdump.cpp @@ -420,6 +420,27 @@ bool _AddKeyToLocalWallet(const CKey& Key, const std::string& strLabel, int64_t return true; } +bool _AddLedgerKeyToLocalWallet(const CLedgerKey& ledgerKey, const std::string& strLabel, bool addInAddressBook) +{ + CKeyID keyid = ledgerKey.vchPubKey.GetID(); + NLog.write(b_sev::info, "Importing ledger address {}...", CBitcoinAddress(keyid).ToString()); + + // if key exists already in the local wallet, don't add it + if (pwalletMain->HaveLedgerKey(keyid)) { + return false; + } + + // attempt to add the key + if (!pwalletMain->AddLedgerKey(ledgerKey)) { + return false; + } + + if (addInAddressBook) { + pwalletMain->SetAddressBookEntry(keyid, strLabel); + } + return true; +} + CBitcoinAddress GetAddressFromKey(const CKey& key) { CBitcoinAddress a; @@ -487,6 +508,11 @@ std::pair ImportBackupWallet(const std::string& Src, std::string& Pa std::set allKeyIDsSet; backupWallet.GetKeys(allKeyIDsSet); + std::set ledgerKeyIDsSet; + backupWallet.GetLedgerKeys(ledgerKeyIDsSet); + + allKeyIDsSet.insert(ledgerKeyIDsSet.begin(),ledgerKeyIDsSet.end()); + // deque to simply elements access const std::deque allKeyIDs(allKeyIDsSet.begin(), allKeyIDsSet.end()); using AddressBookIt = std::map::const_iterator; @@ -500,29 +526,37 @@ std::pair ImportBackupWallet(const std::string& Src, std::string& Pa for (long i = 0; i < static_cast(allKeyIDs.size()); i++) { AddressBookIt it = addrBook.find(allKeyIDs[i]); bool foundKeyInAddressBook = (it != addrBook.end()); - - // retrieve key using key ID - CKey key; - bool getKeySucceeded = backupWallet.GetKey(allKeyIDs[i], key); - if (!getKeySucceeded) - continue; - - // add the key, whether to the address book or simply to reserve - if (foundKeyInAddressBook) { - // import from address book - bool addSucceeded = _AddKeyToLocalWallet( - key, it->second.name, - backupWallet.mapKeyMetadata.at(boost::get(it->first)).nCreateTime, earliestTime, - true); - if (addSucceeded) - succeessfullyAddedOutOfTotal.first++; - } else { - // import reserve keys - bool addSucceeded = - _AddKeyToLocalWallet(key, "", backupWallet.mapKeyMetadata.at(allKeyIDs[i]).nCreateTime, - earliestTime, importReserveToAddressBook); - if (addSucceeded) - succeessfullyAddedOutOfTotal.first++; + + CKey key; + CLedgerKey ledgerKey; + if (backupWallet.GetKey(allKeyIDs[i], key)) { + // add the key, whether to the address book or simply to reserve + if (foundKeyInAddressBook) { + // import from address book + bool addSucceeded = _AddKeyToLocalWallet( + key, it->second.name, + backupWallet.mapKeyMetadata.at(boost::get(it->first)).nCreateTime, earliestTime, + true); + if (addSucceeded) + succeessfullyAddedOutOfTotal.first++; + } else { + // import reserve keys + bool addSucceeded = + _AddKeyToLocalWallet(key, "", backupWallet.mapKeyMetadata.at(allKeyIDs[i]).nCreateTime, + earliestTime, importReserveToAddressBook); + if (addSucceeded) + succeessfullyAddedOutOfTotal.first++; + } + } else if (backupWallet.GetLedgerKey(allKeyIDs[i], ledgerKey)) { + // all Ledger keys should currently be in the address book + if (foundKeyInAddressBook) { + bool addSucceeded = _AddLedgerKeyToLocalWallet(ledgerKey, it->second.name, true); + if (addSucceeded) + succeessfullyAddedOutOfTotal.first++; + } else { + // TODO GK - check after change outputs are implemented + throw std::runtime_error("Ledger key not found in address book."); + } } } _RescanBlockchain(earliestTime, txdb); @@ -572,6 +606,12 @@ std::string GetCurrentWalletHash() { std::set allKeyIDsSet; pwalletMain->GetKeys(allKeyIDsSet); + + std::set ledgerKeyIDsSet; + pwalletMain->GetLedgerKeys(ledgerKeyIDsSet); + + allKeyIDsSet.insert(ledgerKeyIDsSet.begin(),ledgerKeyIDsSet.end()); + // deque to simply elements access const std::deque allKeyIDs(allKeyIDsSet.begin(), allKeyIDsSet.end()); @@ -580,12 +620,15 @@ std::string GetCurrentWalletHash() for (long i = 0; i < static_cast(allKeyIDs.size()); i++) { // retrieve key using key ID CKey key; - bool getKeySucceeded = pwalletMain->GetKey(allKeyIDs[i], key); - if (!getKeySucceeded) { + CLedgerKey ledgerKey; + if (pwalletMain->GetKey(allKeyIDs[i], key)) { + finalStringToHash += key.GetPubKey().GetHash().ToString(); + } else if (pwalletMain->GetLedgerKey(allKeyIDs[i], ledgerKey)) { + finalStringToHash += ledgerKey.vchPubKey.GetHash().ToString(); + } else { NLog.write(b_sev::err, "Failed to get key number {}", i); continue; } - finalStringToHash += key.GetPubKey().GetHash().ToString(); } if (finalStringToHash.empty()) { throw std::runtime_error("Error: Backup public keys hash is empty. This should not happen."); From 9ab4e78874e1391f3bc7b13cb1cc1caa00847828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 29 Mar 2023 14:15:34 +0200 Subject: [PATCH 045/129] Move Ledger GetPublicKey call into Ledger bridge --- wallet/ledgerBridge.cpp | 27 ++++++++++++++++++++++----- wallet/ledgerBridge.h | 2 ++ wallet/qt/addresstablemodel.cpp | 10 +++------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 92867643f..9f11333b1 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -11,11 +11,29 @@ #include namespace ledgerbridge -{ +{ + const ledger::Transport::TransportType TRANSPORT_TYPE = ledger::Transport::TransportType::SPECULOS; + LedgerBridge::LedgerBridge() {} LedgerBridge::~LedgerBridge() {} + ledger::bytes LedgerBridge::GetPublicKey(const std::string& path, bool display) { + ledger::Ledger ledger(TRANSPORT_TYPE); + ledger.open(); + + auto result = ledger.GetPublicKey(path, display); + + ledger.close(); + + return ledger::utils::CompressPubKey(std::get<0>(result)); + } + + ledger::bytes LedgerBridge::GetPublicKey(int account, int index, bool display) { + std::string path = ledger::bip32::GetBip32Path(account, index); + return GetPublicKey(path, display); + } + void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos) { std::vector signaturePaths; @@ -45,7 +63,7 @@ namespace ledgerbridge auto changePath = signaturePaths[0]; - ledger::Ledger ledger(ledger::Transport::TransportType::SPECULOS); + ledger::Ledger ledger(TRANSPORT_TYPE); ledger.open(); // sign tx @@ -54,9 +72,8 @@ namespace ledgerbridge // add signatures to tx and verify for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { auto signature = std::get<1>(signTxResults[sigIndex]); - - auto pubKeyResult = ledger.GetPublicKey(signaturePaths[sigIndex], false); - auto pubKey = CPubKey(ledger::utils::CompressPubKey(std::get<0>(pubKeyResult))); + + auto pubKey = CPubKey(GetPublicKey(signaturePaths[sigIndex], false)); // hash type signature.push_back(0x01); diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 17a05d047..93cedf7ac 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -22,6 +22,8 @@ namespace ledgerbridge LedgerBridge(); ~LedgerBridge(); + ledger::bytes GetPublicKey(const std::string& path, bool display); + ledger::bytes GetPublicKey(int account, int index, bool display); void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos); private: ledger::Tx ToLedgerTx(const CTransaction& tx); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index e8df9087b..304b04787 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -2,8 +2,8 @@ #include "guiutil.h" #include "ledger/bip32.h" #include "ledger/error.h" -#include "ledger/ledger.h" #include "ledger/utils.h" +#include "ledgerBridge.h" #include "walletmodel.h" #include "base58.h" @@ -428,12 +428,8 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con ledger::bytes pubKey; try { - ledger::Ledger l; - l.open(); - std::string path = ledger::bip32::GetBip32Path(account, index); - auto result = l.GetPublicKey(path, true); - l.close(); - pubKey = ledger::utils::CompressPubKey(std::get<0>(result)); + ledgerbridge::LedgerBridge ledgerBridge; + pubKey = ledgerBridge.GetPublicKey(account, index, true); } catch (const ledger::Error& e) { editStatus = LEDGER_ERROR; ledgerError = e; From bc0dcd36e0eb777b9fd47eafc4b6a1c397706b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Thu, 30 Mar 2023 09:21:20 +0200 Subject: [PATCH 046/129] Refactor BIP32 path into a class --- wallet/ledger/bip32.cpp | 97 ++++++++++++++++++++++++++------- wallet/ledger/bip32.h | 32 +++++++++-- wallet/ledger/ledger.cpp | 16 +++--- wallet/ledger/ledger.h | 7 ++- wallet/ledgerBridge.cpp | 17 +++--- wallet/ledgerBridge.h | 5 +- wallet/qt/addresstablemodel.cpp | 6 +- wallet/qt/editaddressdialog.cpp | 6 +- wallet/rpcdump.cpp | 6 +- wallet/walletdb.h | 7 ++- 10 files changed, 141 insertions(+), 58 deletions(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index 5c360e02a..31225760c 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -3,19 +3,26 @@ #include -namespace ledger::bip32 +namespace ledger { - uint32_t Harden(uint32_t n) + uint32_t Bip32Path::Harden(uint32_t n) const { return n | 0x80000000; } - // copied from https://github.com/bitcoin/bitcoin/blob/master/src/util/bip32.cpp#L13 - // and adjusted for uint8_t instead of uint32_t vector - bytes ParseHDKeypath(const std::string &keypath_str) + uint32_t Bip32Path::Unharden(uint32_t n) const { - bytes keypath; - std::stringstream ss(keypath_str); + return n & ~(0x80000000); + } + + bool Bip32Path::IsHardened(uint32_t n) const + { + return n == Harden(n); + } + + Bip32Path::Bip32Path(const std::string &keyPathStr) { + std::vector components; + std::stringstream ss(keyPathStr); std::string item; bool first = true; while (std::getline(ss, item, '/')) @@ -49,22 +56,70 @@ namespace ledger::bip32 throw std::runtime_error("Invalid keypath"); } - utils::AppendUint32(keypath, std::stoul(item) | path); + // utils::AppendUint32(keypath, std::stoul(item) | path); + components.push_back(std::stoul(item) | path); first = false; } - return keypath; + + if (components.size() != 5) + { + throw std::runtime_error("Invalid keypath size"); + } + + if (components[0] != Harden(BIP32_PURPOSE)) + { + throw std::runtime_error("Invalid keypath purpose"); + } + + if (components[1] != Harden(BIP32_COIN_TYPE)) + { + throw std::runtime_error("Invalid keypath coin type"); + } + + if (!IsHardened(components[2])) + { + throw std::runtime_error("Invalid keypath account"); + } + + if (IsHardened(components[3])) + { + throw std::runtime_error("Invalid keypath change"); + } + + if (IsHardened(components[4])) + { + throw std::runtime_error("Invalid keypath index"); + } + + account = Unharden(components[2]); + isChange = components[3] == 1; + index = components[4]; + } + + Bip32Path::Bip32Path(const std::string &account, bool isChange, const std::string &index) + : Bip32Path(std::stoul(account), isChange, std::stoul(index)) {}; + + Bip32Path::Bip32Path(uint32_t account, bool isChange, uint32_t index) + : purpose(BIP32_PURPOSE), coinType(BIP32_COIN_TYPE), account(account), isChange(isChange), index(index) {}; + + bytes Bip32Path::Serialize() const + { + bytes serializedKeyPath; + + utils::AppendUint32(serializedKeyPath, Harden(purpose)); + utils::AppendUint32(serializedKeyPath, Harden(coinType)); + utils::AppendUint32(serializedKeyPath, Harden(account)); + utils::AppendUint32(serializedKeyPath, isChange ? 1 : 0); + utils::AppendUint32(serializedKeyPath, index); + + return serializedKeyPath; } - std::string GetBip32Path(const std::string &account, const std::string &index) - { - std::stringstream ss; - ss << "m/44'/146'/" << account << "'/0/" << index; - return ss.str(); - } - - std::string GetBip32Path(uint32_t account, uint32_t index) - { - return GetBip32Path(std::to_string(account), std::to_string(index)); - } -} // namespace ledger::utils + std::string Bip32Path::ToString() const + { + std::stringstream ss; + ss << "m/" << purpose << "'/" << coinType << "'/" << account << "'/" << (isChange ? 1 : 0) << "/" << index; + return ss.str(); + } +} // namespace ledger diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 8851cf9ab..0db7ccf56 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -1,18 +1,38 @@ #ifndef __LEDGER_BIP32 #define __LEDGER_BIP32 1 -#include "ledger.h" +#include "bytes.h" #include #include #include -namespace ledger::bip32 +namespace ledger { - uint32_t Harden(uint32_t n); - bytes ParseHDKeypath(const std::string &keypath_str); - std::string GetBip32Path(const std::string &account, const std::string &index); - std::string GetBip32Path(uint32_t account, uint32_t index); + class Bip32Path + { + public: + const int BIP32_PURPOSE = 44; + const int BIP32_COIN_TYPE = 146; + + Bip32Path(const std::string &keyPath); + Bip32Path(const std::string &account, bool isChange, const std::string &index); + Bip32Path(uint32_t account, bool isChange, uint32_t index); + + uint32_t Harden(uint32_t n) const; + uint32_t Unharden(uint32_t n) const; + bool IsHardened(uint32_t n) const; + + bytes Serialize() const; + std::string ToString() const; + + private: + uint32_t purpose; + uint32_t coinType; + uint32_t account; + bool isChange; + uint32_t index; + }; } #endif \ No newline at end of file diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index ee3d43d91..70e286c59 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -28,11 +28,11 @@ namespace ledger std::cout << "Ledger connection opened." << std::endl; } - std::tuple Ledger::GetPublicKey(const std::string &path, bool confirm) + std::tuple Ledger::GetPublicKey(const Bip32Path path, bool confirm) { auto payload = bytes(); - auto pathBytes = bip32::ParseHDKeypath(path); + auto pathBytes = path.Serialize(); payload.push_back(pathBytes.size() / 4); utils::AppendVector(payload, pathBytes); @@ -118,15 +118,15 @@ namespace ledger return GetTrustedInputRaw(false, utils::IntToBytes(utxoTx.locktime, 4)); } - void Ledger::UntrustedHashTxInputFinalize(const Tx &tx, const std::string &changePath) + void Ledger::UntrustedHashTxInputFinalize(const Tx &tx, bool hasChange, const Bip32Path changePath) { auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE; auto p2 = 0x00; auto p1 = 0xFF; - if (changePath.length() > 0) + if (hasChange) { - auto serializedChangePath = bip32::ParseHDKeypath(changePath); + auto serializedChangePath = changePath.Serialize(); bytes changePathData; changePathData.push_back(serializedChangePath.size() / 4); @@ -219,7 +219,7 @@ namespace ledger } } - std::vector> Ledger::SignTransaction(const Tx &tx, const std::string& changePath, const std::vector &signPaths, const std::vector &utxos) + std::vector> Ledger::SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector& signPaths, const std::vector &utxos) { assert(tx.inputs.size() == signPaths.size()); assert(tx.inputs.size() == utxos.size()); @@ -244,13 +244,13 @@ namespace ledger auto &script = utxos[i].tx.outputs[utxos[i].outputIndex].script; UntrustedHashTxInputStart(tx, trustedInputs, i, script, i == 0); - UntrustedHashTxInputFinalize(tx, changePath); + UntrustedHashTxInputFinalize(tx, hasChange, changePath); auto ins = INS_UNTRUSTED_HASH_SIGN; auto p1 = 0x00; auto p2 = 0x00; - auto serializedSignPath = bip32::ParseHDKeypath(signPaths[i]); + auto serializedSignPath = signPaths[i].Serialize(); bytes data; data.push_back(serializedSignPath.size() / 4); diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index eecff957c..375ddc3ca 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -1,5 +1,6 @@ #pragma once +#include "bip32.h" #include "bytes.h" #include "transport.h" #include "tx.h" @@ -26,8 +27,8 @@ namespace ledger void open(); - std::tuple GetPublicKey(const std::string &path, bool confirm); - std::vector> SignTransaction(const Tx &tx,const std::string& changePath, const std::vector &signPaths, const std::vector &utxos); + std::tuple GetPublicKey(const Bip32Path path, bool confirm); + std::vector> SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector &signPaths, const std::vector &utxos); void close(); @@ -37,7 +38,7 @@ namespace ledger bytes ProcessScriptBlocks(const bytes &script, uint32_t sequence); bytes GetTrustedInput(const Tx &utxoTx, uint32_t indexLookup); bytes GetTrustedInputRaw(bool firstRound, const bytes &data); - void UntrustedHashTxInputFinalize(const Tx &tx, const std::string &changePath); + void UntrustedHashTxInputFinalize(const Tx &tx, bool hasChange, const Bip32Path changePath); void UntrustedHashTxInputStart(const Tx &tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction); TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); }; diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 9f11333b1..6b47a0ebe 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -18,7 +18,8 @@ namespace ledgerbridge LedgerBridge::~LedgerBridge() {} - ledger::bytes LedgerBridge::GetPublicKey(const std::string& path, bool display) { + ledger::bytes LedgerBridge::GetPublicKey(const ledger::Bip32Path path, bool display) + { ledger::Ledger ledger(TRANSPORT_TYPE); ledger.open(); @@ -29,14 +30,14 @@ namespace ledgerbridge return ledger::utils::CompressPubKey(std::get<0>(result)); } - ledger::bytes LedgerBridge::GetPublicKey(int account, int index, bool display) { - std::string path = ledger::bip32::GetBip32Path(account, index); - return GetPublicKey(path, display); + ledger::bytes LedgerBridge::GetPublicKey(int account, bool isChange, int index, bool display) + { + return GetPublicKey(ledger::Bip32Path(account, isChange, index), display); } void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos) { - std::vector signaturePaths; + std::vector signaturePaths; // transform wallet tx to ledger tx ledger::Tx tx = ToLedgerTx(wtxNew); @@ -58,16 +59,18 @@ namespace ledgerbridge throw "Ledger key was not found in wallet"; } - signaturePaths.push_back(ledger::bip32::GetBip32Path(ledgerKey.account, ledgerKey.index)); + signaturePaths.push_back(ledger::Bip32Path(ledgerKey.account, ledgerKey.isChange, ledgerKey.index)); } + // TODO GK - the transaction might not have any change auto changePath = signaturePaths[0]; ledger::Ledger ledger(TRANSPORT_TYPE); ledger.open(); + // TODO GK - the transaction might not have any change // sign tx - auto signTxResults = ledger.SignTransaction(tx, changePath, signaturePaths, ledgerUtxos); + auto signTxResults = ledger.SignTransaction(tx, true, changePath, signaturePaths, ledgerUtxos); // add signatures to tx and verify for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 93cedf7ac..846d7fcde 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -1,3 +1,4 @@ +#include "ledger/bip32.h" #include "ledger/tx.h" #include "ledger/bytes.h" @@ -22,8 +23,8 @@ namespace ledgerbridge LedgerBridge(); ~LedgerBridge(); - ledger::bytes GetPublicKey(const std::string& path, bool display); - ledger::bytes GetPublicKey(int account, int index, bool display); + ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); + ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos); private: ledger::Tx ToLedgerTx(const CTransaction& tx); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 304b04787..6603986af 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -232,7 +232,7 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const { // IsLedger, LedgerAccount and LedgerIndex are hidden, we display the info here // (however, they still need to be above due to EditAddressDialog data mapping) - std::string path = ledger::bip32::GetBip32Path(rec->ledgerAccount, rec->ledgerIndex); + std::string path = ledger::Bip32Path(rec->ledgerAccount, false, rec->ledgerIndex).ToString(); return isLedger ? QString::fromStdString(path) : "-"; } } @@ -429,7 +429,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con try { ledgerbridge::LedgerBridge ledgerBridge; - pubKey = ledgerBridge.GetPublicKey(account, index, true); + pubKey = ledgerBridge.GetPublicKey(account, false, index, true); } catch (const ledger::Error& e) { editStatus = LEDGER_ERROR; ledgerError = e; @@ -437,7 +437,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } CPubKey cpubkey(pubKey); - CLedgerKey ledgerKey(cpubkey, account, index); + CLedgerKey ledgerKey(cpubkey, account, false, index); wallet->AddLedgerKey(ledgerKey); strAddress = CBitcoinAddress(cpubkey.GetID()).ToString(); } else { diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index ba0ba2c95..37f422cad 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -224,10 +224,12 @@ void EditAddressDialog::accept() void EditAddressDialog::updateLedgerPathLabel() { - std::string path = ledger::bip32::GetBip32Path( + auto path = (new ledger::Bip32Path( ui->ledgerAccountEdit->text().toStdString(), + false, ui->ledgerIndexEdit->text().toStdString() - ); + ))->ToString(); + ui->ledgerPathLabel->setText(tr("Ledger path: %1").arg(QString::fromStdString(path))); } diff --git a/wallet/rpcdump.cpp b/wallet/rpcdump.cpp index dd1470e34..b5c28919a 100644 --- a/wallet/rpcdump.cpp +++ b/wallet/rpcdump.cpp @@ -548,14 +548,14 @@ std::pair ImportBackupWallet(const std::string& Src, std::string& Pa succeessfullyAddedOutOfTotal.first++; } } else if (backupWallet.GetLedgerKey(allKeyIDs[i], ledgerKey)) { - // all Ledger keys should currently be in the address book if (foundKeyInAddressBook) { bool addSucceeded = _AddLedgerKeyToLocalWallet(ledgerKey, it->second.name, true); if (addSucceeded) succeessfullyAddedOutOfTotal.first++; } else { - // TODO GK - check after change outputs are implemented - throw std::runtime_error("Ledger key not found in address book."); + bool addSucceeded = _AddLedgerKeyToLocalWallet(ledgerKey, "", importReserveToAddressBook); + if (addSucceeded) + succeessfullyAddedOutOfTotal.first++; } } } diff --git a/wallet/walletdb.h b/wallet/walletdb.h index 62a502372..ff23a2dad 100644 --- a/wallet/walletdb.h +++ b/wallet/walletdb.h @@ -43,14 +43,15 @@ class CLedgerKey public: CPubKey vchPubKey; uint32_t account; + bool isChange; uint32_t index; CLedgerKey() {} - CLedgerKey(const CPubKey& vchPubKeyIn, uint32_t accountIn, uint32_t indexIn) : - vchPubKey(vchPubKeyIn), account(accountIn), index(indexIn) {}; + CLedgerKey(const CPubKey& vchPubKeyIn, uint32_t accountIn, bool isChange, uint32_t indexIn) : + vchPubKey(vchPubKeyIn), account(accountIn), isChange(isChange), index(indexIn) {}; - IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(account); READWRITE(index);) + IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(account); READWRITE(isChange); READWRITE(index);) }; /** Access to the wallet database (wallet.dat) */ From 5f06a10ac6a23d6d6f254e1b221e079b7c30527d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 3 Apr 2023 20:43:49 +0200 Subject: [PATCH 047/129] Refactor `Bip32Path` to support account paths --- wallet/ledger/bip32.cpp | 59 ++++++++++++++++++++++++++++------------- wallet/ledger/bip32.h | 13 +++++---- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index 31225760c..d8dc7a092 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -21,7 +21,7 @@ namespace ledger } Bip32Path::Bip32Path(const std::string &keyPathStr) { - std::vector components; + std::vector _components; std::stringstream ss(keyPathStr); std::string item; bool first = true; @@ -57,61 +57,72 @@ namespace ledger } // utils::AppendUint32(keypath, std::stoul(item) | path); - components.push_back(std::stoul(item) | path); + _components.push_back(std::stoul(item) | path); first = false; } - if (components.size() != 5) + if (_components.size() != 3 || _components.size() != 5) { throw std::runtime_error("Invalid keypath size"); } - if (components[0] != Harden(BIP32_PURPOSE)) + if (_components[0] != Harden(BIP32_PURPOSE)) { throw std::runtime_error("Invalid keypath purpose"); } - if (components[1] != Harden(BIP32_COIN_TYPE)) + if (_components[1] != Harden(BIP32_COIN_TYPE)) { throw std::runtime_error("Invalid keypath coin type"); } - if (!IsHardened(components[2])) + if (!IsHardened(_components[2])) { throw std::runtime_error("Invalid keypath account"); } - if (IsHardened(components[3])) + if (_components.size() == 5 && IsHardened(_components[3])) { throw std::runtime_error("Invalid keypath change"); } - if (IsHardened(components[4])) + if (_components.size() == 5 && IsHardened(_components[4])) { throw std::runtime_error("Invalid keypath index"); } - - account = Unharden(components[2]); - isChange = components[3] == 1; - index = components[4]; + + components.push_back(BIP32_PURPOSE); + components.push_back(BIP32_COIN_TYPE); + components.push_back(_components[ACCOUNT_INDEX]); + components.push_back(_components[CHANGE_INDEX]); + components.push_back(_components[ADDRESS_INDEX_INDEX]); + } + + Bip32Path::Bip32Path(uint32_t account) { + components.push_back(BIP32_PURPOSE); + components.push_back(BIP32_COIN_TYPE); + components.push_back(account); } Bip32Path::Bip32Path(const std::string &account, bool isChange, const std::string &index) : Bip32Path(std::stoul(account), isChange, std::stoul(index)) {}; Bip32Path::Bip32Path(uint32_t account, bool isChange, uint32_t index) - : purpose(BIP32_PURPOSE), coinType(BIP32_COIN_TYPE), account(account), isChange(isChange), index(index) {}; + : Bip32Path(account) { + components.push_back(isChange ? 1 : 0); + components.push_back(index); + }; bytes Bip32Path::Serialize() const { bytes serializedKeyPath; - utils::AppendUint32(serializedKeyPath, Harden(purpose)); - utils::AppendUint32(serializedKeyPath, Harden(coinType)); - utils::AppendUint32(serializedKeyPath, Harden(account)); - utils::AppendUint32(serializedKeyPath, isChange ? 1 : 0); - utils::AppendUint32(serializedKeyPath, index); + utils::AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); + utils::AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); + utils::AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); + utils::AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); + utils::AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); return serializedKeyPath; } @@ -119,7 +130,17 @@ namespace ledger std::string Bip32Path::ToString() const { std::stringstream ss; - ss << "m/" << purpose << "'/" << coinType << "'/" << account << "'/" << (isChange ? 1 : 0) << "/" << index; + + ss << "m/" << components[PURPOSE_INDEX] << "'/" << components[COIN_TYPE_INDEX] << "'/" << components[ACCOUNT_INDEX]; + + if (components.size() > 3) { + ss << "/" << components[CHANGE_INDEX]; + } + + if (components.size() > 4) { + ss << "/" << components[ADDRESS_INDEX_INDEX]; + } + return ss.str(); } } // namespace ledger diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 0db7ccf56..74de40ea8 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -12,10 +12,17 @@ namespace ledger class Bip32Path { public: + const int PURPOSE_INDEX = 0; + const int COIN_TYPE_INDEX = 1; + const int ACCOUNT_INDEX = 2; + const int CHANGE_INDEX = 3; + const int ADDRESS_INDEX_INDEX = 4; + const int BIP32_PURPOSE = 44; const int BIP32_COIN_TYPE = 146; Bip32Path(const std::string &keyPath); + Bip32Path(uint32_t account); Bip32Path(const std::string &account, bool isChange, const std::string &index); Bip32Path(uint32_t account, bool isChange, uint32_t index); @@ -27,11 +34,7 @@ namespace ledger std::string ToString() const; private: - uint32_t purpose; - uint32_t coinType; - uint32_t account; - bool isChange; - uint32_t index; + std::vector components; }; } From 3fb5e36074a4ac09017eca4294c3a29204906a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 3 Apr 2023 20:47:17 +0200 Subject: [PATCH 048/129] Import Ledger change keys and store account pub key id --- wallet/ledgerBridge.cpp | 5 +++++ wallet/ledgerBridge.h | 1 + wallet/qt/addresstablemodel.cpp | 22 ++++++++++++++++------ wallet/walletdb.h | 7 ++++--- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 6b47a0ebe..2019c6b0b 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -35,6 +35,11 @@ namespace ledgerbridge return GetPublicKey(ledger::Bip32Path(account, isChange, index), display); } + ledger::bytes LedgerBridge::GetAccountPublicKey(int account, bool display) + { + return GetPublicKey(ledger::Bip32Path(account), display); + } + void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos) { std::vector signaturePaths; diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 846d7fcde..16febbbff 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -25,6 +25,7 @@ namespace ledgerbridge ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); + ledger::bytes GetAccountPublicKey(int account, bool display); void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos); private: ledger::Tx ToLedgerTx(const CTransaction& tx); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 6603986af..879a4fec7 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -425,21 +425,31 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con return QString(); } - ledger::bytes pubKey; + ledger::bytes accountPubKeyBytes; + ledger::bytes paymentPubKeyBytes; + ledger::bytes changePubKeyBytes; try { ledgerbridge::LedgerBridge ledgerBridge; - pubKey = ledgerBridge.GetPublicKey(account, false, index, true); + accountPubKeyBytes = ledgerBridge.GetPublicKey(account, false); + paymentPubKeyBytes = ledgerBridge.GetPublicKey(account, false, index, true); + changePubKeyBytes = ledgerBridge.GetPublicKey(account, true, index, false); } catch (const ledger::Error& e) { editStatus = LEDGER_ERROR; ledgerError = e; return QString(); } - CPubKey cpubkey(pubKey); - CLedgerKey ledgerKey(cpubkey, account, false, index); - wallet->AddLedgerKey(ledgerKey); - strAddress = CBitcoinAddress(cpubkey.GetID()).ToString(); + CPubKey accountPubKey(accountPubKeyBytes); + + CPubKey paymentPubKey(paymentPubKeyBytes); + CLedgerKey paymentLedgerKey(paymentPubKey, accountPubKey.GetID(), account, false, index); + wallet->AddLedgerKey(paymentLedgerKey); + strAddress = CBitcoinAddress(paymentPubKey.GetID()).ToString(); + + CPubKey changePubKey(changePubKeyBytes); + CLedgerKey changeLedgerKey(changePubKey, accountPubKey.GetID(), account, true, index); + wallet->AddLedgerKey(changeLedgerKey); } else { return QString(); } diff --git a/wallet/walletdb.h b/wallet/walletdb.h index ff23a2dad..19c55495d 100644 --- a/wallet/walletdb.h +++ b/wallet/walletdb.h @@ -42,16 +42,17 @@ class CLedgerKey { public: CPubKey vchPubKey; + CKeyID accountPubKeyID; uint32_t account; bool isChange; uint32_t index; CLedgerKey() {} - CLedgerKey(const CPubKey& vchPubKeyIn, uint32_t accountIn, bool isChange, uint32_t indexIn) : - vchPubKey(vchPubKeyIn), account(accountIn), isChange(isChange), index(indexIn) {}; + CLedgerKey(const CPubKey& vchPubKeyIn, const CKeyID& accountPubKeyIDIn, uint32_t accountIn, bool isChangeIn, uint32_t indexIn) : + vchPubKey(vchPubKeyIn), accountPubKeyID(accountPubKeyIDIn), account(accountIn), isChange(isChangeIn), index(indexIn) {}; - IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(account); READWRITE(isChange); READWRITE(index);) + IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(accountPubKeyID); READWRITE(account); READWRITE(isChange); READWRITE(index);) }; /** Access to the wallet database (wallet.dat) */ From a8a23aa4bef9a2bcaee0f0acb0a2a51ea94d7217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 3 Apr 2023 20:56:55 +0200 Subject: [PATCH 049/129] Remove redundant whitespaces --- wallet/wallet.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index bd1e69906..2aaf76822 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2236,7 +2236,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorvout[coin.second].scriptPubKey}; } else { if (SignSignature(*this, *coin.first, wtxNew, nIn) != SignatureState::Verified) { @@ -2486,7 +2486,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector Date: Mon, 3 Apr 2023 21:00:01 +0200 Subject: [PATCH 050/129] Remove unused variable (`changeKeyID` in `CreateTransaction`) --- wallet/wallet.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 2aaf76822..4abd6e4ad 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2323,8 +2323,6 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0 || ntp1TokenChangeExists) { // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so @@ -2334,7 +2332,6 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(&coinControl->destChange)) { scriptChange.SetDestination(coinControl->destChange); - changeKeyID = boost::get(coinControl->destChange); } else if (wtxNew.fLedgerTx) { CTxDestination destChange; if (!ExtractDestination(txdb, ledgerInputKey.get(), destChange)) { @@ -2345,7 +2342,6 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(destChange); } // no coin control: send change to newly generated address else { @@ -2365,8 +2361,6 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector Date: Mon, 3 Apr 2023 21:01:46 +0200 Subject: [PATCH 051/129] Use Ledger change path in change outputs --- wallet/ledger/bip32.cpp | 9 +++++++++ wallet/ledger/bip32.h | 2 ++ wallet/ledgerBridge.cpp | 2 +- wallet/wallet.cpp | 15 ++++++++++++++- wallet/wallet.h | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index d8dc7a092..f9490caa4 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -20,6 +20,15 @@ namespace ledger return n == Harden(n); } + Bip32Path Bip32Path::ToChangePath() const + { + if (components.size() == 3) { + throw std::runtime_error("Account keypath cannot be converted to change path!"); + } + + return Bip32Path(components[ACCOUNT_INDEX], true, components[ADDRESS_INDEX_INDEX]); + } + Bip32Path::Bip32Path(const std::string &keyPathStr) { std::vector _components; std::stringstream ss(keyPathStr); diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 74de40ea8..339761650 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -30,6 +30,8 @@ namespace ledger uint32_t Unharden(uint32_t n) const; bool IsHardened(uint32_t n) const; + Bip32Path ToChangePath() const; + bytes Serialize() const; std::string ToString() const; diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 2019c6b0b..767206e8f 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -68,7 +68,7 @@ namespace ledgerbridge } // TODO GK - the transaction might not have any change - auto changePath = signaturePaths[0]; + auto changePath = signaturePaths[0].ToChangePath(); ledger::Ledger ledger(TRANSPORT_TYPE); ledger.open(); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 4abd6e4ad..6713949d1 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2341,7 +2341,20 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(destChange); + if (IsLedgerChangeKey(destinationKeyID)) { + scriptChange.SetDestination(destChange); + } else { + CLedgerKey changeKey; + if (!this->GetOtherLedgerKey(destinationKeyID, changeKey, false)) { + NLog.write(b_sev::err, "Ledger change key not found!"); + CreateErrorMsg(errorMsg, "Ledger change key not found!"); + return false; + } + + scriptChange.SetDestination(changeKey.vchPubKey.GetID()); + } } // no coin control: send change to newly generated address else { diff --git a/wallet/wallet.h b/wallet/wallet.h index 4571327de..aae8871ab 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -492,6 +492,46 @@ class CWallet : public CCryptoKeyStore } return false; } + + bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const + { + std::cout << "Address: " << address.GetHex() << std::endl; + { + LOCK(cs_LedgerKeyStore); + std::map ::const_iterator mi = ledgerKeys.find(address); + if (mi != ledgerKeys.end()) + { + auto key = (*mi).second; + if (key.isChange != isChange) { + throw std::runtime_error("Function called with wrong isChange parameter"); + } + + auto it = std::find_if(ledgerKeys.begin(), ledgerKeys.end(), + [&key](const std::pair& entry) { + auto candidateKey = entry.second; + return candidateKey.isChange != key.isChange + && candidateKey.accountPubKeyID == key.accountPubKeyID + && candidateKey.index == key.index; + }); + + if (it != ledgerKeys.end()) { + ledgerKeyOut = it->second; + return true; + } + } + } + return false; + } + + bool IsLedgerChangeKey(const CKeyID &address) + { + CLedgerKey ledgerKey; + if (GetLedgerKey(address, ledgerKey)) + { + return ledgerKey.isChange; + } + return false; + } }; /** A key allocated from the key pool. */ From 534e4bd6ef7921b39395db367d4894063722ff74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 3 Apr 2023 21:04:12 +0200 Subject: [PATCH 052/129] Include Ledger change keys in available coins --- wallet/wallet.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 6713949d1..b2809bef3 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1447,8 +1447,31 @@ void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fO CTxDestination destination; if (ExtractDestination(txdb, scriptPubKey, destination)) { const auto entry = this->mapAddressBook.get(destination); - if (!entry || !entry.is_initialized() || entry->name != strFromAccount) + if (!entry || !entry.is_initialized() || entry->name != strFromAccount) { + // We only store Ledger payment keys in the address book. + // That's why if the current coin is a Ledger change key + // (i.e. we have its UTxOs but the key is not in the address book) + // we have to also check if the corresponding payment key + // for the given change key is strFromAccount. + if (mine != ISMINE_LEDGER || !fIncludeLedgerCoins) { continue; + } + + CKeyID changeLedgerKeyID; + ((CBitcoinAddress)destination).GetKeyID(changeLedgerKeyID); + + CLedgerKey ledgerPaymentKey; + if (!this->GetOtherLedgerKey(changeLedgerKeyID, ledgerPaymentKey, true)) { + continue; + } + + const auto ledgerPaymentKeyEntry = this->mapAddressBook.get(ledgerPaymentKey.vchPubKey.GetID()); + if (!ledgerPaymentKeyEntry + || !ledgerPaymentKeyEntry.is_initialized() + || ledgerPaymentKeyEntry->name != strFromAccount) { + continue; + } + } } } // bool fIsValid = From f9d32590c6ab86880f2c6704e343f47bbcc47013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 3 Apr 2023 21:08:24 +0200 Subject: [PATCH 053/129] Fix edit address dialog crashing after `Bip32Path` refactors --- wallet/qt/editaddressdialog.cpp | 19 +++++++++++++------ wallet/qt/editaddressdialog.h | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 37f422cad..f4364e6b5 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -64,9 +64,10 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); // Ledger submenu collapsed by default - ui->ledgerWidget->setVisible(false); - ui->ledgerPathLabel->setVisible(false); - ui->ledgerInfoLabel->setVisible(false); + ledgerItemsVisible = false; + ui->ledgerWidget->setVisible(ledgerItemsVisible); + ui->ledgerPathLabel->setVisible(ledgerItemsVisible); + ui->ledgerInfoLabel->setVisible(ledgerItemsVisible); // Ledger account and index defaults and validators ui->ledgerAccountEdit->setText("0"); @@ -224,6 +225,10 @@ void EditAddressDialog::accept() void EditAddressDialog::updateLedgerPathLabel() { + if (!ledgerItemsVisible) { + return; + } + auto path = (new ledger::Bip32Path( ui->ledgerAccountEdit->text().toStdString(), false, @@ -240,9 +245,11 @@ void EditAddressDialog::setAddress(QString addressIn) void EditAddressDialog::on_ledgerCheckBox_toggled(bool checked) { - ui->ledgerWidget->setVisible(checked); - ui->ledgerPathLabel->setVisible(checked); - ui->ledgerInfoLabel->setVisible(checked); + ledgerItemsVisible = checked; + + ui->ledgerWidget->setVisible(ledgerItemsVisible); + ui->ledgerPathLabel->setVisible(ledgerItemsVisible); + ui->ledgerInfoLabel->setVisible(ledgerItemsVisible); // reset dialog height after hiding ledger items setFixedHeight(sizeHint().height()); diff --git a/wallet/qt/editaddressdialog.h b/wallet/qt/editaddressdialog.h index 308d32c84..d99fbe387 100644 --- a/wallet/qt/editaddressdialog.h +++ b/wallet/qt/editaddressdialog.h @@ -63,6 +63,7 @@ public slots: QDataWidgetMapper *mapper; Mode mode; AddressTableModel *model; + bool ledgerItemsVisible; QString address; }; From 4466ae208e543c290fa36fa5e582baf7b158bd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 08:43:16 +0200 Subject: [PATCH 054/129] Remove Ledger specific code from `IsChange` Ledger change outputs can now be identified in the same way as other change outputs - i.e. change address is mine but is not in address book. --- wallet/wallet.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index b2809bef3..4e61a6ced 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -856,26 +856,6 @@ bool CWallet::IsChange(const ITxDB& txdb, const CTransaction& tx, const CTxOut& return true; } - // for Ledger consider any outputs sent to the same address as change - { - LOCK(cs_wallet); - - for (const auto& txin : tx.vin) { - map::const_iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) { - const CWalletTx& prev = (*mi).second; - auto previousOutput = prev.vout[txin.prevout.n]; - - CTxDestination outputAddress; - if (ExtractDestination(txdb, previousOutput.scriptPubKey, outputAddress) - && ::IsMine(*this, outputAddress) == ISMINE_LEDGER - && outputAddress == address) { - return true; - } - } - } - } - return false; } From 035edd999f7fa832dd3c22b3b8934d8c981e1b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 10:57:01 +0200 Subject: [PATCH 055/129] Pass `hasChange` to ledger bridge signTx function --- wallet/ledgerBridge.cpp | 14 +++++++------- wallet/ledgerBridge.h | 2 +- wallet/wallet.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 767206e8f..31960ff8b 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -1,6 +1,4 @@ -#include "ledger/bip32.h" #include "ledger/ledger.h" -#include "ledger/tx.h" #include "ledger/utils.h" #include "ledgerBridge.h" #include "key.h" @@ -40,12 +38,12 @@ namespace ledgerbridge return GetPublicKey(ledger::Bip32Path(account), display); } - void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos) + void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos, bool hasChange) { std::vector signaturePaths; // transform wallet tx to ledger tx - ledger::Tx tx = ToLedgerTx(wtxNew); + ledger::Tx tx = ToLedgerTx(wtxNew); // transform UTxOs and build signature paths std::vector ledgerUtxos; @@ -67,15 +65,17 @@ namespace ledgerbridge signaturePaths.push_back(ledger::Bip32Path(ledgerKey.account, ledgerKey.isChange, ledgerKey.index)); } - // TODO GK - the transaction might not have any change + // We only support transaction from a single Ledger address for now so all the signature paths + // should be the same. Even if that was to change this would still be a valid approach + // to determining the change path. If users would like to use a different change path + // they could leverage the coin control feature. auto changePath = signaturePaths[0].ToChangePath(); ledger::Ledger ledger(TRANSPORT_TYPE); ledger.open(); - // TODO GK - the transaction might not have any change // sign tx - auto signTxResults = ledger.SignTransaction(tx, true, changePath, signaturePaths, ledgerUtxos); + auto signTxResults = ledger.SignTransaction(tx, hasChange, changePath, signaturePaths, ledgerUtxos); // add signatures to tx and verify for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 16febbbff..61fca34e0 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -26,7 +26,7 @@ namespace ledgerbridge ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); ledger::bytes GetAccountPublicKey(int account, bool display); - void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos); + void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos, bool hasChange); private: ledger::Tx ToLedgerTx(const CTransaction& tx); }; diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 4e61a6ced..51158ceee 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2499,7 +2499,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0); } catch (const ledger::Error e) { CreateErrorMsg(errorMsg, "Error while signing Ledger transaction: " + ledger::error_message(e)); return false; From d51f4258c7c6d98af32da00bc8b1d9a28b4ac771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 10:57:40 +0200 Subject: [PATCH 056/129] Fix combining payment and change address inputs --- wallet/wallet.cpp | 69 ++++++++++++++++++++++++++++++++--------------- wallet/wallet.h | 7 +++-- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 51158ceee..07b8a70a5 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1439,6 +1439,10 @@ void CWallet::AvailableCoins(const ITxDB& txdb, vector& vCoins, bool fO CKeyID changeLedgerKeyID; ((CBitcoinAddress)destination).GetKeyID(changeLedgerKeyID); + // If the key is not a change key, we can skip it since it's entry name didn't match strFromAccount. + if (!this->IsLedgerChangeKey(changeLedgerKeyID)) { + continue; + } CLedgerKey ledgerPaymentKey; if (!this->GetOtherLedgerKey(changeLedgerKeyID, ledgerPaymentKey, true)) { @@ -2209,29 +2213,61 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vectorGetDepthInMainChain(txdb, bestBlockHash); } + // check if we have some Ledger inputs and validate them auto hasNonLedgerInputKeys = false; - boost::optional ledgerInputKey; + boost::optional ledgerInputDestination; for (PAIRTYPE(const CWalletTx*, unsigned int) pcoin : setCoins) { - if (!IsMineCheck(::IsMine(*this, pcoin.first->vout[pcoin.second].scriptPubKey), ISMINE_LEDGER)) { + auto coinScriptPubKey = pcoin.first->vout[pcoin.second].scriptPubKey; + + if (!IsMineCheck(::IsMine(*this, coinScriptPubKey), ISMINE_LEDGER)) { hasNonLedgerInputKeys = true; continue; } - if (!ledgerInputKey) { - ledgerInputKey = pcoin.first->vout[pcoin.second].scriptPubKey; + // Ledger coins must belong to a single address index so we need to store the first + // encountered address. We allow combining payment and change addresses from the same + // address index though. + if (!ledgerInputDestination) { + CTxDestination destination; + if (!ExtractDestination(txdb, coinScriptPubKey, destination)) { + NLog.write(b_sev::err, "Failed to extract Ledger coin destination."); + CreateErrorMsg(errorMsg, "Failed to extract Ledger coin destination."); + return false; + } + ledgerInputDestination = destination; continue; } - if (ledgerInputKey != pcoin.first->vout[pcoin.second].scriptPubKey) { - NLog.write(b_sev::err, - "Ledger transactions can only contain inputs belonging to one address."); - CreateErrorMsg(errorMsg, - "Ledger transactions can only contain inputs belonging to one address."); + CTxDestination coinDestination; + if (!ExtractDestination(txdb, coinScriptPubKey, coinDestination)) { + NLog.write(b_sev::err, "Failed to extract coin destination."); + CreateErrorMsg(errorMsg, "Failed to extract coin destination."); + return false; + } + + auto coinKeyID = boost::get(coinDestination); + auto ledgerInputKeyID = boost::get(ledgerInputDestination.get()); + + // If the coin belongs to the same address as the previous inputs, it's valid. + if (ledgerInputKeyID == coinKeyID) { + continue; + } + // Otherwise the coin might also belong to the "other" address so we need to check for that. + CLedgerKey otherLedgerKey; + if (!this->GetOtherLedgerKey(ledgerInputKeyID, otherLedgerKey, this->IsLedgerChangeKey(ledgerInputKeyID))) { + NLog.write(b_sev::err, "Other Ledger key found."); + CreateErrorMsg(errorMsg, "Other Ledger key found."); + return false; + } + // if the coin doesn't belong even to the other address, it's invalid + if (otherLedgerKey.vchPubKey.GetID() != coinKeyID) { + NLog.write(b_sev::err, "Ledger transactions can only contain inputs belonging to one address."); + CreateErrorMsg(errorMsg, "Ledger transactions can only contain inputs belonging to one address."); return false; } } - if (!!ledgerInputKey && !wtxNew.fLedgerTx) { + if (!!ledgerInputDestination && !wtxNew.fLedgerTx) { NLog.write(b_sev::err, "Non-Ledger transactions can not contain ledger inputs."); CreateErrorMsg(errorMsg, @@ -2336,18 +2372,9 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector(&coinControl->destChange)) { scriptChange.SetDestination(coinControl->destChange); } else if (wtxNew.fLedgerTx) { - CTxDestination destChange; - if (!ExtractDestination(txdb, ledgerInputKey.get(), destChange)) { - NLog.write(b_sev::err, - "Invalid Ledger destination."); - CreateErrorMsg(errorMsg, - "Invalid Ledger destination."); - return false; - } - - auto destinationKeyID = boost::get(destChange); + auto destinationKeyID = boost::get(ledgerInputDestination.get()); if (IsLedgerChangeKey(destinationKeyID)) { - scriptChange.SetDestination(destChange); + scriptChange.SetDestination(ledgerInputDestination.get()); } else { CLedgerKey changeKey; if (!this->GetOtherLedgerKey(destinationKeyID, changeKey, false)) { diff --git a/wallet/wallet.h b/wallet/wallet.h index aae8871ab..a21538277 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -493,9 +493,12 @@ class CWallet : public CCryptoKeyStore return false; } + /* + Returns the "other" Ledger key for a given key. "Other" means that for a payment key + we return the corresponding change key and vice versa. + */ bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const { - std::cout << "Address: " << address.GetHex() << std::endl; { LOCK(cs_LedgerKeyStore); std::map ::const_iterator mi = ledgerKeys.find(address); @@ -523,7 +526,7 @@ class CWallet : public CCryptoKeyStore return false; } - bool IsLedgerChangeKey(const CKeyID &address) + bool IsLedgerChangeKey(const CKeyID &address) const { CLedgerKey ledgerKey; if (GetLedgerKey(address, ledgerKey)) From 35215433ec63c975a31dbf59dd4efec1d049f604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 11:11:14 +0200 Subject: [PATCH 057/129] Remove outdated TODOs --- wallet/ledger/ledger.cpp | 1 - wallet/ledger/tx.cpp | 1 - wallet/wallet.cpp | 1 - wallet/wallet_ismine.h | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 70e286c59..790790de3 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -64,7 +64,6 @@ namespace ledger bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes &transactionData) { - // TODO GK - refactor transport to throw instead of returning error auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); auto err = std::get<0>(result); auto buffer = std::get<1>(result); diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index 1af2447b5..fba916ac4 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -130,7 +130,6 @@ Tx DeserializeTransaction(const bytes& transaction) { TrustedInput trustedInput; - // TODO GK - direct assignment ok? utils::AppendVector(trustedInput.serialized, serializedTrustedInput); auto offset = 0; diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 07b8a70a5..2482e8f0e 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -1555,7 +1555,6 @@ void CWallet::AvailableCoinsForStaking(const ITxDB& txdb, vector& vCoin if (IsSpent(pcoin->GetHash(), i, txdb, bestBlockHash)) continue; - // TODO GK: ledger? if (!(mine & ISMINE_SPENDABLE_STAKEABLE) && !(mine & ISMINE_SPENDABLE)) continue; diff --git a/wallet/wallet_ismine.h b/wallet/wallet_ismine.h index 3032edd90..58913bce7 100644 --- a/wallet/wallet_ismine.h +++ b/wallet/wallet_ismine.h @@ -32,7 +32,7 @@ enum isminetype : uint ISMINE_LEDGER = 32, ISMINE_SPENDABLE_ALL = ISMINE_SPENDABLE_DELEGATED | ISMINE_SPENDABLE | ISMINE_LEDGER, ISMINE_SPENDABLE_AVAILABLE = ISMINE_SPENDABLE | ISMINE_LEDGER, - ISMINE_SPENDABLE_STAKEABLE = ISMINE_SPENDABLE_DELEGATED | ISMINE_COLD, // TODO GK - Ledger?? + ISMINE_SPENDABLE_STAKEABLE = ISMINE_SPENDABLE_DELEGATED | ISMINE_COLD, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE | ISMINE_COLD | ISMINE_SPENDABLE_DELEGATED | ISMINE_LEDGER }; /** used for bitflags of isminetype */ From fecedcd072047ea43d62c1a6696dcec73031e47c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 12:13:46 +0200 Subject: [PATCH 058/129] Handle Ledger errors a bit more consistently --- wallet/ledger/comm.h | 3 +- wallet/ledger/error.cpp | 31 +++++-------- wallet/ledger/error.h | 38 ++++++++++++++-- wallet/ledger/hid_device.cpp | 8 ++-- wallet/ledger/hid_device.h | 2 +- wallet/ledger/ledger.cpp | 77 +++++---------------------------- wallet/ledger/speculos.cpp | 9 ++-- wallet/ledger/speculos.h | 2 +- wallet/ledger/transport.cpp | 12 ++--- wallet/ledger/transport.h | 4 +- wallet/ledger/tx.cpp | 7 +-- wallet/qt/addresstablemodel.cpp | 9 ++-- wallet/qt/addresstablemodel.h | 2 +- wallet/wallet.cpp | 4 +- 14 files changed, 87 insertions(+), 121 deletions(-) diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h index 96ce5958a..f715417b2 100644 --- a/wallet/ledger/comm.h +++ b/wallet/ledger/comm.h @@ -4,7 +4,6 @@ #include #include "bytes.h" -#include "error.h" namespace ledger { @@ -13,7 +12,7 @@ namespace ledger public: virtual ~Comm() = default; - virtual Error open() = 0; + virtual void open() = 0; virtual int send(const bytes &data) = 0; virtual int recv(bytes &rdata) = 0; virtual void close() = 0; diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index 7c9549bee..f32da6bd0 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -1,26 +1,17 @@ #include "error.h" namespace ledger -{ - std::string error_message(Error code) +{ + LedgerException::LedgerException(ErrorCode errorCodeIn) : errorCode(errorCodeIn) {} + + ErrorCode LedgerException::GetErrorCode() const { return errorCode; } + + const char *LedgerException::what() const noexcept { + return GetMessage().c_str(); + } + + std::string LedgerException::GetMessage() const { - switch (code) - { - case Error::SUCCESS: - return "Ok"; - case Error::DEVICE_NOT_FOUND: - return "Ledger Not Found"; - case Error::DEVICE_OPEN_FAIL: - return "Failed to open Ledger"; - case Error::DEVICE_DATA_SEND_FAIL: - return "Failed to send data to Ledger"; - case Error::DEVICE_DATA_RECV_FAIL: - return "Failed to receive data from Ledger"; - case Error::APDU_INVALID_CMD: - return "Invalid Ledger data"; - case Error::UNRECOGNIZED_ERROR: - default: - return "Unrecognized error"; - } + return LedgerException::GetMessage(errorCode); } } diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index 08b8d29c5..d9214d780 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -3,16 +3,48 @@ #include namespace ledger { - enum class Error { - SUCCESS = 0, + enum class ErrorCode { DEVICE_NOT_FOUND, DEVICE_OPEN_FAIL, DEVICE_DATA_SEND_FAIL, DEVICE_DATA_RECV_FAIL, APDU_INVALID_CMD, + INVALID_TRUSTED_INPUT, UNRECOGNIZED_ERROR = 999, }; - std::string error_message(Error code); + class LedgerException : public std::exception { + public: + static std::string GetMessage(ErrorCode errorCode) + { + switch (errorCode) + { + case ErrorCode::DEVICE_NOT_FOUND: + return "Ledger Not Found"; + case ErrorCode::DEVICE_OPEN_FAIL: + return "Failed to open Ledger"; + case ErrorCode::DEVICE_DATA_SEND_FAIL: + return "Failed to send data to Ledger"; + case ErrorCode::DEVICE_DATA_RECV_FAIL: + return "Failed to receive data from Ledger"; + case ErrorCode::APDU_INVALID_CMD: + return "Invalid Ledger data"; + case ErrorCode::INVALID_TRUSTED_INPUT: + return "Invalid trusted input"; + case ErrorCode::UNRECOGNIZED_ERROR: + default: + return "Unrecognized error"; + } + } + LedgerException(ErrorCode errorCodeIn); + ~LedgerException() noexcept override = default; + + ErrorCode GetErrorCode() const; + std::string GetMessage() const; + + const char *what() const noexcept override; + private: + ErrorCode errorCode; + }; } // namespace ledger diff --git a/wallet/ledger/hid_device.cpp b/wallet/ledger/hid_device.cpp index 4c6897283..3aa9bcab9 100644 --- a/wallet/ledger/hid_device.cpp +++ b/wallet/ledger/hid_device.cpp @@ -6,29 +6,27 @@ namespace ledger { - Error HID::open() + void HID::open() { if (!opened_) { auto devices = enumerate_devices(vendor_id_); if (devices.empty()) { - return Error::DEVICE_NOT_FOUND; + throw LedgerException(ErrorCode::DEVICE_NOT_FOUND); } path_ = devices.at(0); device_ = hid_open_path(path_.c_str()); if (!device_) { - return Error::DEVICE_OPEN_FAIL; + throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); } hid_set_nonblocking(device_, true); opened_ = true; } - - return Error::SUCCESS; } int HID::send(const bytes &data) diff --git a/wallet/ledger/hid_device.h b/wallet/ledger/hid_device.h index 52271f7bc..ca2093464 100644 --- a/wallet/ledger/hid_device.h +++ b/wallet/ledger/hid_device.h @@ -9,7 +9,7 @@ namespace ledger class HID final : public Comm { public: - Error open() override; + void open() override; int send(const bytes &data) override; int recv(bytes &rdata) override; void close() noexcept override; diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 790790de3..cfee97472 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -16,17 +16,7 @@ namespace ledger Ledger::~Ledger() { transport_->close(); } - void Ledger::open() - { - std::cout << "Opening Ledger connection." << std::endl; - auto openError = transport_->open(); - if (openError != ledger::Error::SUCCESS) - { - // TODO GK - what should we be throwing? (in the whole file) - throw openError; - } - std::cout << "Ledger connection opened." << std::endl; - } + void Ledger::open() { transport_->open(); } std::tuple Ledger::GetPublicKey(const Bip32Path path, bool confirm) { @@ -37,11 +27,7 @@ namespace ledger utils::AppendVector(payload, pathBytes); // 0x00 = P2_LEGACY (base58) - auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + auto buffer = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); auto offset = 1; auto pubKeyLen = (int)buffer[offset] * 16 + 1; @@ -57,20 +43,14 @@ namespace ledger offset += 32; if (offset != buffer.size()) - throw Error::UNRECOGNIZED_ERROR; + throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); return {pubKey, std::string(address.begin(), address.end()), chainCode}; } bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes &transactionData) { - auto result = transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; - - return buffer; + return transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); } bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) @@ -131,27 +111,15 @@ namespace ledger changePathData.push_back(serializedChangePath.size() / 4); utils::AppendVector(changePathData, serializedChangePath); - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, changePathData); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, changePathData); } else { - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, {0x00}); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, {0x00}); } p1 = 0x00; - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, utils::CreateVarint(tx.outputs.size())); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, utils::CreateVarint(tx.outputs.size())); for (auto i = 0; i < tx.outputs.size(); i++) { @@ -163,11 +131,7 @@ namespace ledger utils::AppendVector(outputData, utils::CreateVarint(output.script.size())); utils::AppendVector(outputData, output.script); - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, outputData); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, outputData); } } @@ -182,11 +146,7 @@ namespace ledger utils::AppendUint32(data, tx.time, true); utils::AppendVector(data, utils::CreateVarint(trustedInputs.size())); - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, data); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, data); p1 = 0x80; for (auto i = 0; i < trustedInputs.size(); i++) @@ -200,21 +160,13 @@ namespace ledger utils::AppendVector(_data, trustedInput.serialized); utils::AppendVector(_data, utils::CreateVarint(_script.size())); - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, _data); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, _data); bytes scriptData; utils::AppendVector(scriptData, _script); utils::AppendUint32(scriptData, 0xffffffff, true); - result = transport_->exchange(APDU::CLA, ins, p1, p2, scriptData); - err = std::get<0>(result); - buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; + transport_->exchange(APDU::CLA, ins, p1, p2, scriptData); } } @@ -258,12 +210,7 @@ namespace ledger utils::AppendUint32(data, tx.locktime); data.push_back(0x01); - auto result = transport_->exchange(APDU::CLA, ins, p1, p2, data); - auto err = std::get<0>(result); - auto buffer = std::get<1>(result); - if (err != Error::SUCCESS) - throw err; - + auto buffer = transport_->exchange(APDU::CLA, ins, p1, p2, data); if (buffer[0] & 0x01) { bytes data; diff --git a/wallet/ledger/speculos.cpp b/wallet/ledger/speculos.cpp index bf19927a6..02cd65de8 100644 --- a/wallet/ledger/speculos.cpp +++ b/wallet/ledger/speculos.cpp @@ -17,13 +17,13 @@ namespace ledger { - Error Speculos::open() + void Speculos::open() { if (!opened_) { auto _sockfd = socket(AF_INET, SOCK_STREAM, 0); if (_sockfd < 0) - return Error::DEVICE_OPEN_FAIL; + throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); auto server = "localhost"; auto port = "9999"; @@ -33,13 +33,11 @@ namespace ledger serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); serv_addr.sin_port = htons(9999); if (connect(_sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) - return Error::DEVICE_OPEN_FAIL; + throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); this->sockfd = _sockfd; opened_ = true; } - - return Error::SUCCESS; } int Speculos::send(const bytes &data) @@ -86,6 +84,7 @@ namespace ledger return opened_; } + // TODO GK - remove and check if other clean up is needed std::vector Speculos::enumerate_devices(unsigned short vendor_id) noexcept { std::vector devices; diff --git a/wallet/ledger/speculos.h b/wallet/ledger/speculos.h index 196206b03..4fc0e50a6 100644 --- a/wallet/ledger/speculos.h +++ b/wallet/ledger/speculos.h @@ -9,7 +9,7 @@ namespace ledger class Speculos final : public Comm { public: - Error open() override; + void open() override; int send(const bytes &data) override; int recv(bytes &rdata) override; void close() noexcept override; diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 2ab72ec64..c001b72b8 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -19,26 +19,26 @@ namespace ledger } } - Error Transport::open() + void Transport::open() { return comm_->open(); } - std::tuple Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) + bytes Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) { int length = this->send(cla, ins, p1, p2, cdata); if (length < 0) - return {Error::DEVICE_DATA_SEND_FAIL, {}}; + throw LedgerException(ErrorCode::DEVICE_DATA_SEND_FAIL); bytes buffer; int sw = this->recv(buffer); if (sw < 0) - return {Error::DEVICE_DATA_RECV_FAIL, {}}; + throw LedgerException(ErrorCode::DEVICE_DATA_RECV_FAIL); if (sw != 0x9000) - return {Error::APDU_INVALID_CMD, {}}; + throw LedgerException(ErrorCode::APDU_INVALID_CMD); - return {Error::SUCCESS, buffer}; + return buffer; } void Transport::close() noexcept diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index b45220f92..f5bba3c51 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -17,8 +17,8 @@ namespace ledger }; Transport(TransportType type); - Error open(); - std::tuple exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); + void open(); + bytes exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); void close() noexcept; private: diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index fba916ac4..c742f2630 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -1,3 +1,4 @@ +#include "error.h" #include "tx.h" #include "utils.h" @@ -136,12 +137,12 @@ Tx DeserializeTransaction(const bytes& transaction) auto trustedInputMagic = serializedTrustedInput[offset]; if (trustedInputMagic != 0x32) - throw "Invalid trusted input magic"; + throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); offset += 1; auto zeroByte = serializedTrustedInput[offset]; if (zeroByte != 0x00) - throw "Zero byte is not a zero byte"; + throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); offset += 1; trustedInput.random = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 2)); @@ -160,7 +161,7 @@ Tx DeserializeTransaction(const bytes& transaction) offset += 8; if (offset != serializedTrustedInput.size()) - throw "Leftover bytes in trusted input"; + throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); return trustedInput; } diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 879a4fec7..70e61903c 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -434,9 +434,9 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con accountPubKeyBytes = ledgerBridge.GetPublicKey(account, false); paymentPubKeyBytes = ledgerBridge.GetPublicKey(account, false, index, true); changePubKeyBytes = ledgerBridge.GetPublicKey(account, true, index, false); - } catch (const ledger::Error& e) { + } catch (const ledger::LedgerException& e) { editStatus = LEDGER_ERROR; - ledgerError = e; + ledgerError = e.GetErrorCode(); return QString(); } @@ -507,9 +507,8 @@ int AddressTableModel::lookupAddress(const QString& address) const std::string AddressTableModel::getLedgerErrorMessage() const { if (editStatus != LEDGER_ERROR) return ""; - if (ledgerError == ledger::Error::SUCCESS) - return "Unknown error"; - return ledger::error_message(ledgerError); + + return ledger::LedgerException::GetMessage(ledgerError); } void AddressTableModel::emitDataChanged(int idx) diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index 917fe98f6..d5a76ac34 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -93,7 +93,7 @@ class AddressTableModel : public QAbstractTableModel AddressTablePriv* priv; QStringList columns; EditStatus editStatus; - ledger::Error ledgerError; + ledger::ErrorCode ledgerError; /** Notify listeners that data changed. */ void emitDataChanged(int index); diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 2482e8f0e..c8475a335 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2526,8 +2526,8 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0); - } catch (const ledger::Error e) { - CreateErrorMsg(errorMsg, "Error while signing Ledger transaction: " + ledger::error_message(e)); + } catch (const ledger::LedgerException& e) { + CreateErrorMsg(errorMsg, "Error while signing Ledger transaction: " + e.GetMessage()); return false; } } From 7e24395bccb3cd59776b92fbada59aa2cdd8222e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 12:23:18 +0200 Subject: [PATCH 059/129] Minor refactors in Ledger comm interfaces --- wallet/ledger/comm.h | 4 +- wallet/ledger/hid_device.cpp | 112 +++++++++++++++++------------------ wallet/ledger/hid_device.h | 16 ++--- wallet/ledger/ledger.cpp | 28 ++++----- wallet/ledger/ledger.h | 2 +- wallet/ledger/speculos.cpp | 38 +++--------- wallet/ledger/speculos.h | 12 +--- wallet/ledger/transport.cpp | 26 ++++---- wallet/ledger/transport.h | 6 +- 9 files changed, 107 insertions(+), 137 deletions(-) diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h index f715417b2..e7bb7fd50 100644 --- a/wallet/ledger/comm.h +++ b/wallet/ledger/comm.h @@ -14,8 +14,8 @@ namespace ledger virtual void open() = 0; virtual int send(const bytes &data) = 0; - virtual int recv(bytes &rdata) = 0; + virtual int receive(bytes &rdata) = 0; virtual void close() = 0; - [[nodiscard]] virtual bool is_open() const = 0; + [[nodiscard]] virtual bool isOpen() const = 0; }; } // namespace ledger diff --git a/wallet/ledger/hid_device.cpp b/wallet/ledger/hid_device.cpp index 3aa9bcab9..b9ba62919 100644 --- a/wallet/ledger/hid_device.cpp +++ b/wallet/ledger/hid_device.cpp @@ -8,24 +8,24 @@ namespace ledger { void HID::open() { - if (!opened_) + if (!opened) { - auto devices = enumerate_devices(vendor_id_); + auto devices = enumerateDevices(vendorId); if (devices.empty()) { throw LedgerException(ErrorCode::DEVICE_NOT_FOUND); } - path_ = devices.at(0); - device_ = hid_open_path(path_.c_str()); - if (!device_) + path = devices.at(0); + device = hid_open_path(path.c_str()); + if (!device) { throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); } - hid_set_nonblocking(device_, true); + hid_set_nonblocking(device, true); - opened_ = true; + opened = true; } } @@ -34,116 +34,116 @@ namespace ledger if (data.empty()) return -1; - auto data_new = utils::IntToBytes(data.size(), 2); - data_new.insert(data_new.end(), data.begin(), data.end()); + auto dataNew = utils::IntToBytes(data.size(), 2); + dataNew.insert(dataNew.end(), data.begin(), data.end()); size_t offset = 0; - size_t seq_idx = 0; + size_t seqIdx = 0; size_t length = 0; - while (offset < data_new.size()) + while (offset < dataNew.size()) { // Header: channel (0x0101), tag (0x05), sequence index bytes header{0x01, 0x01, 0x05}; - auto seq_idx_bytes = utils::IntToBytes(seq_idx, 2); - header.insert(header.end(), seq_idx_bytes.begin(), seq_idx_bytes.end()); + auto seqIdxBytes = utils::IntToBytes(seqIdx, 2); + header.insert(header.end(), seqIdxBytes.begin(), seqIdxBytes.end()); bytes::iterator it; - if (data_new.size() - offset < 64 - header.size()) + if (dataNew.size() - offset < 64 - header.size()) { - it = data_new.end(); + it = dataNew.end(); } else { - it = data_new.begin() + offset + 64 - header.size(); + it = dataNew.begin() + offset + 64 - header.size(); } - bytes data_chunk{data_new.begin() + offset, it}; - data_chunk.insert(data_chunk.begin(), header.begin(), header.end()); - data_chunk.insert(data_chunk.begin(), 0x00); + bytes dataChunk{dataNew.begin() + offset, it}; + dataChunk.insert(dataChunk.begin(), header.begin(), header.end()); + dataChunk.insert(dataChunk.begin(), 0x00); - if (hid_write(device_, data_chunk.data(), data_chunk.size()) == -1) + if (hid_write(device, dataChunk.data(), dataChunk.size()) == -1) return -1; - length += data_chunk.size(); + length += dataChunk.size(); offset += 64 - header.size(); - seq_idx += 1; + seqIdx += 1; } return length; } - int HID::recv(bytes &rdata) + int HID::receive(bytes &rdata) { - int seq_idx = 0; + int seqIdx = 0; uint8_t buf[64]; - hid_set_nonblocking(device_, false); - if (hid_read_timeout(device_, buf, sizeof(buf), timeout_ms_) <= 0) + hid_set_nonblocking(device, false); + if (hid_read_timeout(device, buf, sizeof(buf), timeoutMs) <= 0) return -1; - hid_set_nonblocking(device_, true); + hid_set_nonblocking(device, true); - bytes data_chunk(buf, buf + sizeof(buf)); + bytes dataChunk(buf, buf + sizeof(buf)); - assert(data_chunk[0] == 0x01); - assert(data_chunk[1] == 0x01); - assert(data_chunk[2] == 0x05); + assert(dataChunk[0] == 0x01); + assert(dataChunk[1] == 0x01); + assert(dataChunk[2] == 0x05); - auto seq_idx_bytes = utils::IntToBytes(seq_idx, 2); - assert(seq_idx_bytes[0] == data_chunk[3]); - assert(seq_idx_bytes[1] == data_chunk[4]); + auto seqIdxBytes = utils::IntToBytes(seqIdx, 2); + assert(seqIdxBytes[0] == dataChunk[3]); + assert(seqIdxBytes[1] == dataChunk[4]); - auto data_len = utils::BytesToInt(bytes(data_chunk.begin() + 5, data_chunk.begin() + 7)); - bytes data(data_chunk.begin() + 7, data_chunk.end()); + auto dataLen = utils::BytesToInt(bytes(dataChunk.begin() + 5, dataChunk.begin() + 7)); + bytes data(dataChunk.begin() + 7, dataChunk.end()); - while (data.size() < data_len) + while (data.size() < dataLen) { - uint8_t read_bytes[64]; - if (hid_read_timeout(device_, read_bytes, sizeof(read_bytes), 1000) == -1) + uint8_t readBytes[64]; + if (hid_read_timeout(device, readBytes, sizeof(readBytes), 1000) == -1) return -1; - bytes tmp(read_bytes, read_bytes + sizeof(read_bytes)); + bytes tmp(readBytes, readBytes + sizeof(readBytes)); data.insert(data.end(), tmp.begin() + 5, tmp.end()); } - auto sw = utils::BytesToInt(bytes(data.begin() + data_len - 2, data.begin() + data_len)); - rdata = bytes(data.begin(), data.begin() + data_len - 2); + auto sw = utils::BytesToInt(bytes(data.begin() + dataLen - 2, data.begin() + dataLen)); + rdata = bytes(data.begin(), data.begin() + dataLen - 2); return sw; } void HID::close() noexcept { - if (opened_) + if (opened) { - hid_close(device_); - opened_ = false; + hid_close(device); + opened = false; } hid_exit(); } - bool HID::is_open() const + bool HID::isOpen() const { - return opened_; + return opened; } - std::vector HID::enumerate_devices(unsigned short vendor_id) noexcept + std::vector HID::enumerateDevices(unsigned short vendorId) noexcept { std::vector devices; - struct hid_device_info *devs, *cur_dev; + struct hid_device_info *devs, *curDev; - devs = hid_enumerate(vendor_id, 0x0); - cur_dev = devs; - while (cur_dev) + devs = hid_enumerate(vendorId, 0x0); + curDev = devs; + while (curDev) { - if (cur_dev->interface_number == 0 || + if (curDev->interface_number == 0 || // MacOS specific - cur_dev->usage_page == 0xffa0) + curDev->usage_page == 0xffa0) { - devices.emplace_back(cur_dev->path); + devices.emplace_back(curDev->path); } - cur_dev = cur_dev->next; + curDev = curDev->next; } hid_free_enumeration(devs); diff --git a/wallet/ledger/hid_device.h b/wallet/ledger/hid_device.h index ca2093464..c19c52ca7 100644 --- a/wallet/ledger/hid_device.h +++ b/wallet/ledger/hid_device.h @@ -11,17 +11,17 @@ namespace ledger public: void open() override; int send(const bytes &data) override; - int recv(bytes &rdata) override; + int receive(bytes &rdata) override; void close() noexcept override; - [[nodiscard]] bool is_open() const override; + [[nodiscard]] bool isOpen() const override; private: - static std::vector enumerate_devices(unsigned short vendor_id = 0x2c97) noexcept; + static std::vector enumerateDevices(unsigned short vendor_id = 0x2c97) noexcept; - hid_device *device_ = nullptr; - std::string path_ = {}; - bool opened_ = false; - const int timeout_ms_ = 60 * 1000; - unsigned short vendor_id_ = 0x2c97; // Ledger Vendor ID + hid_device *device = nullptr; + std::string path = {}; + bool opened = false; + const int timeoutMs = 60 * 1000; + unsigned short vendorId = 0x2c97; // Ledger Vendor ID }; } // namespace ledger diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index cfee97472..c17491577 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -12,11 +12,11 @@ namespace ledger { - Ledger::Ledger(Transport::TransportType transportType) { this->transport_ = std::unique_ptr(new Transport(transportType)); } + Ledger::Ledger(Transport::TransportType transportType) { this->transport = std::unique_ptr(new Transport(transportType)); } - Ledger::~Ledger() { transport_->close(); } + Ledger::~Ledger() { transport->close(); } - void Ledger::open() { transport_->open(); } + void Ledger::open() { transport->open(); } std::tuple Ledger::GetPublicKey(const Bip32Path path, bool confirm) { @@ -27,7 +27,7 @@ namespace ledger utils::AppendVector(payload, pathBytes); // 0x00 = P2_LEGACY (base58) - auto buffer = transport_->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); + auto buffer = transport->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); auto offset = 1; auto pubKeyLen = (int)buffer[offset] * 16 + 1; @@ -50,7 +50,7 @@ namespace ledger bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes &transactionData) { - return transport_->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); + return transport->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); } bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) @@ -111,15 +111,15 @@ namespace ledger changePathData.push_back(serializedChangePath.size() / 4); utils::AppendVector(changePathData, serializedChangePath); - transport_->exchange(APDU::CLA, ins, p1, p2, changePathData); + transport->exchange(APDU::CLA, ins, p1, p2, changePathData); } else { - transport_->exchange(APDU::CLA, ins, p1, p2, {0x00}); + transport->exchange(APDU::CLA, ins, p1, p2, {0x00}); } p1 = 0x00; - transport_->exchange(APDU::CLA, ins, p1, p2, utils::CreateVarint(tx.outputs.size())); + transport->exchange(APDU::CLA, ins, p1, p2, utils::CreateVarint(tx.outputs.size())); for (auto i = 0; i < tx.outputs.size(); i++) { @@ -131,7 +131,7 @@ namespace ledger utils::AppendVector(outputData, utils::CreateVarint(output.script.size())); utils::AppendVector(outputData, output.script); - transport_->exchange(APDU::CLA, ins, p1, p2, outputData); + transport->exchange(APDU::CLA, ins, p1, p2, outputData); } } @@ -146,7 +146,7 @@ namespace ledger utils::AppendUint32(data, tx.time, true); utils::AppendVector(data, utils::CreateVarint(trustedInputs.size())); - transport_->exchange(APDU::CLA, ins, p1, p2, data); + transport->exchange(APDU::CLA, ins, p1, p2, data); p1 = 0x80; for (auto i = 0; i < trustedInputs.size(); i++) @@ -160,13 +160,13 @@ namespace ledger utils::AppendVector(_data, trustedInput.serialized); utils::AppendVector(_data, utils::CreateVarint(_script.size())); - transport_->exchange(APDU::CLA, ins, p1, p2, _data); + transport->exchange(APDU::CLA, ins, p1, p2, _data); bytes scriptData; utils::AppendVector(scriptData, _script); utils::AppendUint32(scriptData, 0xffffffff, true); - transport_->exchange(APDU::CLA, ins, p1, p2, scriptData); + transport->exchange(APDU::CLA, ins, p1, p2, scriptData); } } @@ -210,7 +210,7 @@ namespace ledger utils::AppendUint32(data, tx.locktime); data.push_back(0x01); - auto buffer = transport_->exchange(APDU::CLA, ins, p1, p2, data); + auto buffer = transport->exchange(APDU::CLA, ins, p1, p2, data); if (buffer[0] & 0x01) { bytes data; @@ -227,5 +227,5 @@ namespace ledger return signatures; } - void Ledger::close() { return transport_->close(); } + void Ledger::close() { return transport->close(); } } // namespace ledger diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 375ddc3ca..54fca28ff 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -33,7 +33,7 @@ namespace ledger void close(); private: - std::unique_ptr transport_; + std::unique_ptr transport; bytes ProcessScriptBlocks(const bytes &script, uint32_t sequence); bytes GetTrustedInput(const Tx &utxoTx, uint32_t indexLookup); diff --git a/wallet/ledger/speculos.cpp b/wallet/ledger/speculos.cpp index 02cd65de8..84112f9e2 100644 --- a/wallet/ledger/speculos.cpp +++ b/wallet/ledger/speculos.cpp @@ -19,7 +19,7 @@ namespace ledger { void Speculos::open() { - if (!opened_) + if (!opened) { auto _sockfd = socket(AF_INET, SOCK_STREAM, 0); if (_sockfd < 0) @@ -36,7 +36,7 @@ namespace ledger throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); this->sockfd = _sockfd; - opened_ = true; + opened = true; } } @@ -51,7 +51,7 @@ namespace ledger return result; } - int Speculos::recv(bytes &rdata) + int Speculos::receive(bytes &rdata) { uint8_t lengthB[4]; if (read(sockfd, lengthB, sizeof(lengthB)) == -1) @@ -72,39 +72,15 @@ namespace ledger void Speculos::close() noexcept { - if (opened_) + if (opened) { ::close(sockfd); - opened_ = false; + opened = false; } } - bool Speculos::is_open() const + bool Speculos::isOpen() const { - return opened_; - } - - // TODO GK - remove and check if other clean up is needed - std::vector Speculos::enumerate_devices(unsigned short vendor_id) noexcept - { - std::vector devices; - - struct hid_device_info *devs, *cur_dev; - - devs = hid_enumerate(vendor_id, 0x0); - cur_dev = devs; - while (cur_dev) - { - if (cur_dev->interface_number == 0 || - // MacOS specific - cur_dev->usage_page == 0xffa0) - { - devices.emplace_back(cur_dev->path); - } - cur_dev = cur_dev->next; - } - hid_free_enumeration(devs); - - return devices; + return opened; } } // namespace ledger diff --git a/wallet/ledger/speculos.h b/wallet/ledger/speculos.h index 4fc0e50a6..9874d44c0 100644 --- a/wallet/ledger/speculos.h +++ b/wallet/ledger/speculos.h @@ -11,18 +11,12 @@ namespace ledger public: void open() override; int send(const bytes &data) override; - int recv(bytes &rdata) override; + int receive(bytes &rdata) override; void close() noexcept override; - [[nodiscard]] bool is_open() const override; + [[nodiscard]] bool isOpen() const override; private: - static std::vector enumerate_devices(unsigned short vendor_id = 0x2c97) noexcept; - int sockfd = -1; - hid_device *device_ = nullptr; - std::string path_ = {}; - bool opened_ = false; - const int timeout_ms_ = 60 * 1000; - unsigned short vendor_id_ = 0x2c97; // Ledger Vendor ID + bool opened = false; }; } // namespace ledger diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index c001b72b8..3cb0d373f 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -11,17 +11,17 @@ namespace ledger switch (type) { case TransportType::HID: - comm_ = std::unique_ptr(new HID()); - break; + comm = std::unique_ptr(new HID()); + break; case TransportType::SPECULOS: - comm_ = std::unique_ptr(new Speculos()); + comm = std::unique_ptr(new Speculos()); break; } } void Transport::open() { - return comm_->open(); + return comm->open(); } bytes Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) @@ -31,7 +31,7 @@ namespace ledger throw LedgerException(ErrorCode::DEVICE_DATA_SEND_FAIL); bytes buffer; - int sw = this->recv(buffer); + int sw = this->receive(buffer); if (sw < 0) throw LedgerException(ErrorCode::DEVICE_DATA_RECV_FAIL); @@ -43,29 +43,29 @@ namespace ledger void Transport::close() noexcept { - return comm_->close(); + return comm->close(); } int Transport::send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) { - if (!comm_->is_open()) + if (!comm->isOpen()) return -1; - auto header = apdu_header(cla, ins, p1, p2, cdata.size()); + auto header = apduHeader(cla, ins, p1, p2, cdata.size()); header.insert(header.end(), cdata.begin(), cdata.end()); - return comm_->send(header); + return comm->send(header); } - int Transport::recv(bytes &rdata) + int Transport::receive(bytes &rdata) { - if (!comm_->is_open()) + if (!comm->isOpen()) return -1; - return comm_->recv(rdata); + return comm->receive(rdata); } - bytes Transport::apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) + bytes Transport::apduHeader(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) { return bytes{cla, ins, p1, p2, lc}; } diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index f5bba3c51..e9b7d862d 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -23,9 +23,9 @@ namespace ledger private: int send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); - int recv(bytes &rdata); - static bytes apdu_header(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc); + int receive(bytes &rdata); + static bytes apduHeader(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc); - std::unique_ptr comm_; + std::unique_ptr comm; }; } // namespace ledger From 3238537207cb6301f1eb869ee555b3047ff68f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 12:26:46 +0200 Subject: [PATCH 060/129] Rename `hid_device` files to `hid` --- wallet/ledger/{hid_device.cpp => hid.cpp} | 2 +- wallet/ledger/{hid_device.h => hid.h} | 0 wallet/ledger/transport.cpp | 2 +- wallet/wallet.pri | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename wallet/ledger/{hid_device.cpp => hid.cpp} (99%) rename wallet/ledger/{hid_device.h => hid.h} (100%) diff --git a/wallet/ledger/hid_device.cpp b/wallet/ledger/hid.cpp similarity index 99% rename from wallet/ledger/hid_device.cpp rename to wallet/ledger/hid.cpp index b9ba62919..240de1ec5 100644 --- a/wallet/ledger/hid_device.cpp +++ b/wallet/ledger/hid.cpp @@ -1,5 +1,5 @@ #include "error.h" -#include "hid_device.h" +#include "hid.h" #include "utils.h" #include diff --git a/wallet/ledger/hid_device.h b/wallet/ledger/hid.h similarity index 100% rename from wallet/ledger/hid_device.h rename to wallet/ledger/hid.h diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 3cb0d373f..5ea91360f 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -1,5 +1,5 @@ #include "error.h" -#include "hid_device.h" +#include "hid.h" #include "speculos.h" #include "transport.h" #include "utils.h" diff --git a/wallet/wallet.pri b/wallet/wallet.pri index 28e948e4c..f9123376e 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -7,7 +7,7 @@ HEADERS += qt/bitcoingui.h \ ledger/comm.h \ ledger/error.h \ ledger/hash.h \ - ledger/hid_device.h \ + ledger/hid.h \ ledger/ledger.h \ ledger/speculos.h \ ledger/transport.h \ @@ -216,7 +216,7 @@ HEADERS += \ SOURCES += qt/bitcoin.cpp \ ledger/bip32.cpp \ ledger/error.cpp \ - ledger/hid_device.cpp \ + ledger/hid.cpp \ ledger/ledger.cpp \ ledger/speculos.cpp \ ledger/transport.cpp \ From 250773ace1add9bbd3811415c01d15e58bda39de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 4 Apr 2023 13:29:07 +0200 Subject: [PATCH 061/129] Fix Ledger `hid_exit` crash and make HID default transport --- wallet/ledgerBridge.cpp | 21 +++++++++++---------- wallet/ledgerBridge.h | 2 ++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 31960ff8b..bf8b37ef1 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -10,24 +10,27 @@ namespace ledgerbridge { - const ledger::Transport::TransportType TRANSPORT_TYPE = ledger::Transport::TransportType::SPECULOS; + const ledger::Transport::TransportType TRANSPORT_TYPE = ledger::Transport::TransportType::HID; LedgerBridge::LedgerBridge() {} LedgerBridge::~LedgerBridge() {} - ledger::bytes LedgerBridge::GetPublicKey(const ledger::Bip32Path path, bool display) + ledger::bytes LedgerBridge::GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, bool display) { - ledger::Ledger ledger(TRANSPORT_TYPE); - ledger.open(); - auto result = ledger.GetPublicKey(path, display); - ledger.close(); - return ledger::utils::CompressPubKey(std::get<0>(result)); } + ledger::bytes LedgerBridge::GetPublicKey(const ledger::Bip32Path path, bool display) + { + ledger::Ledger ledger(TRANSPORT_TYPE); + ledger.open(); + + return GetPublicKey(ledger, path, display); + } + ledger::bytes LedgerBridge::GetPublicKey(int account, bool isChange, int index, bool display) { return GetPublicKey(ledger::Bip32Path(account, isChange, index), display); @@ -81,7 +84,7 @@ namespace ledgerbridge for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { auto signature = std::get<1>(signTxResults[sigIndex]); - auto pubKey = CPubKey(GetPublicKey(signaturePaths[sigIndex], false)); + auto pubKey = CPubKey(GetPublicKey(ledger, signaturePaths[sigIndex], false)); // hash type signature.push_back(0x01); @@ -94,8 +97,6 @@ namespace ledgerbridge throw std::exception(); } } - - ledger.close(); } ledger::Tx LedgerBridge::ToLedgerTx(const CTransaction &tx) diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 61fca34e0..32752f061 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -1,6 +1,7 @@ #include "ledger/bip32.h" #include "ledger/tx.h" #include "ledger/bytes.h" +#include "ledger/ledger.h" #include "itxdb.h" #include "wallet.h" @@ -23,6 +24,7 @@ namespace ledgerbridge LedgerBridge(); ~LedgerBridge(); + ledger::bytes GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, bool display); ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); ledger::bytes GetAccountPublicKey(int account, bool display); From b4ffab570395ce163f7e0116cb8f5c97ce1d6a32 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 4 Apr 2023 21:55:33 +0200 Subject: [PATCH 062/129] Show ledger messagebox when signing a tx --- wallet/ledger/messagebox.cpp | 32 +++++++++++++++++++++++ wallet/ledger/messagebox.h | 30 +++++++++++++++++++++ wallet/qt/editaddressdialog.cpp | 24 +++-------------- wallet/qt/sendcoinsdialog.cpp | 46 ++++++++++++++++++++++++++------- wallet/qt/sendcoinsdialog.h | 42 +++++++++++++++++++++++++++--- wallet/wallet.pri | 2 ++ 6 files changed, 143 insertions(+), 33 deletions(-) create mode 100644 wallet/ledger/messagebox.cpp create mode 100644 wallet/ledger/messagebox.h diff --git a/wallet/ledger/messagebox.cpp b/wallet/ledger/messagebox.cpp new file mode 100644 index 000000000..cbffadfb5 --- /dev/null +++ b/wallet/ledger/messagebox.cpp @@ -0,0 +1,32 @@ +#include "messagebox.h" + +#include +#include +#include + +namespace ledger +{ + MessageBox::MessageBox(QWidget *parent, QSharedPointer worker) : worker_(worker), msgBox_(parent) + { + worker_->moveToThread(&thread_); + connect(&thread_, SIGNAL(finished()), &thread_, SLOT(deleteLater())); + thread_.start(); + + msgBox_.setIcon(QMessageBox::Icon::Information); + msgBox_.setWindowTitle(parent->windowTitle()); + msgBox_.setText("Please confirm or cancel the action on your Ledger device."); + msgBox_.setStandardButtons(QMessageBox::StandardButton::NoButton); + } + + void MessageBox::exec() + { + msgBox_.exec(); + thread_.wait(); // to make sure that the thread is finished + } + + void MessageBox::quit() + { + msgBox_.accept(); + thread_.quit(); + } +} diff --git a/wallet/ledger/messagebox.h b/wallet/ledger/messagebox.h new file mode 100644 index 000000000..e3ffcd3b4 --- /dev/null +++ b/wallet/ledger/messagebox.h @@ -0,0 +1,30 @@ +#ifndef __LEDGER_MESSAGEBOX +#define __LEDGER_MESSAGEBOX 1 + +#include +#include +#include +#include +#include + +namespace ledger +{ + class MessageBox : public QObject + { + Q_OBJECT + + public: + MessageBox(QWidget *parent, QSharedPointer worker); + void exec(); + + public slots: + void quit(); + + private: + QThread thread_; + QSharedPointer worker_; + QMessageBox msgBox_; + }; +} + +#endif diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index f4364e6b5..753d29c9f 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -3,12 +3,11 @@ #include "addresstablemodel.h" #include "guiutil.h" #include "ledger/bip32.h" +#include #include "ledger/utils.h" #include -#include #include -#include #include void AddLedgerRowWorker::addRow(Ui::EditAddressDialog *ui, AddressTableModel *model, QSharedPointer workerPtr) { @@ -115,27 +114,12 @@ bool EditAddressDialog::saveCurrentRow() case NewReceivingAddress: if (isLedger) { - QThread addRowThread; QSharedPointer worker = QSharedPointer::create(); - worker->moveToThread(&addRowThread); - - connect(worker.data(), SIGNAL(resultReady(QString)), &addRowThread, SLOT(quit())); - connect(&addRowThread, SIGNAL(finished()), &addRowThread, SLOT(deleteLater())); - - QMessageBox msgBox(this); - msgBox.setIcon(QMessageBox::Icon::Information); - msgBox.setWindowTitle(windowTitle()); - msgBox.setText("Please confirm or cancel the action on your Ledger device."); - msgBox.setStandardButtons(QMessageBox::StandardButton::NoButton); - + ledger::MessageBox msgBox(this, worker); connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(setAddress(QString))); - connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(accept())); - - addRowThread.start(); + connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(quit())); QTimer::singleShot(0, worker.data(), [this, worker]() { worker->addRow(ui, model, worker); }); msgBox.exec(); - - addRowThread.wait(); // to make sure that the thread is finished } else { address = model->addRow( AddressTableModel::Receive, @@ -213,7 +197,7 @@ void EditAddressDialog::accept() break; case AddressTableModel::LEDGER_ERROR: QMessageBox::critical(this, windowTitle(), - tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected and the Neblio app is opened on the device.").arg(QString::fromStdString(model->getLedgerErrorMessage())), + tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(model->getLedgerErrorMessage())), QMessageBox::Ok, QMessageBox::Ok); break; diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 663bec95c..1bc5f621d 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -24,9 +24,29 @@ #include "ntp1/ntp1tokenlistmodel.h" #include "ntp1/ntp1tools.h" +#include "ledger/messagebox.h" + +void LedgerSignTxWorker::signTx( + WalletModel* model, + QList recipients, + boost::shared_ptr ntp1wallet, + const RawNTP1MetadataBeforeSend& ntp1metadata, + bool fSpendDelegated, + const CCoinControl* coinControl, + const std::string& strFromAccount, + QSharedPointer workerPtr +) { + auto sendStatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegated, coinControl, strFromAccount, true); + + emit resultReady(sendStatus); + workerPtr.reset(); +} + SendCoinsDialog::SendCoinsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), model(0) { + qRegisterMetaType("WalletModel::SendCoinsReturn"); + ui->setupUi(this); #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac @@ -264,11 +284,19 @@ void SendCoinsDialog::on_sendButton_clicked() std::string strFromAccount = ""; if (fLedgerTx) strFromAccount = ui->ledgerPayFromNameEdit->text().toStdString(); - - auto sendstatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, - coinControl, strFromAccount, fLedgerTx); - switch (sendstatus.status) { + if (fLedgerTx) { + QSharedPointer worker = QSharedPointer::create(); + ledger::MessageBox msgBox(this, worker); + connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), this, SLOT(setSendStatus(WalletModel::SendCoinsReturn))); + connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), &msgBox, SLOT(quit())); + QTimer::singleShot(0, worker.data(), [&]() { worker->signTx(model, recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, coinControl, strFromAccount, worker); }); + msgBox.exec(); + } else { + sendStatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, coinControl, strFromAccount, fLedgerTx); + } + + switch (sendStatus.status) { case WalletModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), tr("The recipient address is not valid, please recheck."), QMessageBox::Ok, @@ -286,7 +314,7 @@ void SendCoinsDialog::on_sendButton_clicked() QMessageBox::warning( this, tr("Send Coins"), tr("The total exceeds your balance when the %1 transaction fee is included.") - .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)), + .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendStatus.fee)), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::DuplicateAddress: @@ -297,7 +325,7 @@ void SendCoinsDialog::on_sendButton_clicked() break; case WalletModel::TransactionCreationFailed: QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed. ") + sendstatus.msg, + tr("Error: Transaction creation failed. ") + sendStatus.msg, QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::TransactionCommitFailed: @@ -317,7 +345,7 @@ void SendCoinsDialog::on_sendButton_clicked() "You should NOT send NEBL from these addresses or the NTP1 tokens could be permanently " "burned. " "This address contains NTP1 tokens: " + - sendstatus.address + + sendStatus.address + "\n\n" "You have the following options:\n" "1. Use coin control and choose addresses that do not contain NTP1 tokens.\n" @@ -330,7 +358,7 @@ void SendCoinsDialog::on_sendButton_clicked() QMessageBox::warning( this, tr("Send Coins - NTP1 tokens problem"), "Error: Unable to check whether your addresses contain NTP1 tokens (for address: " + - sendstatus.address + + sendStatus.address + ")\n" "Sending NEBL from an address that contains NTP1 tokens could result in those tokens " "being permanently burned. " @@ -380,7 +408,7 @@ void SendCoinsDialog::on_sendButton_clicked() case WalletModel::NTP1TokenCalculationsFailed: QMessageBox::warning(this, tr("Send Coins - NTP1 calculations failed"), "Unable to calculate reserve tokens to be spent in this transaction. " + - sendstatus.msg, + sendStatus.msg, QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::OK: diff --git a/wallet/qt/sendcoinsdialog.h b/wallet/qt/sendcoinsdialog.h index a388f6872..4a3bbe889 100644 --- a/wallet/qt/sendcoinsdialog.h +++ b/wallet/qt/sendcoinsdialog.h @@ -1,14 +1,21 @@ #ifndef SENDCOINSDIALOG_H #define SENDCOINSDIALOG_H +#include #include +#include +#include #include #include +#include "coincontroldialog.h" +#include "ntp1/ntp1wallet.h" +#include "ntp1/ntp1transaction.h" +#include "walletmodel.h" + namespace Ui { class SendCoinsDialog; } -class WalletModel; class SendCoinsEntry; class SendCoinsRecipient; class BalancesWorker; @@ -17,6 +24,30 @@ QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE +/* Object for signing a transaction on a Ledger device in a separate thread. +*/ +class LedgerSignTxWorker : public QObject +{ + Q_OBJECT + +public slots: + // we use the shared pointer argument to ensure that workerPtr will be deleted after doing the + // retrieval + void signTx( + WalletModel* model, + QList recipients, + boost::shared_ptr ntp1wallet, + const RawNTP1MetadataBeforeSend& ntp1metadata, + bool fSpendDelegated, + const CCoinControl* coinControl, + const std::string& strFromAccount, + QSharedPointer workerPtr + ); + +signals: + void resultReady(WalletModel::SendCoinsReturn); +}; + /** Dialog for sending bitcoins */ class SendCoinsDialog : public QDialog { @@ -48,14 +79,17 @@ public slots: void showEditMetadataDialog(); private: - Ui::SendCoinsDialog* ui; - WalletModel* model; - bool fNewRecipientAllowed; + Ui::SendCoinsDialog* ui; + WalletModel* model; + bool fNewRecipientAllowed; + WalletModel::SendCoinsReturn sendStatus; + private slots: void on_ledgerCheckBox_toggled(bool checked); void on_ledgerAddressBookButton_clicked(); void on_sendButton_clicked(); + void setSendStatus(WalletModel::SendCoinsReturn status) { sendStatus = status; } void removeEntry(SendCoinsEntry* entry); void updateDisplayUnit(); void coinControlFeatureChanged(bool); diff --git a/wallet/wallet.pri b/wallet/wallet.pri index f9123376e..b9ce43de5 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -9,6 +9,7 @@ HEADERS += qt/bitcoingui.h \ ledger/hash.h \ ledger/hid.h \ ledger/ledger.h \ + ledger/messagebox.h \ ledger/speculos.h \ ledger/transport.h \ ledger/tx.h \ @@ -218,6 +219,7 @@ SOURCES += qt/bitcoin.cpp \ ledger/error.cpp \ ledger/hid.cpp \ ledger/ledger.cpp \ + ledger/messagebox.cpp \ ledger/speculos.cpp \ ledger/transport.cpp \ ledger/tx.cpp \ From bd7639a501d77097f4d80479df09fdfc91c9ab93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 5 Apr 2023 09:05:09 +0200 Subject: [PATCH 063/129] Add type to `Bip32Path` and fix small bugs --- wallet/ledger/bip32.cpp | 71 ++++++++++++++++++++++++++++------------- wallet/ledger/bip32.h | 9 +++++- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index f9490caa4..919568ab2 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -22,14 +22,16 @@ namespace ledger Bip32Path Bip32Path::ToChangePath() const { - if (components.size() == 3) { + if (type == Bip32PathType::Account) + { throw std::runtime_error("Account keypath cannot be converted to change path!"); } return Bip32Path(components[ACCOUNT_INDEX], true, components[ADDRESS_INDEX_INDEX]); } - Bip32Path::Bip32Path(const std::string &keyPathStr) { + Bip32Path::Bip32Path(const std::string &keyPathStr) + { std::vector _components; std::stringstream ss(keyPathStr); std::string item; @@ -71,44 +73,62 @@ namespace ledger first = false; } - if (_components.size() != 3 || _components.size() != 5) + if (_components.size() == 3) + { + type = Bip32PathType::Account; + } + else if (_components.size() == 5) + { + type = Bip32PathType::Address; + } + else { throw std::runtime_error("Invalid keypath size"); } - if (_components[0] != Harden(BIP32_PURPOSE)) + if (_components[PURPOSE_INDEX] != Harden(BIP32_PURPOSE)) { throw std::runtime_error("Invalid keypath purpose"); } - if (_components[1] != Harden(BIP32_COIN_TYPE)) + if (_components[COIN_TYPE_INDEX] != Harden(BIP32_COIN_TYPE)) { throw std::runtime_error("Invalid keypath coin type"); } - if (!IsHardened(_components[2])) + if (!IsHardened(_components[ACCOUNT_INDEX])) { throw std::runtime_error("Invalid keypath account"); } - if (_components.size() == 5 && IsHardened(_components[3])) + if (type == Bip32PathType::Address) { - throw std::runtime_error("Invalid keypath change"); - } + if (IsHardened(_components[CHANGE_INDEX])) + { + throw std::runtime_error("Invalid keypath change"); + } - if (_components.size() == 5 && IsHardened(_components[4])) - { - throw std::runtime_error("Invalid keypath index"); + if (IsHardened(_components[ADDRESS_INDEX_INDEX])) + { + throw std::runtime_error("Invalid keypath index"); + } } components.push_back(BIP32_PURPOSE); components.push_back(BIP32_COIN_TYPE); components.push_back(_components[ACCOUNT_INDEX]); - components.push_back(_components[CHANGE_INDEX]); - components.push_back(_components[ADDRESS_INDEX_INDEX]); + + if (type == Bip32PathType::Address) + { + components.push_back(_components[CHANGE_INDEX]); + components.push_back(_components[ADDRESS_INDEX_INDEX]); + } } - Bip32Path::Bip32Path(uint32_t account) { + Bip32Path::Bip32Path(uint32_t account) + { + type = Bip32PathType::Account; + components.push_back(BIP32_PURPOSE); components.push_back(BIP32_COIN_TYPE); components.push_back(account); @@ -118,7 +138,10 @@ namespace ledger : Bip32Path(std::stoul(account), isChange, std::stoul(index)) {}; Bip32Path::Bip32Path(uint32_t account, bool isChange, uint32_t index) - : Bip32Path(account) { + : Bip32Path(account) + { + type = Bip32PathType::Address; + components.push_back(isChange ? 1 : 0); components.push_back(index); }; @@ -130,8 +153,12 @@ namespace ledger utils::AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); utils::AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); utils::AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); - utils::AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); - utils::AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); + + if (type == Bip32PathType::Address) + { + utils::AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); + utils::AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); + } return serializedKeyPath; } @@ -140,13 +167,11 @@ namespace ledger { std::stringstream ss; - ss << "m/" << components[PURPOSE_INDEX] << "'/" << components[COIN_TYPE_INDEX] << "'/" << components[ACCOUNT_INDEX]; + ss << "m/" << components[PURPOSE_INDEX] << "'/" << components[COIN_TYPE_INDEX] << "'/" << components[ACCOUNT_INDEX] << "'"; - if (components.size() > 3) { + if (type == Bip32PathType::Address) + { ss << "/" << components[CHANGE_INDEX]; - } - - if (components.size() > 4) { ss << "/" << components[ADDRESS_INDEX_INDEX]; } diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 339761650..6798143be 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -36,7 +36,14 @@ namespace ledger std::string ToString() const; private: - std::vector components; + enum class Bip32PathType + { + Account, + Address + }; + + Bip32PathType type; + std::vector components; }; } From e8674b01745526d41e72098fcd42e3e90df9658c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 5 Apr 2023 09:05:26 +0200 Subject: [PATCH 064/129] Fix memory leak in `EditAddressDialog` --- wallet/qt/editaddressdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 753d29c9f..6de1dc134 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -213,11 +213,11 @@ void EditAddressDialog::updateLedgerPathLabel() return; } - auto path = (new ledger::Bip32Path( + auto path = ledger::Bip32Path( ui->ledgerAccountEdit->text().toStdString(), false, ui->ledgerIndexEdit->text().toStdString() - ))->ToString(); + ).ToString(); ui->ledgerPathLabel->setText(tr("Ledger path: %1").arg(QString::fromStdString(path))); } From 8e3464c6c5cac3f2581174dbaabe03e75a8b0d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 5 Apr 2023 17:21:15 +0200 Subject: [PATCH 065/129] Remove unused Ledger files --- wallet/ledger/base58.h | 75 ---- wallet/ledger/hash.h | 172 --------- wallet/ledger/uint256.h | 784 ---------------------------------------- wallet/wallet.pri | 3 - 4 files changed, 1034 deletions(-) delete mode 100644 wallet/ledger/base58.h delete mode 100644 wallet/ledger/hash.h delete mode 100644 wallet/ledger/uint256.h diff --git a/wallet/ledger/base58.h b/wallet/ledger/base58.h deleted file mode 100644 index bb4fb271e..000000000 --- a/wallet/ledger/base58.h +++ /dev/null @@ -1,75 +0,0 @@ -#include "bytes.h" - -#include -#include -#include -#include -#include -#include - -using namespace ledger; - -inline static constexpr const uint8_t Base58Map[] = { - '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', - 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', - 'y', 'z'}; -inline static constexpr const uint8_t AlphaMap[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0xff, 0x11, 0x12, 0x13, 0x14, 0x15, 0xff, - 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0xff, 0x2c, 0x2d, 0x2e, - 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff}; - -std::string Base58Encode(const bytes &data) -{ - bytes digits((data.size() * 138 / 100) + 1); - size_t digitslen = 1; - for (size_t i = 0; i < data.size(); i++) - { - uint32_t carry = static_cast(data[i]); - for (size_t j = 0; j < digitslen; j++) - { - carry = carry + static_cast(digits[j] << 8); - digits[j] = static_cast(carry % 58); - carry /= 58; - } - for (; carry; carry /= 58) - digits[digitslen++] = static_cast(carry % 58); - } - std::string result; - for (size_t i = 0; i < (data.size() - 1) && !data[i]; i++) - result.push_back(Base58Map[0]); - for (size_t i = 0; i < digitslen; i++) - result.push_back(Base58Map[digits[digitslen - 1 - i]]); - return result; -} - -bytes Base58Decode(const std::string &data) -{ - bytes result((data.size() * 138 / 100) + 1); - size_t resultlen = 1; - for (size_t i = 0; i < data.size(); i++) - { - uint32_t carry = static_cast(AlphaMap[data[i] & 0x7f]); - for (size_t j = 0; j < resultlen; j++, carry >>= 8) - { - carry += static_cast(result[j] * 58); - result[j] = static_cast(carry); - } - for (; carry; carry >>= 8) - result[resultlen++] = static_cast(carry); - } - result.resize(resultlen); - for (size_t i = 0; i < (data.size() - 1) && data[i] == AlphaMap[0]; i++) - result.push_back(0); - std::reverse(result.begin(), result.end()); - return result; -} \ No newline at end of file diff --git a/wallet/ledger/hash.h b/wallet/ledger/hash.h deleted file mode 100644 index 574c0ef60..000000000 --- a/wallet/ledger/hash.h +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_HASH_H -#define BITCOIN_HASH_H - -#include "uint256.h" - -#include -#include -#include -#include - -template -inline uint256 Hash(const T1 pbegin, const T1 pend) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256((pbegin == pend ? pblank : (unsigned char *)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), - (unsigned char *)&hash1); - uint256 hash2; - SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); - return hash2; -} - -class CHashWriter -{ -private: - SHA256_CTX ctx; - -public: - int nType; - int nVersion; - - void Init() { SHA256_Init(&ctx); } - - CHashWriter(int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { Init(); } - - CHashWriter &write(const char *pch, size_t size) - { - SHA256_Update(&ctx, pch, size); - return (*this); - } - - // invalidates the object - uint256 GetHash() - { - uint256 hash1; - SHA256_Final((unsigned char *)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); - return hash2; - } - - // template - // CHashWriter &operator<<(const T &obj) - // { - // // Serialize to this stream - // ::Serialize(*this, obj, nType, nVersion); - // return (*this); - // } -}; - -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char *)&p1begin[0]), - (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char *)&p2begin[0]), - (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Final((unsigned char *)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); - return hash2; -} - -template -inline uint256 Hash(const T1 p1begin, const T1 p1end, const T2 p2begin, const T2 p2end, const T3 p3begin, - const T3 p3end) -{ - static unsigned char pblank[1]; - uint256 hash1; - SHA256_CTX ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char *)&p1begin[0]), - (p1end - p1begin) * sizeof(p1begin[0])); - SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char *)&p2begin[0]), - (p2end - p2begin) * sizeof(p2begin[0])); - SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char *)&p3begin[0]), - (p3end - p3begin) * sizeof(p3begin[0])); - SHA256_Final((unsigned char *)&hash1, &ctx); - uint256 hash2; - SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); - return hash2; -} - -// template -// uint256 SerializeHash(const T &obj, int nType = SER_GETHASH, int nVersion = PROTOCOL_VERSION) -// { -// CHashWriter ss(nType, nVersion); -// ss << obj; -// return ss.GetHash(); -// } - -inline uint160 Hash160(const std::vector &vch) -{ - uint256 hash1; - SHA256(&vch[0], vch.size(), (unsigned char *)&hash1); - uint160 hash2; - RIPEMD160((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); - return hash2; -} - -unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector &vDataToHash); - -template -class HashCalculator -{ - CTXType ctx; - -public: - HashCalculator() { reset(); } - void push_data(const std::string &data) - { - UpdateFunc(&ctx, reinterpret_cast(&data.front()), data.size()); - } - void push_data(const std::vector &data) - { - UpdateFunc(&ctx, reinterpret_cast(&data.front()), data.size()); - } - void push_data(const std::vector &data) - { - UpdateFunc(&ctx, reinterpret_cast(&data.front()), data.size()); - } - void reset() - { - ctx = CTXType(); - InitFunc(&ctx); - } - std::string getHashAndReset() - { - std::string res; - res.resize(DigestSize); - FinalFunc(reinterpret_cast(&res.front()), &ctx); - reset(); - return res; - } -}; - -using Sha1Calculator = HashCalculator; -using Sha224Calculator = - HashCalculator; -using Sha256Calculator = - HashCalculator; -using Sha384Calculator = - HashCalculator; -using Sha512Calculator = - HashCalculator; -using Md5Calculator = HashCalculator; -using Ripemd160HashCalculator = HashCalculator; - -void *KDF_SHA256(const void *in, size_t inlen, void *out, size_t *outlen); -void *KDF_SHA512(const void *in, size_t inlen, void *out, size_t *outlen); - -#endif diff --git a/wallet/ledger/uint256.h b/wallet/ledger/uint256.h deleted file mode 100644 index 8c31baefa..000000000 --- a/wallet/ledger/uint256.h +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_UINT256_H -#define BITCOIN_UINT256_H - -#include -#include -#include - -#include -#include -#include - -inline int Testuint256AdHoc(std::vector vArg); - -/** Base class without constructors for uint256 and uint160. - * This makes the compiler let u use it in a union. - */ -template -class base_uint -{ -protected: - enum - { - WIDTH = BITS / 32 - }; - static_assert(WIDTH * 32 == BITS, "You cannot have a width that is not a multiple of 32"); - uint32_t pn[WIDTH]; - -public: - bool operator!() const - { - for (int i = 0; i < WIDTH; i++) - if (pn[i] != 0) - return false; - return true; - } - - const base_uint operator~() const - { - base_uint ret; - for (int i = 0; i < WIDTH; i++) - ret.pn[i] = ~pn[i]; - return ret; - } - - const base_uint operator-() const - { - base_uint ret; - for (int i = 0; i < WIDTH; i++) - ret.pn[i] = ~pn[i]; - ret++; - return ret; - } - - double getdouble() const - { - double ret = 0.0; - double fact = 1.0; - for (int i = 0; i < WIDTH; i++) - { - ret += fact * pn[i]; - fact *= 4294967296.0; - } - return ret; - } - - base_uint &operator=(uint64_t b) - { - pn[0] = (uint32_t)b; - pn[1] = (uint32_t)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - base_uint &operator^=(const base_uint &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] ^= b.pn[i]; - return *this; - } - - base_uint &operator&=(const base_uint &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] &= b.pn[i]; - return *this; - } - - base_uint &operator|=(const base_uint &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] |= b.pn[i]; - return *this; - } - - base_uint &operator^=(uint64_t b) - { - pn[0] ^= (uint32_t)b; - pn[1] ^= (uint32_t)(b >> 32); - return *this; - } - - base_uint &operator|=(uint64_t b) - { - pn[0] |= (uint32_t)b; - pn[1] |= (uint32_t)(b >> 32); - return *this; - } - - base_uint &operator<<=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i + k + 1 < WIDTH && shift != 0) - pn[i + k + 1] |= (a.pn[i] >> (32 - shift)); - if (i + k < WIDTH) - pn[i + k] |= (a.pn[i] << shift); - } - return *this; - } - - base_uint &operator>>=(unsigned int shift) - { - base_uint a(*this); - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i - k - 1 >= 0 && shift != 0) - pn[i - k - 1] |= (a.pn[i] << (32 - shift)); - if (i - k >= 0) - pn[i - k] |= (a.pn[i] >> shift); - } - return *this; - } - - base_uint &operator+=(const base_uint &b) - { - uint64_t carry = 0; - for (int i = 0; i < WIDTH; i++) - { - uint64_t n = carry + pn[i] + b.pn[i]; - pn[i] = n & 0xffffffff; - carry = n >> 32; - } - return *this; - } - - base_uint &operator-=(const base_uint &b) - { - *this += -b; - return *this; - } - - base_uint &operator+=(uint64_t b64) - { - base_uint b; - b = b64; - *this += b; - return *this; - } - - base_uint &operator-=(uint64_t b64) - { - base_uint b; - b = b64; - *this += -b; - return *this; - } - - base_uint &operator++() - { - // prefix operator - int i = 0; - while (++pn[i] == 0 && i < WIDTH - 1) - i++; - return *this; - } - - const base_uint operator++(int) - { - // postfix operator - const base_uint ret = *this; - ++(*this); - return ret; - } - - base_uint &operator--() - { - // prefix operator - int i = 0; - while (--pn[i] == -1 && i < WIDTH - 1) - i++; - return *this; - } - - const base_uint operator--(int) - { - // postfix operator - const base_uint ret = *this; - --(*this); - return ret; - } - - friend inline bool operator<(const base_uint &a, const base_uint &b) - { - for (int i = base_uint::WIDTH - 1; i >= 0; i--) - { - if (a.pn[i] < b.pn[i]) - return true; - else if (a.pn[i] > b.pn[i]) - return false; - } - return false; - } - - friend inline bool operator<=(const base_uint &a, const base_uint &b) - { - for (int i = base_uint::WIDTH - 1; i >= 0; i--) - { - if (a.pn[i] < b.pn[i]) - return true; - else if (a.pn[i] > b.pn[i]) - return false; - } - return true; - } - - friend inline bool operator>(const base_uint &a, const base_uint &b) - { - for (int i = base_uint::WIDTH - 1; i >= 0; i--) - { - if (a.pn[i] > b.pn[i]) - return true; - else if (a.pn[i] < b.pn[i]) - return false; - } - return false; - } - - friend inline bool operator>=(const base_uint &a, const base_uint &b) - { - for (int i = base_uint::WIDTH - 1; i >= 0; i--) - { - if (a.pn[i] > b.pn[i]) - return true; - else if (a.pn[i] < b.pn[i]) - return false; - } - return true; - } - - friend inline bool operator==(const base_uint &a, const base_uint &b) - { - for (int i = 0; i < base_uint::WIDTH; i++) - if (a.pn[i] != b.pn[i]) - return false; - return true; - } - - friend inline bool operator==(const base_uint &a, uint64_t b) - { - if (a.pn[0] != (uint32_t)b) - return false; - if (a.pn[1] != (uint32_t)(b >> 32)) - return false; - for (int i = 2; i < base_uint::WIDTH; i++) - if (a.pn[i] != 0) - return false; - return true; - } - - friend inline bool operator!=(const base_uint &a, const base_uint &b) - { - return (!(a == b)); - } - - friend inline bool operator!=(const base_uint &a, uint64_t b) - { - return (!(a == b)); - } - - std::string GetHex() const - { - char psz[sizeof(pn) * 2 + 1]; - for (unsigned int i = 0; i < sizeof(pn); i++) - sprintf(psz + i * 2, "%02x", ((unsigned char *)pn)[sizeof(pn) - i - 1]); - return std::string(psz, psz + sizeof(pn) * 2); - } - - void SetHex(const char *psz) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - - // skip leading spaces - while (isspace(*psz)) - psz++; - - // skip 0x - if (psz[0] == '0' && tolower(psz[1]) == 'x') - psz += 2; - - // hex string to uint - static const unsigned char phexdigit[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - const char *pbegin = psz; - while (phexdigit[(unsigned char)*psz] || *psz == '0') - psz++; - psz--; - unsigned char *p1 = (unsigned char *)pn; - unsigned char *pend = p1 + WIDTH * 4; - while (psz >= pbegin && p1 < pend) - { - *p1 = phexdigit[(unsigned char)*psz--]; - if (psz >= pbegin) - { - *p1 |= (phexdigit[(unsigned char)*psz--] << 4); - p1++; - } - } - } - - void SetHex(const std::string &str) - { - SetHex(str.c_str()); - } - - std::string ToString() const - { - return (GetHex()); - } - - unsigned char *begin() - { - return (unsigned char *)&pn[0]; - } - - unsigned char *end() - { - return (unsigned char *)&pn[WIDTH]; - } - - const unsigned char *begin() const - { - return (unsigned char *)&pn[0]; - } - - const unsigned char *end() const - { - return (unsigned char *)&pn[WIDTH]; - } - - unsigned int size() const - { - return sizeof(pn); - } - - uint64_t Get64(int n = 0) const - { - return pn[2 * n] | (uint64_t)pn[2 * n + 1] << 32; - } - - unsigned int GetSerializeSize(int /*nType*/, int /*nVersion*/) const - { - return sizeof(pn); - } - - template - void Serialize(Stream &s, int /*nType*/, int /*nVersion*/) const - { - s.write((char *)pn, sizeof(pn)); - } - - template - void Unserialize(Stream &s, int /*nType*/, int /*nVersion*/) - { - s.read((char *)pn, sizeof(pn)); - } - - friend class uint160; - friend class uint256; - friend inline int Testuint256AdHoc(std::vector vArg); -}; - -typedef base_uint<160> base_uint160; -typedef base_uint<256> base_uint256; - -// -// uint160 and uint256 could be implemented as templates, but to keep -// compile errors and debugging cleaner, they're copy and pasted. -// - -////////////////////////////////////////////////////////////////////////////// -// -// uint160 -// - -/** 160-bit unsigned integer */ -class uint160 : public base_uint160 -{ -public: - typedef base_uint160 basetype; - - uint160() - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - } - - uint160(const basetype &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - } - - uint160 &operator=(const basetype &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - return *this; - } - - uint160(uint64_t b) - { - pn[0] = (uint32_t)b; - pn[1] = (uint32_t)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - } - - uint160 &operator=(uint64_t b) - { - pn[0] = (uint32_t)b; - pn[1] = (uint32_t)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - explicit uint160(const std::string &str) - { - SetHex(str); - } - - explicit uint160(const std::vector &vch) - { - if (vch.size() == sizeof(pn)) - memcpy(pn, &vch[0], sizeof(pn)); - else - *this = 0; - } -}; - -inline bool operator==(const uint160 &a, uint64_t b) { return (base_uint160)a == b; } -inline bool operator!=(const uint160 &a, uint64_t b) { return (base_uint160)a != b; } -inline const uint160 operator<<(const base_uint160 &a, unsigned int shift) { return uint160(a) <<= shift; } -inline const uint160 operator>>(const base_uint160 &a, unsigned int shift) { return uint160(a) >>= shift; } -inline const uint160 operator<<(const uint160 &a, unsigned int shift) { return uint160(a) <<= shift; } -inline const uint160 operator>>(const uint160 &a, unsigned int shift) { return uint160(a) >>= shift; } - -inline const uint160 operator^(const base_uint160 &a, const base_uint160 &b) { return uint160(a) ^= b; } -inline const uint160 operator&(const base_uint160 &a, const base_uint160 &b) { return uint160(a) &= b; } -inline const uint160 operator|(const base_uint160 &a, const base_uint160 &b) { return uint160(a) |= b; } -inline const uint160 operator+(const base_uint160 &a, const base_uint160 &b) { return uint160(a) += b; } -inline const uint160 operator-(const base_uint160 &a, const base_uint160 &b) { return uint160(a) -= b; } - -inline bool operator<(const base_uint160 &a, const uint160 &b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const base_uint160 &a, const uint160 &b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const base_uint160 &a, const uint160 &b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const base_uint160 &a, const uint160 &b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const base_uint160 &a, const uint160 &b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const base_uint160 &a, const uint160 &b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const base_uint160 &a, const uint160 &b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const base_uint160 &a, const uint160 &b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const base_uint160 &a, const uint160 &b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const base_uint160 &a, const uint160 &b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const base_uint160 &a, const uint160 &b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160 &a, const base_uint160 &b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160 &a, const base_uint160 &b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160 &a, const base_uint160 &b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160 &a, const base_uint160 &b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160 &a, const base_uint160 &b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160 &a, const base_uint160 &b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160 &a, const base_uint160 &b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160 &a, const base_uint160 &b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160 &a, const base_uint160 &b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160 &a, const base_uint160 &b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160 &a, const base_uint160 &b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160 &a, const uint160 &b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160 &a, const uint160 &b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160 &a, const uint160 &b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160 &a, const uint160 &b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160 &a, const uint160 &b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160 &a, const uint160 &b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160 &a, const uint160 &b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160 &a, const uint160 &b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160 &a, const uint160 &b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160 &a, const uint160 &b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160 &a, const uint160 &b) { return (base_uint160)a - (base_uint160)b; } - -////////////////////////////////////////////////////////////////////////////// -// -// uint256 -// - -/** 256-bit unsigned integer */ -class uint256 : public base_uint256 -{ -public: - typedef base_uint256 basetype; - - uint256() - { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; - } - - uint256(const basetype &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - } - - uint256 &operator=(const basetype &b) - { - for (int i = 0; i < WIDTH; i++) - pn[i] = b.pn[i]; - return *this; - } - - uint256(uint64_t b) - { - pn[0] = (uint32_t)b; - pn[1] = (uint32_t)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - } - - uint256 &operator=(uint64_t b) - { - pn[0] = (uint32_t)b; - pn[1] = (uint32_t)(b >> 32); - for (int i = 2; i < WIDTH; i++) - pn[i] = 0; - return *this; - } - - explicit uint256(const std::string &str) - { - SetHex(str); - } - - explicit uint256(const std::vector &vch) - { - if (vch.size() == sizeof(pn)) - memcpy(pn, &vch[0], sizeof(pn)); - else - *this = 0; - } - - bool IsNull() const - { - for (int i = 0; i < WIDTH; i++) - if (pn[i] != 0) - return false; - return true; - } - - void SetNull() - { - std::memset(pn, 0, sizeof(pn)); - } -}; - -namespace std -{ - template <> - struct hash - { - std::size_t operator()(const uint256 &k) const { return std::hash()(k.Get64(0)); } - }; -} // namespace std - -inline bool operator==(const uint256 &a, uint64_t b) { return (base_uint256)a == b; } -inline bool operator!=(const uint256 &a, uint64_t b) { return (base_uint256)a != b; } -inline const uint256 operator<<(const base_uint256 &a, unsigned int shift) { return uint256(a) <<= shift; } -inline const uint256 operator>>(const base_uint256 &a, unsigned int shift) { return uint256(a) >>= shift; } -inline const uint256 operator<<(const uint256 &a, unsigned int shift) { return uint256(a) <<= shift; } -inline const uint256 operator>>(const uint256 &a, unsigned int shift) { return uint256(a) >>= shift; } - -inline const uint256 operator^(const base_uint256 &a, const base_uint256 &b) { return uint256(a) ^= b; } -inline const uint256 operator&(const base_uint256 &a, const base_uint256 &b) { return uint256(a) &= b; } -inline const uint256 operator|(const base_uint256 &a, const base_uint256 &b) { return uint256(a) |= b; } -inline const uint256 operator+(const base_uint256 &a, const base_uint256 &b) { return uint256(a) += b; } -inline const uint256 operator-(const base_uint256 &a, const base_uint256 &b) { return uint256(a) -= b; } - -inline bool operator<(const base_uint256 &a, const uint256 &b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const base_uint256 &a, const uint256 &b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const base_uint256 &a, const uint256 &b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const base_uint256 &a, const uint256 &b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const base_uint256 &a, const uint256 &b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const base_uint256 &a, const uint256 &b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const base_uint256 &a, const uint256 &b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const base_uint256 &a, const uint256 &b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const base_uint256 &a, const uint256 &b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const base_uint256 &a, const uint256 &b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const base_uint256 &a, const uint256 &b) { return (base_uint256)a - (base_uint256)b; } - -inline bool operator<(const uint256 &a, const base_uint256 &b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const uint256 &a, const base_uint256 &b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const uint256 &a, const base_uint256 &b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const uint256 &a, const base_uint256 &b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const uint256 &a, const base_uint256 &b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const uint256 &a, const base_uint256 &b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const uint256 &a, const base_uint256 &b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const uint256 &a, const base_uint256 &b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const uint256 &a, const base_uint256 &b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const uint256 &a, const base_uint256 &b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const uint256 &a, const base_uint256 &b) { return (base_uint256)a - (base_uint256)b; } - -inline bool operator<(const uint256 &a, const uint256 &b) { return (base_uint256)a < (base_uint256)b; } -inline bool operator<=(const uint256 &a, const uint256 &b) { return (base_uint256)a <= (base_uint256)b; } -inline bool operator>(const uint256 &a, const uint256 &b) { return (base_uint256)a > (base_uint256)b; } -inline bool operator>=(const uint256 &a, const uint256 &b) { return (base_uint256)a >= (base_uint256)b; } -inline bool operator==(const uint256 &a, const uint256 &b) { return (base_uint256)a == (base_uint256)b; } -inline bool operator!=(const uint256 &a, const uint256 &b) { return (base_uint256)a != (base_uint256)b; } -inline const uint256 operator^(const uint256 &a, const uint256 &b) { return (base_uint256)a ^ (base_uint256)b; } -inline const uint256 operator&(const uint256 &a, const uint256 &b) { return (base_uint256)a & (base_uint256)b; } -inline const uint256 operator|(const uint256 &a, const uint256 &b) { return (base_uint256)a | (base_uint256)b; } -inline const uint256 operator+(const uint256 &a, const uint256 &b) { return (base_uint256)a + (base_uint256)b; } -inline const uint256 operator-(const uint256 &a, const uint256 &b) { return (base_uint256)a - (base_uint256)b; } - -#ifdef TEST_UINT256 - -#include "logging/logger.h" - -inline int Testuint256AdHoc(std::vector /*vArg*/) -{ - uint256 g(0); - - NLog.write(b_sev::info, "{}", g.ToString()); - g--; - NLog.write(b_sev::info, "g--"); - NLog.write(b_sev::info, "{}", g.ToString()); - g--; - NLog.write(b_sev::info, "g--"); - NLog.write(b_sev::info, "{}", g.ToString()); - g++; - NLog.write(b_sev::info, "g++"); - NLog.write(b_sev::info, "{}", g.ToString()); - g++; - NLog.write(b_sev::info, "g++"); - NLog.write(b_sev::info, "{}", g.ToString()); - g++; - NLog.write(b_sev::info, "g++"); - NLog.write(b_sev::info, "{}", g.ToString()); - g++; - NLog.write(b_sev::info, "g++"); - NLog.write(b_sev::info, "{}", g.ToString()); - - uint256 a(7); - NLog.write(b_sev::info, "a=7"); - NLog.write(b_sev::info, "{}", a.ToString()); - - uint256 b; - NLog.write(b_sev::info, "b undefined"); - NLog.write(b_sev::info, "{}", b.ToString()); - int c = 3; - - a = c; - a.pn[3] = 15; - NLog.write(b_sev::info, "{}", a.ToString()); - uint256 k(c); - - a = 5; - a.pn[3] = 15; - NLog.write(b_sev::info, "{}", a.ToString()); - b = 1; - b <<= 52; - - a |= b; - - a ^= 0x500; - - NLog.write(b_sev::info, "a {}", a.ToString()); - - a = a | b | (uint256)0x1000; - - NLog.write(b_sev::info, "a {}", a.ToString()); - NLog.write(b_sev::info, "b {}", b.ToString()); - - a = 0xfffffffe; - a.pn[4] = 9; - - NLog.write(b_sev::info, "{}", a.ToString()); - a++; - NLog.write(b_sev::info, "{}", a.ToString()); - a++; - NLog.write(b_sev::info, "{}", a.ToString()); - a++; - NLog.write(b_sev::info, "{}", a.ToString()); - a++; - NLog.write(b_sev::info, "{}", a.ToString()); - - a--; - NLog.write(b_sev::info, "{}", a.ToString()); - a--; - NLog.write(b_sev::info, "{}", a.ToString()); - a--; - NLog.write(b_sev::info, "{}", a.ToString()); - uint256 d = a--; - NLog.write(b_sev::info, "{}", d.ToString()); - NLog.write(b_sev::info, "{}", a.ToString()); - a--; - NLog.write(b_sev::info, "{}", a.ToString()); - a--; - NLog.write(b_sev::info, "{}", a.ToString()); - - d = a; - - NLog.write(b_sev::info, "{}", d.ToString()); - for (int i = uint256::WIDTH - 1; i >= 0; i--) - NLog.write(b_sev::info, "%08x", d.pn[i]); - NLog.write(b_sev::info, ""); - - uint256 neg = d; - neg = ~neg; - NLog.write(b_sev::info, "{}", neg.ToString()); - - uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - NLog.write(b_sev::info, ""); - NLog.write(b_sev::info, "{}", e.ToString()); - - NLog.write(b_sev::info, ""); - uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - uint256 x2; - NLog.write(b_sev::info, "{}", x1.ToString()); - for (int i = 0; i < 270; i += 4) - { - x2 = x1 << i; - NLog.write(b_sev::info, "{}", x2.ToString()); - } - - NLog.write(b_sev::info, ""); - NLog.write(b_sev::info, "{}", x1.ToString()); - for (int i = 0; i < 270; i += 4) - { - x2 = x1; - x2 >>= i; - NLog.write(b_sev::info, "{}", x2.ToString()); - } - - for (int i = 0; i < 100; i++) - { - uint256 k = (~uint256(0) >> i); - NLog.write(b_sev::info, "{}", k.ToString()); - } - - for (int i = 0; i < 100; i++) - { - uint256 k = (~uint256(0) << i); - NLog.write(b_sev::info, "{}", k.ToString()); - } - - return (0); -} - -#endif - -#endif diff --git a/wallet/wallet.pri b/wallet/wallet.pri index b9ce43de5..aab2076f0 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -1,19 +1,16 @@ # Input DEPENDPATH += . json qt HEADERS += qt/bitcoingui.h \ - ledger/base58.h \ ledger/bip32.h \ ledger/bytes.h \ ledger/comm.h \ ledger/error.h \ - ledger/hash.h \ ledger/hid.h \ ledger/ledger.h \ ledger/messagebox.h \ ledger/speculos.h \ ledger/transport.h \ ledger/tx.h \ - ledger/uint256.h \ ledger/utils.h \ qt/transactiontablemodel.h \ qt/addresstablemodel.h \ From 33eb5dd6a9879e5c8214eea820361f5ecb4d8871 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 6 Apr 2023 14:17:49 +0200 Subject: [PATCH 066/129] Fix ledger address row double-click behavior --- wallet/qt/addresstablemodel.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 70e61903c..3c23d64d7 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -349,11 +349,21 @@ Qt::ItemFlags AddressTableModel::flags(const QModelIndex& index) const AddressTableEntry* rec = static_cast(index.internalPointer()); Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - // Can edit address and label for sending addresses, + // Can edit label and address for sending addresses, // and only label for receiving addresses. - if (rec->type == AddressTableEntry::Sending || - (rec->type == AddressTableEntry::Receiving && index.column() == Label)) { // TODO DM? - retval |= Qt::ItemIsEditable; + switch (rec->type) + { + case AddressTableEntry::Sending: + if (index.column() == Label || index.column() == Address) + retval |= Qt::ItemIsEditable; + break; + case AddressTableEntry::Receiving: + case AddressTableEntry::ReceivingLedger: + if (index.column() == Label) + retval |= Qt::ItemIsEditable; + break; + default: + break; } return retval; } @@ -466,7 +476,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex& parent { Q_UNUSED(parent); AddressTableEntry* rec = priv->index(row); - if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving) { // TODO DM? + if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving || rec->type == AddressTableEntry::ReceivingLedger) { // Can only remove one row at a time, and cannot remove rows not in model. // Also refuse to remove receiving addresses. return false; From 4b58b0e7c98d4ab7e24ab87e22f342a4d87841c6 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 6 Apr 2023 14:56:40 +0200 Subject: [PATCH 067/129] Require unique label for ledger addresses --- wallet/qt/addresstablemodel.cpp | 41 +++++++++++++++++++++++++++++++++ wallet/qt/addresstablemodel.h | 24 +++++++++++-------- wallet/qt/editaddressdialog.cpp | 10 ++++++++ wallet/wallet.cpp | 11 +++++++++ wallet/wallet.h | 3 ++- 5 files changed, 79 insertions(+), 10 deletions(-) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 3c23d64d7..67fffb6c1 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -9,6 +9,8 @@ #include "base58.h" #include "wallet.h" +#include + #include #include @@ -276,6 +278,37 @@ bool AddressTableModel::isWhitelisted(const std::string& address) const return purposeForAddress(address).compare(AddressBook::AddressBookPurpose::DELEGATOR) == 0; } +bool AddressTableModel::isLabelUsedByLedger(const QString& label) +{ + std::string strLabel = label.toStdString(); + CTxDestination addressOut; + return ( + wallet->GetAddressBookEntryByLabel(strLabel, addressOut) && + addressOut.type() == typeid(CKeyID) && + wallet->HaveLedgerKey(*boost::get(&addressOut)) + ); +} + +bool AddressTableModel::isLabelUsableForLedger(const QString& label) +{ + std::string strLabel = label.toStdString(); + CTxDestination addressOut; + return !strLabel.empty() && !wallet->GetAddressBookEntryByLabel(strLabel, addressOut); +} + +bool AddressTableModel::checkLabelAvailability(const QString& label, bool isLedgerAddress) +{ + if (isLabelUsedByLedger(label)) { + editStatus = LABEL_USED_BY_LEDGER; + return false; + } + if (isLedgerAddress && !isLabelUsableForLedger(label)) { + editStatus = LABEL_NOT_USABLE_FOR_LEDGER; + return false; + } + return true; +} + bool AddressTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) @@ -292,6 +325,8 @@ bool AddressTableModel::setData(const QModelIndex& index, const QVariant& value, editStatus = NO_CHANGES; return false; } + if (!checkLabelAvailability(value.toString(), rec->type == AddressTableEntry::ReceivingLedger)) + return false; wallet->SetAddressBookEntry(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString()); break; @@ -394,6 +429,9 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con editStatus = OK; + if (!checkLabelAvailability(label, type == ReceiveLedger)) + return QString(); + if (type == Send) { if (!walletModel->validateAddress(address)) { editStatus = INVALID_ADDRESS; @@ -467,6 +505,9 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con // Add entry { LOCK(wallet->cs_wallet); + // Double-check label availability (now that we have acquired the lock) + if (!checkLabelAvailability(label, type == ReceiveLedger)) + return QString(); wallet->SetAddressBookEntry(CBitcoinAddress(strAddress).Get(), strLabel); } return QString::fromStdString(strAddress); diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index d5a76ac34..113895dbf 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -38,15 +38,17 @@ class AddressTableModel : public QAbstractTableModel /** Return status of edit/insert operation */ enum EditStatus { - OK, /**< Everything ok */ - NO_CHANGES, /**< No changes were made during edit operation */ - INVALID_ADDRESS, /**< Unparseable address */ - DUPLICATE_ADDRESS, /**< Address already in address book */ - INVALID_LEDGER_ACCOUNT, /**< Ledger account outside of recommended range */ - INVALID_LEDGER_INDEX, /**< Ledger index outside of recommended range */ - WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ - KEY_GENERATION_FAILURE, /**< Generating a new public key for a receiving address failed */ - LEDGER_ERROR /**< Ledger operation error */ + OK, /**< Everything ok */ + NO_CHANGES, /**< No changes were made during edit operation */ + INVALID_ADDRESS, /**< Unparseable address */ + DUPLICATE_ADDRESS, /**< Address already in address book */ + INVALID_LEDGER_ACCOUNT, /**< Ledger account outside of recommended range */ + INVALID_LEDGER_INDEX, /**< Ledger index outside of recommended range */ + WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ + KEY_GENERATION_FAILURE, /**< Generating a new public key for a receiving address failed */ + LABEL_USED_BY_LEDGER, /**< Using this label would break Ledger's unique label requirement */ + LABEL_NOT_USABLE_FOR_LEDGER, /**< Ledger address requires a fresh, unique label */ + LEDGER_ERROR /**< Ledger operation error */ }; static const QString Send; /**< Specifies send address */ @@ -87,6 +89,10 @@ class AddressTableModel : public QAbstractTableModel bool isWhitelisted(const std::string& address) const; + bool isLabelUsedByLedger(const QString& label); + bool isLabelUsableForLedger(const QString& label); + bool checkLabelAvailability(const QString& label, bool isLedgerAddress); + private: WalletModel* walletModel; CWallet* wallet; diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 6de1dc134..56c356976 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -195,6 +195,16 @@ void EditAddressDialog::accept() tr("New key generation failed."), QMessageBox::Ok, QMessageBox::Ok); break; + case AddressTableModel::LABEL_USED_BY_LEDGER: + QMessageBox::critical(this, windowTitle(), + tr("This label is already used by a Ledger address."), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::LABEL_NOT_USABLE_FOR_LEDGER: + QMessageBox::critical(this, windowTitle(), + tr("Ledger addresses require a unique label."), + QMessageBox::Ok, QMessageBox::Ok); + break; case AddressTableModel::LEDGER_ERROR: QMessageBox::critical(this, windowTitle(), tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(model->getLedgerErrorMessage())), diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index c8475a335..de20225a2 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2895,6 +2895,17 @@ bool CWallet::HasAddressBookEntry(const CTxDestination& address) const return mapAddressBook.exists(address); } +bool CWallet::GetAddressBookEntryByLabel(const std::string& label, CTxDestination& addressOut) const +{ + for (const auto& item : mapAddressBook.getInternalMap()) { + if (item.second.name == label) { + addressOut = item.first; + return true; + } + } + return false; +} + bool CWallet::HasDelegator(const ITxDB& txdb, const CTxOut& out) const { CTxDestination delegator; diff --git a/wallet/wallet.h b/wallet/wallet.h index a21538277..2a10cc450 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -422,6 +422,7 @@ class CWallet : public CCryptoKeyStore bool HasDelegator(const ITxDB& txdb, const CTxOut& out) const; bool HasAddressBookEntry(const CTxDestination& address) const; + bool GetAddressBookEntryByLabel(const std::string& label, CTxDestination& addressOut) const; /// getNewAddress functions throw std::runtime error on failure CBitcoinAddress getNewAddress(const std::string& addressLabel, const std::string& purpose); @@ -513,7 +514,7 @@ class CWallet : public CCryptoKeyStore [&key](const std::pair& entry) { auto candidateKey = entry.second; return candidateKey.isChange != key.isChange - && candidateKey.accountPubKeyID == key.accountPubKeyID + && candidateKey.accountPubKeyID == key.accountPubKeyID && candidateKey.index == key.index; }); From 35861057d989a6f95c3a0ee9703d7d1520a70dcf Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 6 Apr 2023 19:05:16 +0200 Subject: [PATCH 068/129] Disable message singing button with ledger address --- wallet/qt/addressbookpage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index ae0dbab5a..1efdc6010 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -267,6 +267,8 @@ void AddressBookPage::selectionChanged() if(table->selectionModel()->hasSelection()) { + QModelIndexList ledgerColumn = table->selectionModel()->selectedRows(AddressTableModel::IsLedger); + bool isLedger = ledgerColumn.size() && ledgerColumn[0].data().toBool(); switch(tab) { case SendingTab: @@ -280,7 +282,7 @@ void AddressBookPage::selectionChanged() // Deleting receiving addresses, however, is not allowed ui->deleteButton->setEnabled(false); deleteAction->setEnabled(false); - ui->signMessage->setEnabled(true); + ui->signMessage->setEnabled(!isLedger); ui->verifyMessage->setEnabled(false); break; case LedgerTab: From 8184644cc26776b7d90a9db5c6df6448083227f5 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 6 Apr 2023 19:15:17 +0200 Subject: [PATCH 069/129] Disable spending delegated coins checkbox --- wallet/qt/sendcoinsdialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 1bc5f621d..6acd2094f 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -148,10 +148,13 @@ void SendCoinsDialog::on_ledgerCheckBox_toggled(bool checked) if (checked) { ui->ledgerWidget->setVisible(true); ui->editMetadataButton->setDisabled(true); + ui->allowSpendingDelegatedCoins->setDisabled(true); + ui->allowSpendingDelegatedCoins->setChecked(false); ui->sendButton->setText(tr("Sign and s&end")); } else { ui->ledgerWidget->setVisible(false); ui->editMetadataButton->setDisabled(false); + ui->allowSpendingDelegatedCoins->setDisabled(false); ui->sendButton->setText(tr("S&end")); } } From c4f84c0034d81797a1ed532a4ea83d861eb71bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 7 Apr 2023 08:06:47 +0200 Subject: [PATCH 070/129] Remove `PrintHex` function --- wallet/ledger/utils.cpp | 10 ---------- wallet/ledger/utils.h | 1 - 2 files changed, 11 deletions(-) diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index dfdcee9b3..16c9113f5 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -68,16 +68,6 @@ namespace ledger::utils return ss.str(); } - void PrintHex(const bytes &vec) - { - for (int i = 0; i < vec.size(); i++) - { - std::cout << std::hex << std::setfill('0') << std::setw(2) << (int)vec[i]; - } - - std::cout << std::dec << std::endl; - } - bytes HexToBytes(const std::string &data) { std::stringstream ss; diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index 29c0874e9..3d09ee338 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -12,7 +12,6 @@ namespace ledger::utils std::tuple DeserializeVarint(const bytes &data, uint32_t offset); bytes CreateVarint(uint32_t value); std::string BytesToHex(const bytes &vec); - void PrintHex(const bytes &vec); bytes HexToBytes(const std::string &data); uint64_t BytesToUint64(const bytes &bytes, bool littleEndian = false); int BytesToInt(const bytes &bytes, bool littleEndian = false); From 142f373c02ad422fb33ec1459a69c00c3e1cad5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Thu, 6 Apr 2023 14:53:46 +0200 Subject: [PATCH 071/129] Fix ledger::utils namespace --- wallet/ledger/bip32.cpp | 11 +++-- wallet/ledger/hid.cpp | 10 ++--- wallet/ledger/ledger.cpp | 66 +++++++++++++++--------------- wallet/ledger/speculos.cpp | 8 ++-- wallet/ledger/tx.cpp | 72 ++++++++++++++++----------------- wallet/ledger/utils.cpp | 4 +- wallet/ledger/utils.h | 4 +- wallet/ledgerBridge.cpp | 2 +- wallet/qt/addresstablemodel.cpp | 4 +- wallet/qt/editaddressdialog.cpp | 8 ++-- 10 files changed, 94 insertions(+), 95 deletions(-) diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index 919568ab2..dc4a81101 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -67,7 +67,6 @@ namespace ledger throw std::runtime_error("Invalid keypath"); } - // utils::AppendUint32(keypath, std::stoul(item) | path); _components.push_back(std::stoul(item) | path); first = false; @@ -150,14 +149,14 @@ namespace ledger { bytes serializedKeyPath; - utils::AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); - utils::AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); - utils::AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); + AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); + AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); + AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); if (type == Bip32PathType::Address) { - utils::AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); - utils::AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); + AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); + AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); } return serializedKeyPath; diff --git a/wallet/ledger/hid.cpp b/wallet/ledger/hid.cpp index 240de1ec5..0f99c6cfa 100644 --- a/wallet/ledger/hid.cpp +++ b/wallet/ledger/hid.cpp @@ -34,7 +34,7 @@ namespace ledger if (data.empty()) return -1; - auto dataNew = utils::IntToBytes(data.size(), 2); + auto dataNew = IntToBytes(data.size(), 2); dataNew.insert(dataNew.end(), data.begin(), data.end()); size_t offset = 0; @@ -46,7 +46,7 @@ namespace ledger // Header: channel (0x0101), tag (0x05), sequence index bytes header{0x01, 0x01, 0x05}; - auto seqIdxBytes = utils::IntToBytes(seqIdx, 2); + auto seqIdxBytes = IntToBytes(seqIdx, 2); header.insert(header.end(), seqIdxBytes.begin(), seqIdxBytes.end()); bytes::iterator it; @@ -90,11 +90,11 @@ namespace ledger assert(dataChunk[1] == 0x01); assert(dataChunk[2] == 0x05); - auto seqIdxBytes = utils::IntToBytes(seqIdx, 2); + auto seqIdxBytes = IntToBytes(seqIdx, 2); assert(seqIdxBytes[0] == dataChunk[3]); assert(seqIdxBytes[1] == dataChunk[4]); - auto dataLen = utils::BytesToInt(bytes(dataChunk.begin() + 5, dataChunk.begin() + 7)); + auto dataLen = BytesToInt(bytes(dataChunk.begin() + 5, dataChunk.begin() + 7)); bytes data(dataChunk.begin() + 7, dataChunk.end()); while (data.size() < dataLen) @@ -106,7 +106,7 @@ namespace ledger data.insert(data.end(), tmp.begin() + 5, tmp.end()); } - auto sw = utils::BytesToInt(bytes(data.begin() + dataLen - 2, data.begin() + dataLen)); + auto sw = BytesToInt(bytes(data.begin() + dataLen - 2, data.begin() + dataLen)); rdata = bytes(data.begin(), data.begin() + dataLen - 2); return sw; diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index c17491577..3d926b95c 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -24,22 +24,22 @@ namespace ledger auto pathBytes = path.Serialize(); payload.push_back(pathBytes.size() / 4); - utils::AppendVector(payload, pathBytes); + AppendVector(payload, pathBytes); // 0x00 = P2_LEGACY (base58) auto buffer = transport->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); auto offset = 1; auto pubKeyLen = (int)buffer[offset] * 16 + 1; - auto pubKey = utils::Splice(buffer, offset, pubKeyLen); + auto pubKey = Splice(buffer, offset, pubKeyLen); offset += pubKeyLen; auto addressLen = (int)buffer[offset]; offset++; - auto address = utils::Splice(buffer, offset, addressLen); + auto address = Splice(buffer, offset, addressLen); offset += addressLen; - auto chainCode = utils::Splice(buffer, offset, 32); + auto chainCode = Splice(buffer, offset, 32); offset += 32; if (offset != buffer.size()) @@ -56,45 +56,45 @@ namespace ledger bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) { bytes firstRoundData; - utils::AppendUint32(firstRoundData, indexLookup); - utils::AppendUint32(firstRoundData, utxoTx.version, true); - utils::AppendUint32(firstRoundData, utxoTx.time, true); - utils::AppendVector(firstRoundData, utils::CreateVarint(utxoTx.inputs.size())); + AppendUint32(firstRoundData, indexLookup); + AppendUint32(firstRoundData, utxoTx.version, true); + AppendUint32(firstRoundData, utxoTx.time, true); + AppendVector(firstRoundData, CreateVarint(utxoTx.inputs.size())); GetTrustedInputRaw(true, firstRoundData); for (auto input : utxoTx.inputs) { bytes inputData; - utils::AppendVector(inputData, input.prevout.hash); - utils::AppendUint32(inputData, input.prevout.index, true); - utils::AppendVector(inputData, utils::CreateVarint(input.script.size())); + AppendVector(inputData, input.prevout.hash); + AppendUint32(inputData, input.prevout.index, true); + AppendVector(inputData, CreateVarint(input.script.size())); GetTrustedInputRaw(false, inputData); bytes inputScriptData; - utils::AppendVector(inputScriptData, input.script); - utils::AppendUint32(inputScriptData, input.sequence); + AppendVector(inputScriptData, input.script); + AppendUint32(inputScriptData, input.sequence); GetTrustedInputRaw(false, inputScriptData); } - GetTrustedInputRaw(false, utils::CreateVarint(utxoTx.outputs.size())); + GetTrustedInputRaw(false, CreateVarint(utxoTx.outputs.size())); for (auto output : utxoTx.outputs) { bytes outputData; - utils::AppendUint64(outputData, output.amount, true); - utils::AppendVector(outputData, utils::CreateVarint(output.script.size())); + AppendUint64(outputData, output.amount, true); + AppendVector(outputData, CreateVarint(output.script.size())); GetTrustedInputRaw(false, outputData); bytes outputScriptData; - utils::AppendVector(outputScriptData, output.script); + AppendVector(outputScriptData, output.script); GetTrustedInputRaw(false, outputScriptData); } - return GetTrustedInputRaw(false, utils::IntToBytes(utxoTx.locktime, 4)); + return GetTrustedInputRaw(false, IntToBytes(utxoTx.locktime, 4)); } void Ledger::UntrustedHashTxInputFinalize(const Tx &tx, bool hasChange, const Bip32Path changePath) @@ -109,7 +109,7 @@ namespace ledger bytes changePathData; changePathData.push_back(serializedChangePath.size() / 4); - utils::AppendVector(changePathData, serializedChangePath); + AppendVector(changePathData, serializedChangePath); transport->exchange(APDU::CLA, ins, p1, p2, changePathData); } @@ -119,7 +119,7 @@ namespace ledger } p1 = 0x00; - transport->exchange(APDU::CLA, ins, p1, p2, utils::CreateVarint(tx.outputs.size())); + transport->exchange(APDU::CLA, ins, p1, p2, CreateVarint(tx.outputs.size())); for (auto i = 0; i < tx.outputs.size(); i++) { @@ -127,9 +127,9 @@ namespace ledger auto output = tx.outputs[i]; bytes outputData; - utils::AppendUint64(outputData, output.amount, true); - utils::AppendVector(outputData, utils::CreateVarint(output.script.size())); - utils::AppendVector(outputData, output.script); + AppendUint64(outputData, output.amount, true); + AppendVector(outputData, CreateVarint(output.script.size())); + AppendVector(outputData, output.script); transport->exchange(APDU::CLA, ins, p1, p2, outputData); } @@ -142,9 +142,9 @@ namespace ledger auto p2 = isNewTransaction ? 0x00 : 0x80; bytes data; - utils::AppendUint32(data, tx.version, true); - utils::AppendUint32(data, tx.time, true); - utils::AppendVector(data, utils::CreateVarint(trustedInputs.size())); + AppendUint32(data, tx.version, true); + AppendUint32(data, tx.time, true); + AppendVector(data, CreateVarint(trustedInputs.size())); transport->exchange(APDU::CLA, ins, p1, p2, data); @@ -157,14 +157,14 @@ namespace ledger bytes _data; _data.push_back(0x01); _data.push_back(trustedInput.serialized.size()); - utils::AppendVector(_data, trustedInput.serialized); - utils::AppendVector(_data, utils::CreateVarint(_script.size())); + AppendVector(_data, trustedInput.serialized); + AppendVector(_data, CreateVarint(_script.size())); transport->exchange(APDU::CLA, ins, p1, p2, _data); bytes scriptData; - utils::AppendVector(scriptData, _script); - utils::AppendUint32(scriptData, 0xffffffff, true); + AppendVector(scriptData, _script); + AppendUint32(scriptData, 0xffffffff, true); transport->exchange(APDU::CLA, ins, p1, p2, scriptData); } @@ -205,9 +205,9 @@ namespace ledger bytes data; data.push_back(serializedSignPath.size() / 4); - utils::AppendVector(data, serializedSignPath); + AppendVector(data, serializedSignPath); data.push_back(0x00); - utils::AppendUint32(data, tx.locktime); + AppendUint32(data, tx.locktime); data.push_back(0x01); auto buffer = transport->exchange(APDU::CLA, ins, p1, p2, data); @@ -215,7 +215,7 @@ namespace ledger { bytes data; data.push_back(0x30); - utils::AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); + AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); signatures.push_back({1, data}); } else diff --git a/wallet/ledger/speculos.cpp b/wallet/ledger/speculos.cpp index 84112f9e2..55aa75e68 100644 --- a/wallet/ledger/speculos.cpp +++ b/wallet/ledger/speculos.cpp @@ -43,8 +43,8 @@ namespace ledger int Speculos::send(const bytes &data) { bytes d; - utils::AppendUint32(d, data.size()); - utils::AppendVector(d, data); + AppendUint32(d, data.size()); + AppendVector(d, data); auto result = write(sockfd, d.data(), d.size()); if (result == -1) return -1; @@ -57,7 +57,7 @@ namespace ledger if (read(sockfd, lengthB, sizeof(lengthB)) == -1) return -1; - auto length = utils::BytesToInt(bytes(lengthB, lengthB + sizeof(lengthB))); + auto length = BytesToInt(bytes(lengthB, lengthB + sizeof(lengthB))); uint8_t d[length]; if (read(sockfd, d, sizeof(d)) == -1) return -1; @@ -67,7 +67,7 @@ namespace ledger return -1; rdata = bytes(d, d + sizeof(d)); - return utils::BytesToInt(bytes(sw, sw + sizeof(sw))); + return BytesToInt(bytes(sw, sw + sizeof(sw))); } void Speculos::close() noexcept diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index c742f2630..ccc719aeb 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -6,28 +6,28 @@ namespace ledger { bytes SerializeTransaction(const Tx& tx) { bytes serializedTransaction; - utils::AppendUint32(serializedTransaction, tx.version, true); - utils::AppendUint32(serializedTransaction, tx.time, true); + AppendUint32(serializedTransaction, tx.version, true); + AppendUint32(serializedTransaction, tx.time, true); - utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.inputs.size())); + AppendVector(serializedTransaction, CreateVarint(tx.inputs.size())); for (auto input : tx.inputs) { - utils::AppendVector(serializedTransaction, input.prevout.hash); - utils::AppendUint32(serializedTransaction, input.prevout.index, true); - utils::AppendVector(serializedTransaction, utils::CreateVarint(input.script.size())); - utils::AppendVector(serializedTransaction, input.script); - utils::AppendUint32(serializedTransaction, input.sequence); + AppendVector(serializedTransaction, input.prevout.hash); + AppendUint32(serializedTransaction, input.prevout.index, true); + AppendVector(serializedTransaction, CreateVarint(input.script.size())); + AppendVector(serializedTransaction, input.script); + AppendUint32(serializedTransaction, input.sequence); } - utils::AppendVector(serializedTransaction, utils::CreateVarint(tx.outputs.size())); + AppendVector(serializedTransaction, CreateVarint(tx.outputs.size())); for (auto output : tx.outputs) { - utils::AppendUint64(serializedTransaction, output.amount, true); - utils::AppendVector(serializedTransaction, utils::CreateVarint(output.script.size())); - utils::AppendVector(serializedTransaction, output.script); + AppendUint64(serializedTransaction, output.amount, true); + AppendVector(serializedTransaction, CreateVarint(output.script.size())); + AppendVector(serializedTransaction, output.script); } - utils::AppendUint32(serializedTransaction, tx.locktime); + AppendUint32(serializedTransaction, tx.locktime); return serializedTransaction; } @@ -39,22 +39,22 @@ Tx DeserializeTransaction(const bytes& transaction) auto offset = 0; - tx.version = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); + tx.version = BytesToInt(Splice(transaction, offset, 4), true); offset += 4; - tx.time = utils::BytesToInt(utils::Splice(transaction, offset, 4), true); + tx.time = BytesToInt(Splice(transaction, offset, 4), true); offset += 4; - auto varint = utils::DeserializeVarint(transaction, offset); + auto varint = DeserializeVarint(transaction, offset); auto inputsCount = std::get<0>(varint); offset += std::get<1>(varint); auto flags = 0; if (inputsCount == 0) { - flags = utils::BytesToInt(utils::Splice(transaction, offset, 1)); + flags = BytesToInt(Splice(transaction, offset, 1)); offset += 1; - varint = utils::DeserializeVarint(transaction, offset); + varint = DeserializeVarint(transaction, offset); inputsCount = std::get<0>(varint); offset += std::get<1>(varint); } @@ -63,38 +63,38 @@ Tx DeserializeTransaction(const bytes& transaction) TxInput input; TxPrevout prevout; - prevout.hash = utils::Splice(transaction, offset, 32); + prevout.hash = Splice(transaction, offset, 32); offset += 32; - prevout.index = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + prevout.index = BytesToInt(Splice(transaction, offset, 4)); offset += 4; input.prevout = prevout; - varint = utils::DeserializeVarint(transaction, offset); + varint = DeserializeVarint(transaction, offset); offset += std::get<1>(varint); - input.script = utils::Splice(transaction, offset, std::get<0>(varint)); + input.script = Splice(transaction, offset, std::get<0>(varint)); offset += std::get<0>(varint); - input.sequence = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + input.sequence = BytesToInt(Splice(transaction, offset, 4)); offset += 4; tx.inputs.push_back(input); } - varint = utils::DeserializeVarint(transaction, offset); + varint = DeserializeVarint(transaction, offset); auto numberOutputs = std::get<0>(varint); offset += std::get<1>(varint); for (auto i = 0; i < numberOutputs; i++) { TxOutput output; - output.amount = utils::BytesToUint64(utils::Splice(transaction, offset, 8), true); + output.amount = BytesToUint64(Splice(transaction, offset, 8), true); offset += 8; - varint = utils::DeserializeVarint(transaction, offset); + varint = DeserializeVarint(transaction, offset); offset += std::get<1>(varint); - output.script = utils::Splice(transaction, offset, std::get<0>(varint)); + output.script = Splice(transaction, offset, std::get<0>(varint)); offset += std::get<0>(varint); tx.outputs.push_back(output); @@ -103,13 +103,13 @@ Tx DeserializeTransaction(const bytes& transaction) if (flags != 0) { TxWitness txWitness; for (auto i = 0; i < inputsCount; i++) { - auto numberOfWitnesses = utils::DeserializeVarint(transaction, offset); + auto numberOfWitnesses = DeserializeVarint(transaction, offset); offset += std::get<1>(numberOfWitnesses); TxInWitness txInWitness; ScriptWitness scriptWitness; for (auto j = 0; j < std::get<0>(numberOfWitnesses); j++) { - auto scriptWitnessSize = utils::DeserializeVarint(transaction, offset); + auto scriptWitnessSize = DeserializeVarint(transaction, offset); offset += std::get<1>(scriptWitnessSize); scriptWitness.stack.push_back( bytes(transaction.begin() + offset, @@ -122,7 +122,7 @@ Tx DeserializeTransaction(const bytes& transaction) } } - tx.locktime = utils::BytesToInt(utils::Splice(transaction, offset, 4)); + tx.locktime = BytesToInt(Splice(transaction, offset, 4)); return tx; } @@ -131,7 +131,7 @@ Tx DeserializeTransaction(const bytes& transaction) { TrustedInput trustedInput; - utils::AppendVector(trustedInput.serialized, serializedTrustedInput); + AppendVector(trustedInput.serialized, serializedTrustedInput); auto offset = 0; @@ -145,19 +145,19 @@ Tx DeserializeTransaction(const bytes& transaction) throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); offset += 1; - trustedInput.random = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 2)); + trustedInput.random = BytesToInt(Splice(serializedTrustedInput, offset, 2)); offset += 2; - trustedInput.prevTxId = utils::Splice(serializedTrustedInput, offset, 32); + trustedInput.prevTxId = Splice(serializedTrustedInput, offset, 32); offset += 32; - trustedInput.outIndex = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 4), true); + trustedInput.outIndex = BytesToInt(Splice(serializedTrustedInput, offset, 4), true); offset += 4; - trustedInput.amount = utils::BytesToInt(utils::Splice(serializedTrustedInput, offset, 8), true); + trustedInput.amount = BytesToInt(Splice(serializedTrustedInput, offset, 8), true); offset += 8; - trustedInput.hmac = utils::Splice(serializedTrustedInput, offset, 8); + trustedInput.hmac = Splice(serializedTrustedInput, offset, 8); offset += 8; if (offset != serializedTrustedInput.size()) diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index 16c9113f5..772c3195b 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -6,7 +6,7 @@ #include #include -namespace ledger::utils +namespace ledger { std::tuple DeserializeVarint(const bytes &data, uint32_t offset) { @@ -190,4 +190,4 @@ namespace ledger::utils return compressedPubKey; } -} // namespace ledger::utils +} // namespace ledger diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index 3d09ee338..bf0b706a2 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -7,7 +7,7 @@ #include #include -namespace ledger::utils +namespace ledger { std::tuple DeserializeVarint(const bytes &data, uint32_t offset); bytes CreateVarint(uint32_t value); @@ -29,6 +29,6 @@ namespace ledger::utils const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; const uint32_t MAX_RECOMMENDED_INDEX = 50000; -} // namespace ledger::utils +} // namespace ledger #endif diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index bf8b37ef1..0dd6e61dd 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -20,7 +20,7 @@ namespace ledgerbridge { auto result = ledger.GetPublicKey(path, display); - return ledger::utils::CompressPubKey(std::get<0>(result)); + return ledger::CompressPubKey(std::get<0>(result)); } ledger::bytes LedgerBridge::GetPublicKey(const ledger::Bip32Path path, bool display) diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 67fffb6c1..2274fe0ba 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -461,14 +461,14 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } else if (type == ReceiveLedger) { bool accountOk = true; uint32_t account = ledgerAccount.toUInt(&accountOk); - if (!accountOk || !validateLedgerPathItem(account, ledger::utils::MAX_RECOMMENDED_ACCOUNT)) { + if (!accountOk || !validateLedgerPathItem(account, ledger::MAX_RECOMMENDED_ACCOUNT)) { editStatus = INVALID_LEDGER_ACCOUNT; return QString(); } bool indexOk = true; uint32_t index = ledgerIndex.toUInt(&indexOk); - if (!indexOk || !validateLedgerPathItem(index, ledger::utils::MAX_RECOMMENDED_INDEX)) { + if (!indexOk || !validateLedgerPathItem(index, ledger::MAX_RECOMMENDED_INDEX)) { editStatus = INVALID_LEDGER_INDEX; return QString(); } diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 56c356976..54f121907 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -71,8 +71,8 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : // Ledger account and index defaults and validators ui->ledgerAccountEdit->setText("0"); ui->ledgerIndexEdit->setText("0"); - GUIUtil::setupIntWidget(ui->ledgerAccountEdit, this, 0, ledger::utils::MAX_RECOMMENDED_ACCOUNT); - GUIUtil::setupIntWidget(ui->ledgerIndexEdit, this, 0, ledger::utils::MAX_RECOMMENDED_INDEX); + GUIUtil::setupIntWidget(ui->ledgerAccountEdit, this, 0, ledger::MAX_RECOMMENDED_ACCOUNT); + GUIUtil::setupIntWidget(ui->ledgerIndexEdit, this, 0, ledger::MAX_RECOMMENDED_INDEX); connect(this->ui->ledgerAccountEdit, SIGNAL(textChanged(QString)), this, SLOT(updateLedgerPathLabel())); connect(this->ui->ledgerIndexEdit, SIGNAL(textChanged(QString)), this, SLOT(updateLedgerPathLabel())); @@ -177,12 +177,12 @@ void EditAddressDialog::accept() break; case AddressTableModel::INVALID_LEDGER_ACCOUNT: QMessageBox::warning(this, windowTitle(), - tr("The entered Ledger account \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerAccountEdit->text()).arg(ledger::utils::MAX_RECOMMENDED_ACCOUNT), + tr("The entered Ledger account \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerAccountEdit->text()).arg(ledger::MAX_RECOMMENDED_ACCOUNT), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::INVALID_LEDGER_INDEX: QMessageBox::warning(this, windowTitle(), - tr("The entered Ledger address index \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerIndexEdit->text()).arg(ledger::utils::MAX_RECOMMENDED_INDEX), + tr("The entered Ledger address index \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerIndexEdit->text()).arg(ledger::MAX_RECOMMENDED_INDEX), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::WALLET_UNLOCK_FAILURE: From 8eaa43506e7262906abf84453f3a76ae354a23e0 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 11 Apr 2023 15:02:49 +0200 Subject: [PATCH 072/129] Add Verify Address feature --- wallet/ledger/error.cpp | 12 ++++- wallet/ledger/error.h | 4 +- wallet/ledger/messagebox.cpp | 10 ++-- wallet/ledger/messagebox.h | 2 +- wallet/qt/addressbookpage.cpp | 93 +++++++++++++++++++++++++++++++-- wallet/qt/addressbookpage.h | 20 +++++++ wallet/qt/addresstablemodel.cpp | 7 --- wallet/qt/addresstablemodel.h | 2 +- wallet/qt/bitcoin.qrc | 1 + wallet/qt/editaddressdialog.cpp | 3 +- wallet/qt/ui_addressbookpage.h | 19 +++++-- 11 files changed, 149 insertions(+), 24 deletions(-) diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index f32da6bd0..5e76ea735 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -1,11 +1,14 @@ #include "error.h" +#include +#include + namespace ledger -{ +{ LedgerException::LedgerException(ErrorCode errorCodeIn) : errorCode(errorCodeIn) {} ErrorCode LedgerException::GetErrorCode() const { return errorCode; } - + const char *LedgerException::what() const noexcept { return GetMessage().c_str(); } @@ -14,4 +17,9 @@ namespace ledger { return LedgerException::GetMessage(errorCode); } + + QString LedgerException::GetQtMessage() const + { + return QObject::tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(GetMessage())); + } } diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index d9214d780..d89c096d6 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace ledger { enum class ErrorCode { @@ -42,7 +43,8 @@ namespace ledger { ErrorCode GetErrorCode() const; std::string GetMessage() const; - + QString GetQtMessage() const; + const char *what() const noexcept override; private: ErrorCode errorCode; diff --git a/wallet/ledger/messagebox.cpp b/wallet/ledger/messagebox.cpp index cbffadfb5..971fc5008 100644 --- a/wallet/ledger/messagebox.cpp +++ b/wallet/ledger/messagebox.cpp @@ -6,15 +6,19 @@ namespace ledger { - MessageBox::MessageBox(QWidget *parent, QSharedPointer worker) : worker_(worker), msgBox_(parent) + MessageBox::MessageBox(QWidget *parent, QSharedPointer worker, const QString &text) : worker_(worker), msgBox_(parent) { worker_->moveToThread(&thread_); - connect(&thread_, SIGNAL(finished()), &thread_, SLOT(deleteLater())); thread_.start(); msgBox_.setIcon(QMessageBox::Icon::Information); msgBox_.setWindowTitle(parent->windowTitle()); - msgBox_.setText("Please confirm or cancel the action on your Ledger device."); + if (text.isEmpty()) { + msgBox_.setText("Please confirm or cancel the action on your Ledger device."); + } else { + msgBox_.setText(text); + msgBox_.setInformativeText("Please confirm or cancel the action on your Ledger device."); + } msgBox_.setStandardButtons(QMessageBox::StandardButton::NoButton); } diff --git a/wallet/ledger/messagebox.h b/wallet/ledger/messagebox.h index e3ffcd3b4..b0901b506 100644 --- a/wallet/ledger/messagebox.h +++ b/wallet/ledger/messagebox.h @@ -14,7 +14,7 @@ namespace ledger Q_OBJECT public: - MessageBox(QWidget *parent, QSharedPointer worker); + MessageBox(QWidget *parent, QSharedPointer worker, const QString &text = QString()); void exec(); public slots: diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index 1efdc6010..928609524 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -7,6 +7,10 @@ #include "editaddressdialog.h" #include "csvmodelwriter.h" #include "guiutil.h" +#include "ledger/bip32.h" +#include "ledger/error.h" +#include "ledger/messagebox.h" +#include "ledgerBridge.h" #include #include @@ -17,6 +21,20 @@ #include "qrcodedialog.h" #endif +void VerifyLedgerAddressWorker::verify(uint32_t account, uint32_t index, QSharedPointer workerPtr) { + ledger::bytes paymentPubKeyBytes; + QString errorMessage; + try { + ledgerbridge::LedgerBridge ledgerBridge; + paymentPubKeyBytes = ledgerBridge.GetPublicKey(account, false, index, true); + } catch (const ledger::LedgerException& e) { + errorMessage = e.GetQtMessage(); + } + + emit resultReady(errorMessage); + workerPtr.reset(); +} + AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : QDialog(parent), ui(new Ui::AddressBookPage), @@ -55,16 +73,19 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : ui->deleteButton->setVisible(true); ui->signMessage->setVisible(false); ui->verifyMessage->setVisible(true); + ui->verifyAddress->setVisible(false); break; case ReceivingTab: ui->deleteButton->setVisible(false); ui->signMessage->setVisible(true); ui->verifyMessage->setVisible(false); + ui->verifyAddress->setVisible(true); break; case LedgerTab: ui->deleteButton->setVisible(false); ui->signMessage->setVisible(false); ui->verifyMessage->setVisible(false); + ui->verifyAddress->setVisible(true); break; } @@ -73,8 +94,9 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : QAction *copyAddressAction = new QAction(ui->copyToClipboard->text(), this); QAction *editAction = new QAction(tr("&Edit"), this); QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this); - QAction *signMessageAction = new QAction(ui->signMessage->text(), this); - QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this); + signMessageAction = new QAction(ui->signMessage->text(), this); + verifyMessageAction = new QAction(ui->verifyMessage->text(), this); + verifyAddressAction = new QAction(ui->verifyAddress->text(), this); deleteAction = new QAction(ui->deleteButton->text(), this); // Build context menu @@ -84,14 +106,25 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : contextMenu->addAction(editAction); if(tabIn == SendingTab) contextMenu->addAction(deleteAction); + contextMenu->addSeparator(); + #ifdef USE_QRCODE contextMenu->addAction(showQRCodeAction); #endif - if(tabIn == ReceivingTab) - contextMenu->addAction(signMessageAction); - else if(tabIn == SendingTab) + switch(tabIn) + { + case SendingTab: contextMenu->addAction(verifyMessageAction); + break; + case ReceivingTab: + contextMenu->addAction(signMessageAction); + contextMenu->addAction(verifyAddressAction); + break; + case LedgerTab: + contextMenu->addAction(verifyAddressAction); + break; + } // Connect signals for context menu actions connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboard_clicked())); @@ -101,6 +134,7 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked())); connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked())); connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked())); + connect(verifyAddressAction, SIGNAL(triggered()), this, SLOT(on_verifyAddress_clicked())); connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); @@ -231,6 +265,42 @@ void AddressBookPage::on_verifyMessage_clicked() emit verifyMessage(addr); } +void AddressBookPage::on_verifyAddress_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + QString ledgerAddress; + uint32_t ledgerAccount; + uint32_t ledgerIndex; + + foreach (QModelIndex index, indexes) + { + ledgerAddress = index.data().toString(); + ledgerAccount = index.sibling(index.row(), AddressTableModel::LedgerAccount).data().toInt(); + ledgerIndex = index.sibling(index.row(), AddressTableModel::LedgerIndex).data().toInt(); + } + + CKeyID ledgerKeyId; + if (!CBitcoinAddress(ledgerAddress.toStdString()).GetKeyID(ledgerKeyId)) { + // Should be unreachable + return; + } + + QSharedPointer worker = QSharedPointer::create(); + ledger::MessageBox msgBox(this, worker, tr("Verifying address: %1").arg(ledgerAddress)); + connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(showVerifyAddressResult(QString))); + connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(quit())); + QTimer::singleShot(0, worker.data(), [worker, ledgerAccount, ledgerIndex]() { worker->verify(ledgerAccount, ledgerIndex, worker); }); + msgBox.exec(); +} + +void AddressBookPage::showVerifyAddressResult(QString errorMessage) +{ + if (!errorMessage.isEmpty()) { + QMessageBox::critical(this, windowTitle(), errorMessage, QMessageBox::Ok, QMessageBox::Ok); + } +} + void AddressBookPage::on_newAddressButton_clicked() { if(!model) @@ -276,20 +346,32 @@ void AddressBookPage::selectionChanged() ui->deleteButton->setEnabled(true); deleteAction->setEnabled(true); ui->signMessage->setEnabled(false); + signMessageAction->setEnabled(false); ui->verifyMessage->setEnabled(true); + verifyMessageAction->setEnabled(true); + ui->verifyAddress->setEnabled(false); + verifyAddressAction->setEnabled(false); break; case ReceivingTab: // Deleting receiving addresses, however, is not allowed ui->deleteButton->setEnabled(false); deleteAction->setEnabled(false); ui->signMessage->setEnabled(!isLedger); + signMessageAction->setEnabled(!isLedger); ui->verifyMessage->setEnabled(false); + verifyMessageAction->setEnabled(false); + ui->verifyAddress->setEnabled(isLedger); + verifyAddressAction->setEnabled(isLedger); break; case LedgerTab: ui->deleteButton->setEnabled(false); deleteAction->setEnabled(false); ui->signMessage->setEnabled(false); + signMessageAction->setEnabled(false); ui->verifyMessage->setEnabled(false); + verifyMessageAction->setEnabled(false); + ui->verifyAddress->setEnabled(isLedger); + verifyAddressAction->setEnabled(isLedger); break; } ui->copyToClipboard->setEnabled(true); @@ -302,6 +384,7 @@ void AddressBookPage::selectionChanged() ui->copyToClipboard->setEnabled(false); ui->signMessage->setEnabled(false); ui->verifyMessage->setEnabled(false); + ui->verifyAddress->setEnabled(false); } } diff --git a/wallet/qt/addressbookpage.h b/wallet/qt/addressbookpage.h index a439f9f8b..a97b25bea 100644 --- a/wallet/qt/addressbookpage.h +++ b/wallet/qt/addressbookpage.h @@ -17,6 +17,21 @@ class QMenu; class QModelIndex; QT_END_NAMESPACE +/* Object for verifying a Ledger address in a separate thread. +*/ +class VerifyLedgerAddressWorker : public QObject +{ + Q_OBJECT + +public slots: + // we use the shared pointer argument to ensure that workerPtr will be deleted after doing the + // retrieval + void verify(uint32_t account, uint32_t index, QSharedPointer workerPtr); + +signals: + void resultReady(QString errorMessage); +}; + /** Widget that shows a list of sending or receiving addresses. */ class AddressBookPage : public QDialog @@ -57,6 +72,9 @@ public slots: QString returnLabel; QSortFilterProxyModel *proxyModel; QMenu *contextMenu; + QAction *signMessageAction; + QAction *verifyMessageAction; + QAction *verifyAddressAction; QAction *deleteAction; QString newAddressToSelect; @@ -67,6 +85,8 @@ private slots: void on_copyToClipboard_clicked(); void on_signMessage_clicked(); void on_verifyMessage_clicked(); + void on_verifyAddress_clicked(); + void showVerifyAddressResult(QString errorMessage); void selectionChanged(); void on_showQRCode_clicked(); /** Spawn contextual menu (right mouse menu) for address book entry */ diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 2274fe0ba..a04445220 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -555,13 +555,6 @@ int AddressTableModel::lookupAddress(const QString& address) const } } -std::string AddressTableModel::getLedgerErrorMessage() const { - if (editStatus != LEDGER_ERROR) - return ""; - - return ledger::LedgerException::GetMessage(ledgerError); -} - void AddressTableModel::emitDataChanged(int idx) { emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length() - 1, QModelIndex())); diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index 113895dbf..df3ad0b59 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -83,7 +83,7 @@ class AddressTableModel : public QAbstractTableModel EditStatus getEditStatus() const { return editStatus; } - std::string getLedgerErrorMessage() const; + ledger::ErrorCode getLedgerError() const { return ledgerError; } std::string purposeForAddress(const std::string& address) const; diff --git a/wallet/qt/bitcoin.qrc b/wallet/qt/bitcoin.qrc index d7f3c9f74..c219d5a2a 100644 --- a/wallet/qt/bitcoin.qrc +++ b/wallet/qt/bitcoin.qrc @@ -67,6 +67,7 @@ res/icons/plus-96.png res/icons/minus-96.png res/icons/true-false-96.png + res/icons/eye.png res/images/about.png diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index 54f121907..d01ccb454 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -3,6 +3,7 @@ #include "addresstablemodel.h" #include "guiutil.h" #include "ledger/bip32.h" +#include "ledger/error.h" #include #include "ledger/utils.h" @@ -207,7 +208,7 @@ void EditAddressDialog::accept() break; case AddressTableModel::LEDGER_ERROR: QMessageBox::critical(this, windowTitle(), - tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(model->getLedgerErrorMessage())), + ledger::LedgerException(model->getLedgerError()).GetQtMessage(), QMessageBox::Ok, QMessageBox::Ok); break; diff --git a/wallet/qt/ui_addressbookpage.h b/wallet/qt/ui_addressbookpage.h index 33a870593..46debe3be 100644 --- a/wallet/qt/ui_addressbookpage.h +++ b/wallet/qt/ui_addressbookpage.h @@ -37,6 +37,7 @@ class Ui_AddressBookPage QPushButton *showQRCode; QPushButton *signMessage; QPushButton *verifyMessage; + QPushButton *verifyAddress; QPushButton *deleteButton; QSpacerItem *horizontalSpacer; QDialogButtonBox *buttonBox; @@ -109,11 +110,19 @@ class Ui_AddressBookPage horizontalLayout->addWidget(verifyMessage); + verifyAddress = new QPushButton(AddressBookPage); + verifyAddress->setObjectName(QStringLiteral("verifyAddress")); + QIcon icon5; + icon5.addFile(QStringLiteral(":/icons/eye"), QSize(), QIcon::Normal, QIcon::Off); + verifyAddress->setIcon(icon5); + + horizontalLayout->addWidget(verifyAddress); + deleteButton = new QPushButton(AddressBookPage); deleteButton->setObjectName(QStringLiteral("deleteButton")); - QIcon icon5; - icon5.addFile(QStringLiteral(":/icons/remove"), QSize(), QIcon::Normal, QIcon::Off); - deleteButton->setIcon(icon5); + QIcon icon6; + icon6.addFile(QStringLiteral(":/icons/remove"), QSize(), QIcon::Normal, QIcon::Off); + deleteButton->setIcon(icon6); horizontalLayout->addWidget(deleteButton); @@ -165,6 +174,10 @@ class Ui_AddressBookPage verifyMessage->setToolTip(QApplication::translate("AddressBookPage", "Verify a message to ensure it was signed with a specified neblio address", Q_NULLPTR)); #endif // QT_NO_TOOLTIP verifyMessage->setText(QApplication::translate("AddressBookPage", "&Verify Message", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + verifyAddress->setToolTip(QApplication::translate("AddressBookPage", "Verify a neblio address on your Ledger device", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + verifyAddress->setText(QApplication::translate("AddressBookPage", "Verify &Address", Q_NULLPTR)); #ifndef QT_NO_TOOLTIP deleteButton->setToolTip(QApplication::translate("AddressBookPage", "Delete the currently selected address from the list", Q_NULLPTR)); #endif // QT_NO_TOOLTIP From 911c9add7368870b458f9a0919535566636e2135 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 11 Apr 2023 17:45:42 +0200 Subject: [PATCH 073/129] Move ledger ui helpers to qt folder --- wallet/ledger/bip32.cpp | 32 +++++++++---------- wallet/ledger/bip32.h | 8 ++--- wallet/ledger/comm.h | 2 +- wallet/ledger/error.cpp | 10 +----- wallet/ledger/error.h | 2 -- wallet/ledger/hid.cpp | 6 ++-- wallet/ledger/hid.h | 2 +- wallet/ledger/ledger.cpp | 12 +++---- wallet/ledger/ledger.h | 8 ++--- wallet/ledger/speculos.cpp | 6 ++-- wallet/ledger/speculos.h | 2 +- wallet/ledger/transport.cpp | 10 +++--- wallet/ledger/transport.h | 4 +-- wallet/ledger/tx.cpp | 8 ++--- wallet/ledger/tx.h | 4 +-- wallet/ledger/utils.cpp | 2 +- wallet/ledger/utils.h | 2 +- wallet/qt/addressbookpage.cpp | 7 ++-- wallet/qt/editaddressdialog.cpp | 7 ++-- .../ledger_ui/ledgermessagebox.cpp} | 10 +++--- .../ledger_ui/ledgermessagebox.h} | 10 +++--- wallet/qt/ledger_ui/ledgeruiutils.cpp | 12 +++++++ wallet/qt/ledger_ui/ledgeruiutils.h | 10 ++++++ wallet/qt/sendcoinsdialog.cpp | 4 +-- wallet/wallet.pri | 6 ++-- 25 files changed, 101 insertions(+), 85 deletions(-) rename wallet/{ledger/messagebox.cpp => qt/ledger_ui/ledgermessagebox.cpp} (74%) rename wallet/{ledger/messagebox.h => qt/ledger_ui/ledgermessagebox.h} (57%) create mode 100644 wallet/qt/ledger_ui/ledgeruiutils.cpp create mode 100644 wallet/qt/ledger_ui/ledgeruiutils.h diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index dc4a81101..a14c9e191 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -1,5 +1,5 @@ -#include "bip32.h" -#include "utils.h" +#include "ledger/bip32.h" +#include "ledger/utils.h" #include @@ -21,8 +21,8 @@ namespace ledger } Bip32Path Bip32Path::ToChangePath() const - { - if (type == Bip32PathType::Account) + { + if (type == Bip32PathType::Account) { throw std::runtime_error("Account keypath cannot be converted to change path!"); } @@ -30,7 +30,7 @@ namespace ledger return Bip32Path(components[ACCOUNT_INDEX], true, components[ADDRESS_INDEX_INDEX]); } - Bip32Path::Bip32Path(const std::string &keyPathStr) + Bip32Path::Bip32Path(const std::string &keyPathStr) { std::vector _components; std::stringstream ss(keyPathStr); @@ -112,18 +112,18 @@ namespace ledger throw std::runtime_error("Invalid keypath index"); } } - + components.push_back(BIP32_PURPOSE); components.push_back(BIP32_COIN_TYPE); components.push_back(_components[ACCOUNT_INDEX]); - + if (type == Bip32PathType::Address) { components.push_back(_components[CHANGE_INDEX]); components.push_back(_components[ADDRESS_INDEX_INDEX]); } } - + Bip32Path::Bip32Path(uint32_t account) { type = Bip32PathType::Account; @@ -148,32 +148,32 @@ namespace ledger bytes Bip32Path::Serialize() const { bytes serializedKeyPath; - + AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); - + if (type == Bip32PathType::Address) { AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); } - + return serializedKeyPath; } - std::string Bip32Path::ToString() const - { + std::string Bip32Path::ToString() const + { std::stringstream ss; - + ss << "m/" << components[PURPOSE_INDEX] << "'/" << components[COIN_TYPE_INDEX] << "'/" << components[ACCOUNT_INDEX] << "'"; - + if (type == Bip32PathType::Address) { ss << "/" << components[CHANGE_INDEX]; ss << "/" << components[ADDRESS_INDEX_INDEX]; } - + return ss.str(); } } // namespace ledger diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 6798143be..575bb9067 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -1,7 +1,7 @@ #ifndef __LEDGER_BIP32 #define __LEDGER_BIP32 1 -#include "bytes.h" +#include "ledger/bytes.h" #include #include @@ -9,7 +9,7 @@ namespace ledger { - class Bip32Path + class Bip32Path { public: const int PURPOSE_INDEX = 0; @@ -25,7 +25,7 @@ namespace ledger Bip32Path(uint32_t account); Bip32Path(const std::string &account, bool isChange, const std::string &index); Bip32Path(uint32_t account, bool isChange, uint32_t index); - + uint32_t Harden(uint32_t n) const; uint32_t Unharden(uint32_t n) const; bool IsHardened(uint32_t n) const; @@ -47,4 +47,4 @@ namespace ledger }; } -#endif \ No newline at end of file +#endif diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h index e7bb7fd50..e0647879c 100644 --- a/wallet/ledger/comm.h +++ b/wallet/ledger/comm.h @@ -3,7 +3,7 @@ #include #include -#include "bytes.h" +#include "ledger/bytes.h" namespace ledger { diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index 5e76ea735..d3b9d17d0 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -1,7 +1,4 @@ -#include "error.h" - -#include -#include +#include "ledger/error.h" namespace ledger { @@ -17,9 +14,4 @@ namespace ledger { return LedgerException::GetMessage(errorCode); } - - QString LedgerException::GetQtMessage() const - { - return QObject::tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(GetMessage())); - } } diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index d89c096d6..0ae3042ba 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -1,7 +1,6 @@ #pragma once #include -#include namespace ledger { enum class ErrorCode { @@ -43,7 +42,6 @@ namespace ledger { ErrorCode GetErrorCode() const; std::string GetMessage() const; - QString GetQtMessage() const; const char *what() const noexcept override; private: diff --git a/wallet/ledger/hid.cpp b/wallet/ledger/hid.cpp index 0f99c6cfa..9893b38ec 100644 --- a/wallet/ledger/hid.cpp +++ b/wallet/ledger/hid.cpp @@ -1,6 +1,6 @@ -#include "error.h" -#include "hid.h" -#include "utils.h" +#include "ledger/error.h" +#include "ledger/hid.h" +#include "ledger/utils.h" #include diff --git a/wallet/ledger/hid.h b/wallet/ledger/hid.h index c19c52ca7..dd099f11f 100644 --- a/wallet/ledger/hid.h +++ b/wallet/ledger/hid.h @@ -1,6 +1,6 @@ #pragma once -#include "comm.h" +#include "ledger/comm.h" #include "hidapi/hidapi.h" diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 3d926b95c..037f6bd81 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -1,10 +1,10 @@ -#include "ledger.h" -#include "error.h" -#include "hash.h" -#include "utils.h" #include "base58.h" -#include "bip32.h" -#include "tx.h" +#include "hash.h" +#include "ledger/ledger.h" +#include "ledger/error.h" +#include "ledger/utils.h" +#include "ledger/bip32.h" +#include "ledger/tx.h" #include #include diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 54fca28ff..2efef4e71 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -1,9 +1,9 @@ #pragma once -#include "bip32.h" -#include "bytes.h" -#include "transport.h" -#include "tx.h" +#include "ledger/bip32.h" +#include "ledger/bytes.h" +#include "ledger/transport.h" +#include "ledger/tx.h" namespace ledger { diff --git a/wallet/ledger/speculos.cpp b/wallet/ledger/speculos.cpp index 55aa75e68..c93e55dfa 100644 --- a/wallet/ledger/speculos.cpp +++ b/wallet/ledger/speculos.cpp @@ -1,6 +1,6 @@ -#include "error.h" -#include "speculos.h" -#include "utils.h" +#include "ledger/error.h" +#include "ledger/speculos.h" +#include "ledger/utils.h" #include #include diff --git a/wallet/ledger/speculos.h b/wallet/ledger/speculos.h index 9874d44c0..161952922 100644 --- a/wallet/ledger/speculos.h +++ b/wallet/ledger/speculos.h @@ -1,6 +1,6 @@ #pragma once -#include "comm.h" +#include "ledger/comm.h" #include "hidapi/hidapi.h" diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 5ea91360f..51c76f695 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -1,8 +1,8 @@ -#include "error.h" -#include "hid.h" -#include "speculos.h" -#include "transport.h" -#include "utils.h" +#include "ledger/error.h" +#include "ledger/hid.h" +#include "ledger/speculos.h" +#include "ledger/transport.h" +#include "ledger/utils.h" namespace ledger { diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index e9b7d862d..230c824c0 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -1,7 +1,7 @@ #pragma once -#include "bytes.h" -#include "comm.h" +#include "ledger/bytes.h" +#include "ledger/comm.h" #include diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index ccc719aeb..ef8e110fb 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -1,10 +1,10 @@ -#include "error.h" -#include "tx.h" -#include "utils.h" +#include "ledger/error.h" +#include "ledger/tx.h" +#include "ledger/utils.h" namespace ledger { -bytes SerializeTransaction(const Tx& tx) { +bytes SerializeTransaction(const Tx& tx) { bytes serializedTransaction; AppendUint32(serializedTransaction, tx.version, true); AppendUint32(serializedTransaction, tx.time, true); diff --git a/wallet/ledger/tx.h b/wallet/ledger/tx.h index 63b7c0d83..d9330f0c4 100644 --- a/wallet/ledger/tx.h +++ b/wallet/ledger/tx.h @@ -1,7 +1,7 @@ #pragma once -#include "bytes.h" -#include "transport.h" +#include "ledger/bytes.h" +#include "ledger/transport.h" namespace ledger { diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index 772c3195b..9ea074a88 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -1,4 +1,4 @@ -#include "utils.h" +#include "ledger/utils.h" #include #include diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index bf0b706a2..6c0ef4ca4 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -1,7 +1,7 @@ #ifndef _LEDGER_UTILS #define _LEDGER_UTILS 1 -#include "bytes.h" +#include "ledger/bytes.h" #include #include diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index 928609524..d78b23f2d 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -9,8 +9,9 @@ #include "guiutil.h" #include "ledger/bip32.h" #include "ledger/error.h" -#include "ledger/messagebox.h" #include "ledgerBridge.h" +#include "ledger_ui/ledgermessagebox.h" +#include "ledger_ui/ledgeruiutils.h" #include #include @@ -28,7 +29,7 @@ void VerifyLedgerAddressWorker::verify(uint32_t account, uint32_t index, QShared ledgerbridge::LedgerBridge ledgerBridge; paymentPubKeyBytes = ledgerBridge.GetPublicKey(account, false, index, true); } catch (const ledger::LedgerException& e) { - errorMessage = e.GetQtMessage(); + errorMessage = ledger_ui::GetQtErrorMessage(e); } emit resultReady(errorMessage); @@ -287,7 +288,7 @@ void AddressBookPage::on_verifyAddress_clicked() } QSharedPointer worker = QSharedPointer::create(); - ledger::MessageBox msgBox(this, worker, tr("Verifying address: %1").arg(ledgerAddress)); + ledger_ui::LedgerMessageBox msgBox(this, worker, tr("Verifying address: %1").arg(ledgerAddress)); connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(showVerifyAddressResult(QString))); connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(quit())); QTimer::singleShot(0, worker.data(), [worker, ledgerAccount, ledgerIndex]() { worker->verify(ledgerAccount, ledgerIndex, worker); }); diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index d01ccb454..0f353190f 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -4,8 +4,9 @@ #include "guiutil.h" #include "ledger/bip32.h" #include "ledger/error.h" -#include #include "ledger/utils.h" +#include "ledger_ui/ledgermessagebox.h" +#include "ledger_ui/ledgeruiutils.h" #include #include @@ -116,7 +117,7 @@ bool EditAddressDialog::saveCurrentRow() if (isLedger) { QSharedPointer worker = QSharedPointer::create(); - ledger::MessageBox msgBox(this, worker); + ledger_ui::LedgerMessageBox msgBox(this, worker); connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(setAddress(QString))); connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(quit())); QTimer::singleShot(0, worker.data(), [this, worker]() { worker->addRow(ui, model, worker); }); @@ -208,7 +209,7 @@ void EditAddressDialog::accept() break; case AddressTableModel::LEDGER_ERROR: QMessageBox::critical(this, windowTitle(), - ledger::LedgerException(model->getLedgerError()).GetQtMessage(), + ledger_ui::GetQtErrorMessage(ledger::LedgerException(model->getLedgerError())), QMessageBox::Ok, QMessageBox::Ok); break; diff --git a/wallet/ledger/messagebox.cpp b/wallet/qt/ledger_ui/ledgermessagebox.cpp similarity index 74% rename from wallet/ledger/messagebox.cpp rename to wallet/qt/ledger_ui/ledgermessagebox.cpp index 971fc5008..3181d6ef7 100644 --- a/wallet/ledger/messagebox.cpp +++ b/wallet/qt/ledger_ui/ledgermessagebox.cpp @@ -1,12 +1,12 @@ -#include "messagebox.h" +#include "ledger_ui/ledgermessagebox.h" #include #include #include -namespace ledger +namespace ledger_ui { - MessageBox::MessageBox(QWidget *parent, QSharedPointer worker, const QString &text) : worker_(worker), msgBox_(parent) + LedgerMessageBox::LedgerMessageBox(QWidget *parent, QSharedPointer worker, const QString &text) : worker_(worker), msgBox_(parent) { worker_->moveToThread(&thread_); thread_.start(); @@ -22,13 +22,13 @@ namespace ledger msgBox_.setStandardButtons(QMessageBox::StandardButton::NoButton); } - void MessageBox::exec() + void LedgerMessageBox::exec() { msgBox_.exec(); thread_.wait(); // to make sure that the thread is finished } - void MessageBox::quit() + void LedgerMessageBox::quit() { msgBox_.accept(); thread_.quit(); diff --git a/wallet/ledger/messagebox.h b/wallet/qt/ledger_ui/ledgermessagebox.h similarity index 57% rename from wallet/ledger/messagebox.h rename to wallet/qt/ledger_ui/ledgermessagebox.h index b0901b506..5c24a8e90 100644 --- a/wallet/ledger/messagebox.h +++ b/wallet/qt/ledger_ui/ledgermessagebox.h @@ -1,5 +1,5 @@ -#ifndef __LEDGER_MESSAGEBOX -#define __LEDGER_MESSAGEBOX 1 +#ifndef __LEDGER_LEDGERMESSAGEBOX +#define __LEDGER_LEDGERMESSAGEBOX 1 #include #include @@ -7,14 +7,14 @@ #include #include -namespace ledger +namespace ledger_ui { - class MessageBox : public QObject + class LedgerMessageBox : public QObject { Q_OBJECT public: - MessageBox(QWidget *parent, QSharedPointer worker, const QString &text = QString()); + LedgerMessageBox(QWidget *parent, QSharedPointer worker, const QString &text = QString()); void exec(); public slots: diff --git a/wallet/qt/ledger_ui/ledgeruiutils.cpp b/wallet/qt/ledger_ui/ledgeruiutils.cpp new file mode 100644 index 000000000..071575f3c --- /dev/null +++ b/wallet/qt/ledger_ui/ledgeruiutils.cpp @@ -0,0 +1,12 @@ +#include "ledger_ui/ledgeruiutils.h" + +#include +#include + +namespace ledger_ui +{ + QString GetQtErrorMessage(const ledger::LedgerException &e) + { + return QObject::tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(e.GetMessage())); + } +} diff --git a/wallet/qt/ledger_ui/ledgeruiutils.h b/wallet/qt/ledger_ui/ledgeruiutils.h new file mode 100644 index 000000000..3adb78944 --- /dev/null +++ b/wallet/qt/ledger_ui/ledgeruiutils.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +#include "ledger/error.h" + +namespace ledger_ui { + QString GetQtErrorMessage(const ledger::LedgerException &e); +} diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index 6acd2094f..c9a223b25 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -24,7 +24,7 @@ #include "ntp1/ntp1tokenlistmodel.h" #include "ntp1/ntp1tools.h" -#include "ledger/messagebox.h" +#include "ledger_ui/ledgermessagebox.h" void LedgerSignTxWorker::signTx( WalletModel* model, @@ -290,7 +290,7 @@ void SendCoinsDialog::on_sendButton_clicked() if (fLedgerTx) { QSharedPointer worker = QSharedPointer::create(); - ledger::MessageBox msgBox(this, worker); + ledger_ui::LedgerMessageBox msgBox(this, worker); connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), this, SLOT(setSendStatus(WalletModel::SendCoinsReturn))); connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), &msgBox, SLOT(quit())); QTimer::singleShot(0, worker.data(), [&]() { worker->signTx(model, recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, coinControl, strFromAccount, worker); }); diff --git a/wallet/wallet.pri b/wallet/wallet.pri index aab2076f0..9deccd643 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -7,11 +7,12 @@ HEADERS += qt/bitcoingui.h \ ledger/error.h \ ledger/hid.h \ ledger/ledger.h \ - ledger/messagebox.h \ ledger/speculos.h \ ledger/transport.h \ ledger/tx.h \ ledger/utils.h \ + qt/ledger_ui/ledgermessagebox.h \ + qt/ledger_ui/ledgeruiutils.h \ qt/transactiontablemodel.h \ qt/addresstablemodel.h \ qt/optionsdialog.h \ @@ -216,11 +217,12 @@ SOURCES += qt/bitcoin.cpp \ ledger/error.cpp \ ledger/hid.cpp \ ledger/ledger.cpp \ - ledger/messagebox.cpp \ ledger/speculos.cpp \ ledger/transport.cpp \ ledger/tx.cpp \ ledger/utils.cpp \ + qt/ledger_ui/ledgermessagebox.cpp \ + qt/ledger_ui/ledgeruiutils.cpp \ qt/bitcoingui.cpp \ qt/transactiontablemodel.cpp \ qt/addresstablemodel.cpp \ From addf8a40b2285c14fac1d715883a6f8c123aa370 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 11 Apr 2023 17:53:20 +0200 Subject: [PATCH 074/129] Add missing icon removals on mac --- wallet/qt/addressbookpage.cpp | 4 ++++ wallet/qt/sendcoinsdialog.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index d78b23f2d..8690dd482 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -49,6 +49,10 @@ AddressBookPage::AddressBookPage(Mode modeIn, Tabs tabIn, QWidget *parent) : #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac ui->newAddressButton->setIcon(QIcon()); ui->copyToClipboard->setIcon(QIcon()); + ui->showQRCode->setIcon(QIcon()); + ui->signMessage->setIcon(QIcon()); + ui->verifyMessage->setIcon(QIcon()); + ui->verifyAddress->setIcon(QIcon()); ui->deleteButton->setIcon(QIcon()); #endif diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index c9a223b25..1df2326e6 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -51,8 +51,10 @@ SendCoinsDialog::SendCoinsDialog(QWidget* parent) #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac ui->addButton->setIcon(QIcon()); + ui->editMetadataButton->setIcon(QIcon()); ui->clearButton->setIcon(QIcon()); ui->sendButton->setIcon(QIcon()); + ui->ledgerAddressBookButton->setIcon(QIcon()); #endif #if QT_VERSION >= 0x040700 From 08093c7c487934bac9ac86ed6a272f0430580ce0 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 11 Apr 2023 17:54:34 +0200 Subject: [PATCH 075/129] Use monospace font in Verify Address dialog --- wallet/qt/addressbookpage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/qt/addressbookpage.cpp b/wallet/qt/addressbookpage.cpp index 8690dd482..35e39787a 100644 --- a/wallet/qt/addressbookpage.cpp +++ b/wallet/qt/addressbookpage.cpp @@ -292,7 +292,7 @@ void AddressBookPage::on_verifyAddress_clicked() } QSharedPointer worker = QSharedPointer::create(); - ledger_ui::LedgerMessageBox msgBox(this, worker, tr("Verifying address: %1").arg(ledgerAddress)); + ledger_ui::LedgerMessageBox msgBox(this, worker, tr("Verifying address: %1").arg(ledgerAddress)); connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(showVerifyAddressResult(QString))); connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(quit())); QTimer::singleShot(0, worker.data(), [worker, ledgerAccount, ledgerIndex]() { worker->verify(ledgerAccount, ledgerIndex, worker); }); From 62b6d66ec87e5c90663e8f863c38d505658b92df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Tue, 11 Apr 2023 16:25:56 +0200 Subject: [PATCH 076/129] Move functions from UI to wallet to be reused in RPC --- wallet/ledgerBridge.h | 11 +++++++ wallet/qt/addresstablemodel.cpp | 52 ++++----------------------------- wallet/wallet.cpp | 48 ++++++++++++++++++++++++++++++ wallet/wallet.h | 12 ++++++++ 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 32752f061..16093750a 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -2,6 +2,7 @@ #include "ledger/tx.h" #include "ledger/bytes.h" #include "ledger/ledger.h" +#include "ledger/utils.h" #include "itxdb.h" #include "wallet.h" @@ -21,6 +22,16 @@ namespace ledgerbridge class LedgerBridge { public: + static bool ValidateAccountIndex(int accountIndex) + { + return 0 <= accountIndex && accountIndex <= ledger::MAX_RECOMMENDED_ACCOUNT; + } + + static bool ValidateAddressIndex(int addressIndex) + { + return 0 <= addressIndex && addressIndex <= ledger::MAX_RECOMMENDED_INDEX; + } + LedgerBridge(); ~LedgerBridge(); diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index a04445220..46cf4d392 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -278,31 +278,14 @@ bool AddressTableModel::isWhitelisted(const std::string& address) const return purposeForAddress(address).compare(AddressBook::AddressBookPurpose::DELEGATOR) == 0; } -bool AddressTableModel::isLabelUsedByLedger(const QString& label) -{ - std::string strLabel = label.toStdString(); - CTxDestination addressOut; - return ( - wallet->GetAddressBookEntryByLabel(strLabel, addressOut) && - addressOut.type() == typeid(CKeyID) && - wallet->HaveLedgerKey(*boost::get(&addressOut)) - ); -} - -bool AddressTableModel::isLabelUsableForLedger(const QString& label) -{ - std::string strLabel = label.toStdString(); - CTxDestination addressOut; - return !strLabel.empty() && !wallet->GetAddressBookEntryByLabel(strLabel, addressOut); -} - bool AddressTableModel::checkLabelAvailability(const QString& label, bool isLedgerAddress) { - if (isLabelUsedByLedger(label)) { + auto labelAvailability = wallet->CheckLabelAvailability(label.toStdString(), isLedgerAddress); + if (labelAvailability == LabelAvailability::USED_BY_LEDGER) { editStatus = LABEL_USED_BY_LEDGER; return false; } - if (isLedgerAddress && !isLabelUsableForLedger(label)) { + if (isLedgerAddress && labelAvailability == LabelAvailability::NOT_USABLE_FOR_LEDGER) { editStatus = LABEL_NOT_USABLE_FOR_LEDGER; return false; } @@ -461,43 +444,25 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } else if (type == ReceiveLedger) { bool accountOk = true; uint32_t account = ledgerAccount.toUInt(&accountOk); - if (!accountOk || !validateLedgerPathItem(account, ledger::MAX_RECOMMENDED_ACCOUNT)) { + if (!accountOk || !ledgerbridge::LedgerBridge::ValidateAccountIndex(account)) { editStatus = INVALID_LEDGER_ACCOUNT; return QString(); } bool indexOk = true; uint32_t index = ledgerIndex.toUInt(&indexOk); - if (!indexOk || !validateLedgerPathItem(index, ledger::MAX_RECOMMENDED_INDEX)) { + if (!indexOk || !ledgerbridge::LedgerBridge::ValidateAddressIndex(index)) { editStatus = INVALID_LEDGER_INDEX; return QString(); } - ledger::bytes accountPubKeyBytes; - ledger::bytes paymentPubKeyBytes; - ledger::bytes changePubKeyBytes; - try { - ledgerbridge::LedgerBridge ledgerBridge; - accountPubKeyBytes = ledgerBridge.GetPublicKey(account, false); - paymentPubKeyBytes = ledgerBridge.GetPublicKey(account, false, index, true); - changePubKeyBytes = ledgerBridge.GetPublicKey(account, true, index, false); + strAddress = wallet->ImportLedgerKey(account, index); } catch (const ledger::LedgerException& e) { editStatus = LEDGER_ERROR; ledgerError = e.GetErrorCode(); return QString(); } - - CPubKey accountPubKey(accountPubKeyBytes); - - CPubKey paymentPubKey(paymentPubKeyBytes); - CLedgerKey paymentLedgerKey(paymentPubKey, accountPubKey.GetID(), account, false, index); - wallet->AddLedgerKey(paymentLedgerKey); - strAddress = CBitcoinAddress(paymentPubKey.GetID()).ToString(); - - CPubKey changePubKey(changePubKeyBytes); - CLedgerKey changeLedgerKey(changePubKey, accountPubKey.GetID(), account, true, index); - wallet->AddLedgerKey(changeLedgerKey); } else { return QString(); } @@ -559,8 +524,3 @@ void AddressTableModel::emitDataChanged(int idx) { emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length() - 1, QModelIndex())); } - -bool AddressTableModel::validateLedgerPathItem(uint32_t value, uint32_t top) const -{ - return 0 <= value && value <= top; -} diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index de20225a2..a854d245f 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -3719,6 +3719,54 @@ CAmount CWalletTx::GetCredit(const uint256& bestBlockHash, const ITxDB& txdb, return credit; } +bool CWallet::IsLabelUsedByLedger(const std::string& label) +{ + CTxDestination addressOut; + return ( + GetAddressBookEntryByLabel(label, addressOut) && + addressOut.type() == typeid(CKeyID) && + HaveLedgerKey(*boost::get(&addressOut)) + ); +} + +bool CWallet::IsLabelUsableForLedger(const std::string& label) +{ + CTxDestination addressOut; + return !label.empty() && !GetAddressBookEntryByLabel(label, addressOut); +} + +LabelAvailability CWallet::CheckLabelAvailability(const std::string& label, bool isLedgerAddress) +{ + if (IsLabelUsedByLedger(label)) { + return LabelAvailability::USED_BY_LEDGER; + } + if (isLedgerAddress && !IsLabelUsableForLedger(label)) { + return LabelAvailability::NOT_USABLE_FOR_LEDGER; + } + return LabelAvailability::AVAILABLE; +} + +std::string CWallet::ImportLedgerKey(int account, int index) +{ + ledgerbridge::LedgerBridge ledgerBridge; + auto accountPubKeyBytes = ledgerBridge.GetPublicKey(account, false); + auto paymentPubKeyBytes = ledgerBridge.GetPublicKey(account, false, index, true); + auto changePubKeyBytes = ledgerBridge.GetPublicKey(account, true, index, false); + + CPubKey accountPubKey(accountPubKeyBytes); + + CPubKey paymentPubKey(paymentPubKeyBytes); + CLedgerKey paymentLedgerKey(paymentPubKey, accountPubKey.GetID(), account, false, index); + AddLedgerKey(paymentLedgerKey); + auto address = CBitcoinAddress(paymentPubKey.GetID()).ToString(); + + CPubKey changePubKey(changePubKeyBytes); + CLedgerKey changeLedgerKey(changePubKey, accountPubKey.GetID(), account, true, index); + AddLedgerKey(changeLedgerKey); + + return address; +} + void CWalletTx::MarkDirty() { c_CreditCached = boost::none; diff --git a/wallet/wallet.h b/wallet/wallet.h index 2a10cc450..c40994dd3 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -48,6 +48,13 @@ enum WalletFeature FEATURE_LATEST = 60000 }; +enum LabelAvailability +{ + AVAILABLE, + USED_BY_LEDGER, /**< Using this label would break Ledger's unique label requirement */ + NOT_USABLE_FOR_LEDGER, /**< Ledger address requires a fresh, unique label */ +}; + class WalletNewTxUpdateFunctor : public boost::enable_shared_from_this { // reload balances if the current height less than the registered height plus this next value @@ -430,6 +437,11 @@ class CWallet : public CCryptoKeyStore CBitcoinAddress getNewStakingAddress(const std::string& label); CAmount GetStakingBalance(const ITxDB& txdb, bool fIncludeColdStaking) const; + bool IsLabelUsedByLedger(const std::string& label); + bool IsLabelUsableForLedger(const std::string& label); + LabelAvailability CheckLabelAvailability(const std::string& label, bool isLedgerAddress); + std::string ImportLedgerKey(int accountIndex, int addressIndex); + mutable CCriticalSection cs_LedgerKeyStore; // TODO GK - LedgerKeyStore class? (this is copied from keystore) From ebbeb55905edbee752b42a84b4f7bc19d614e206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Wed, 12 Apr 2023 11:58:25 +0200 Subject: [PATCH 077/129] Add `addledgeraddress` RPC function --- README.md | 1 + wallet/bitcoinrpc.cpp | 1 + wallet/bitcoinrpc.h | 1 + wallet/rpcwallet.cpp | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/README.md b/README.md index e5d14616d..b2788effc 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,7 @@ RPC commands are used to interact with a running instance of nebliod or neblio-Q ``` abandontransaction +addledgeraddress res/images/about.png diff --git a/wallet/qt/ui_addressbookpage.h b/wallet/qt/ui_addressbookpage.h index 46debe3be..1d6e3484b 100644 --- a/wallet/qt/ui_addressbookpage.h +++ b/wallet/qt/ui_addressbookpage.h @@ -38,6 +38,7 @@ class Ui_AddressBookPage QPushButton *signMessage; QPushButton *verifyMessage; QPushButton *verifyAddress; + QPushButton *addressInfo; QPushButton *deleteButton; QSpacerItem *horizontalSpacer; QDialogButtonBox *buttonBox; @@ -118,11 +119,19 @@ class Ui_AddressBookPage horizontalLayout->addWidget(verifyAddress); + addressInfo = new QPushButton(AddressBookPage); + addressInfo->setObjectName(QStringLiteral("addressInfo")); + QIcon icon6; + icon6.addFile(QStringLiteral(":/icons/info"), QSize(), QIcon::Normal, QIcon::Off); + addressInfo->setIcon(icon6); + + horizontalLayout->addWidget(addressInfo); + deleteButton = new QPushButton(AddressBookPage); deleteButton->setObjectName(QStringLiteral("deleteButton")); - QIcon icon6; - icon6.addFile(QStringLiteral(":/icons/remove"), QSize(), QIcon::Normal, QIcon::Off); - deleteButton->setIcon(icon6); + QIcon icon7; + icon7.addFile(QStringLiteral(":/icons/remove"), QSize(), QIcon::Normal, QIcon::Off); + deleteButton->setIcon(icon7); horizontalLayout->addWidget(deleteButton); @@ -178,6 +187,10 @@ class Ui_AddressBookPage verifyAddress->setToolTip(QApplication::translate("AddressBookPage", "Verify a neblio address on your Ledger device", Q_NULLPTR)); #endif // QT_NO_TOOLTIP verifyAddress->setText(QApplication::translate("AddressBookPage", "Verify &Address", Q_NULLPTR)); +#ifndef QT_NO_TOOLTIP + addressInfo->setToolTip(QApplication::translate("AddressBookPage", "Show information about the selected Ledger address", Q_NULLPTR)); +#endif // QT_NO_TOOLTIP + addressInfo->setText(QApplication::translate("AddressBookPage", "Address &Info", Q_NULLPTR)); #ifndef QT_NO_TOOLTIP deleteButton->setToolTip(QApplication::translate("AddressBookPage", "Delete the currently selected address from the list", Q_NULLPTR)); #endif // QT_NO_TOOLTIP From 2b5425ad842c8d32832570c6636402e00bbdcb30 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 17 Apr 2023 17:32:46 +0200 Subject: [PATCH 101/129] Unify #include guards and #pragma once --- wallet/ledger/bip32.h | 6 +++--- wallet/ledger/bytes.h | 6 +++--- wallet/ledger/comm.h | 5 ++++- wallet/ledger/error.h | 5 ++++- wallet/ledger/hid.h | 5 ++++- wallet/ledger/ledger.h | 5 ++++- wallet/ledger/speculos.h | 5 ++++- wallet/ledger/transport.h | 5 ++++- wallet/ledger/tx.h | 5 ++++- wallet/ledger/utils.h | 6 +++--- wallet/ledgerBridge.h | 7 ++++++- wallet/qt/ledger_ui/ledgermessagebox.h | 6 +++--- wallet/qt/ledger_ui/ledgeruiutils.h | 5 ++++- 13 files changed, 50 insertions(+), 21 deletions(-) diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index 575bb9067..f39ad59df 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -1,5 +1,5 @@ -#ifndef __LEDGER_BIP32 -#define __LEDGER_BIP32 1 +#ifndef LEDGER_BIP32_H +#define LEDGER_BIP32_H #include "ledger/bytes.h" @@ -47,4 +47,4 @@ namespace ledger }; } -#endif +#endif // LEDGER_BIP32_H diff --git a/wallet/ledger/bytes.h b/wallet/ledger/bytes.h index 968fa6071..903f4b9f3 100644 --- a/wallet/ledger/bytes.h +++ b/wallet/ledger/bytes.h @@ -1,5 +1,5 @@ -#ifndef __LEDGER_BYTES -#define __LEDGER_BYTES 1 +#ifndef LEDGER_BYTES_H +#define LEDGER_BYTES_H #include #include @@ -9,4 +9,4 @@ namespace ledger typedef std::vector bytes; } -#endif \ No newline at end of file +#endif // LEDGER_BYTES_H diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h index e0647879c..73f20fa45 100644 --- a/wallet/ledger/comm.h +++ b/wallet/ledger/comm.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_COMM_H +#define LEDGER_COMM_H #include #include @@ -19,3 +20,5 @@ namespace ledger [[nodiscard]] virtual bool isOpen() const = 0; }; } // namespace ledger + +#endif // LEDGER_COMM_H diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index 09cf4ff5e..9c2d86383 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_ERROR_H +#define LEDGER_ERROR_H #include @@ -49,3 +50,5 @@ namespace ledger { std::string message; }; } // namespace ledger + +#endif // LEDGER_ERROR_H diff --git a/wallet/ledger/hid.h b/wallet/ledger/hid.h index dd099f11f..d123a4d35 100644 --- a/wallet/ledger/hid.h +++ b/wallet/ledger/hid.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_HID_H +#define LEDGER_HID_H #include "ledger/comm.h" @@ -25,3 +26,5 @@ namespace ledger unsigned short vendorId = 0x2c97; // Ledger Vendor ID }; } // namespace ledger + +#endif // LEDGER_HID_H diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 2efef4e71..3a1c00a40 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_LEDGER_H +#define LEDGER_LEDGER_H #include "ledger/bip32.h" #include "ledger/bytes.h" @@ -43,3 +44,5 @@ namespace ledger TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); }; } + +#endif // LEDGER_LEDGER_H diff --git a/wallet/ledger/speculos.h b/wallet/ledger/speculos.h index 161952922..dd65fdeed 100644 --- a/wallet/ledger/speculos.h +++ b/wallet/ledger/speculos.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_SPECULOS_H +#define LEDGER_SPECULOS_H #include "ledger/comm.h" @@ -20,3 +21,5 @@ namespace ledger bool opened = false; }; } // namespace ledger + +#endif // LEDGER_SPECULOS_H diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index 230c824c0..7eec39cf7 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_TRANSPORT_H +#define LEDGER_TRANSPORT_H #include "ledger/bytes.h" #include "ledger/comm.h" @@ -29,3 +30,5 @@ namespace ledger std::unique_ptr comm; }; } // namespace ledger + +#endif // LEDGER_TRANSPORT_H diff --git a/wallet/ledger/tx.h b/wallet/ledger/tx.h index d9330f0c4..48a6311b5 100644 --- a/wallet/ledger/tx.h +++ b/wallet/ledger/tx.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_TX_H +#define LEDGER_TX_H #include "ledger/bytes.h" #include "ledger/transport.h" @@ -69,3 +70,5 @@ namespace ledger Tx DeserializeTransaction(const bytes &transaction); TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); } + +#endif // LEDGER_TX_H diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index 6c0ef4ca4..54c2be89b 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -1,5 +1,5 @@ -#ifndef _LEDGER_UTILS -#define _LEDGER_UTILS 1 +#ifndef LEDGER_UTILS_H +#define LEDGER_UTILS_H #include "ledger/bytes.h" @@ -31,4 +31,4 @@ const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; const uint32_t MAX_RECOMMENDED_INDEX = 50000; } // namespace ledger -#endif +#endif // LEDGER_UTILS_H diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 16093750a..0ac27919f 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -1,3 +1,6 @@ +#ifndef LEDGERBRIDGE_H +#define LEDGERBRIDGE_H + #include "ledger/bip32.h" #include "ledger/tx.h" #include "ledger/bytes.h" @@ -34,7 +37,7 @@ namespace ledgerbridge LedgerBridge(); ~LedgerBridge(); - + ledger::bytes GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, bool display); ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); @@ -44,3 +47,5 @@ namespace ledgerbridge ledger::Tx ToLedgerTx(const CTransaction& tx); }; } + +#endif // LEDGERBRIDGE_H diff --git a/wallet/qt/ledger_ui/ledgermessagebox.h b/wallet/qt/ledger_ui/ledgermessagebox.h index 5c24a8e90..51d469cc0 100644 --- a/wallet/qt/ledger_ui/ledgermessagebox.h +++ b/wallet/qt/ledger_ui/ledgermessagebox.h @@ -1,5 +1,5 @@ -#ifndef __LEDGER_LEDGERMESSAGEBOX -#define __LEDGER_LEDGERMESSAGEBOX 1 +#ifndef LEDGER_UI_LEDGERMESSAGEBOX_H +#define LEDGER_UI_LEDGERMESSAGEBOX_H #include #include @@ -27,4 +27,4 @@ namespace ledger_ui }; } -#endif +#endif // LEDGER_UI_LEDGERMESSAGEBOX_H diff --git a/wallet/qt/ledger_ui/ledgeruiutils.h b/wallet/qt/ledger_ui/ledgeruiutils.h index 3adb78944..7bcd7541c 100644 --- a/wallet/qt/ledger_ui/ledgeruiutils.h +++ b/wallet/qt/ledger_ui/ledgeruiutils.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef LEDGER_UI_LEDGERUIUTILS_H +#define LEDGER_UI_LEDGERUIUTILS_H #include #include @@ -8,3 +9,5 @@ namespace ledger_ui { QString GetQtErrorMessage(const ledger::LedgerException &e); } + +#endif // LEDGER_UI_LEDGERUIUTILS_H From 623614505d57792aff3055e5ae57fb4b132d3623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 11:34:47 +0200 Subject: [PATCH 102/129] Remove modern C++ syntax --- wallet/ledger/ledger.cpp | 22 +++++++++++----------- wallet/ledger/ledger.h | 10 +++++----- wallet/ledger/utils.cpp | 12 ++++++------ wallet/ledgerBridge.cpp | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 037f6bd81..0233ffbbf 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -1,10 +1,10 @@ -#include "base58.h" +#include "ledger.h" +#include "error.h" #include "hash.h" -#include "ledger/ledger.h" -#include "ledger/error.h" -#include "ledger/utils.h" -#include "ledger/bip32.h" -#include "ledger/tx.h" +#include "utils.h" +#include "base58.h" +#include "bip32.h" +#include "tx.h" #include #include @@ -45,7 +45,7 @@ namespace ledger if (offset != buffer.size()) throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); - return {pubKey, std::string(address.begin(), address.end()), chainCode}; + return std::make_tuple(pubKey, std::string(address.begin(), address.end()), chainCode); } bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes &transactionData) @@ -170,7 +170,7 @@ namespace ledger } } - std::vector> Ledger::SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector& signPaths, const std::vector &utxos) + std::vector Ledger::SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector& signPaths, const std::vector &utxos) { assert(tx.inputs.size() == signPaths.size()); assert(tx.inputs.size() == utxos.size()); @@ -189,7 +189,7 @@ namespace ledger trustedInputs.push_back(trustedInput); } - std::vector> signatures; + std::vector signatures; for (auto i = 0; i < tx.inputs.size(); i++) { auto &script = utxos[i].tx.outputs[utxos[i].outputIndex].script; @@ -216,11 +216,11 @@ namespace ledger bytes data; data.push_back(0x30); AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); - signatures.push_back({1, data}); + signatures.push_back(data); } else { - signatures.push_back({0, buffer}); + throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); } } diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 3a1c00a40..bd08358cc 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -1,10 +1,10 @@ #ifndef LEDGER_LEDGER_H #define LEDGER_LEDGER_H -#include "ledger/bip32.h" -#include "ledger/bytes.h" -#include "ledger/transport.h" -#include "ledger/tx.h" +#include "bip32.h" +#include "bytes.h" +#include "transport.h" +#include "tx.h" namespace ledger { @@ -29,7 +29,7 @@ namespace ledger void open(); std::tuple GetPublicKey(const Bip32Path path, bool confirm); - std::vector> SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector &signPaths, const std::vector &utxos); + std::vector SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector &signPaths, const std::vector &utxos); void close(); diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index 9ea074a88..33c8a36bb 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -1,4 +1,4 @@ -#include "ledger/utils.h" +#include "utils.h" #include #include @@ -12,23 +12,23 @@ namespace ledger { if (data[offset] < 0xfd) { - return {data[offset], 1}; + return std::make_tuple(data[offset], 1); } if (data[offset] == 0xfd) { - return {(data[offset + 2] << 8) + data[offset + 1], 3}; + return std::make_tuple((data[offset + 2] << 8) + data[offset + 1], 3); } if (data[offset] == 0xfe) { - return { + return std::make_tuple( (data[offset + 4] << 24) + (data[offset + 3] << 16) + (data[offset + 2] << 8) + data[offset + 1], - 5, - }; + 5 + ); } } diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 0dd6e61dd..a1dea565e 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -82,7 +82,7 @@ namespace ledgerbridge // add signatures to tx and verify for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { - auto signature = std::get<1>(signTxResults[sigIndex]); + auto signature = signTxResults[sigIndex]; auto pubKey = CPubKey(GetPublicKey(ledger, signaturePaths[sigIndex], false)); From 499326c3bfe4e8e4e158965b0ab4c87e209f747b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 11:36:16 +0200 Subject: [PATCH 103/129] Include hidapi and its depdendencies in qt build --- wallet/wallet-libs.pri | 11 +++++++++++ wallet/wallet.pro | 13 ------------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/wallet/wallet-libs.pri b/wallet/wallet-libs.pri index a30f91851..b384c9751 100644 --- a/wallet/wallet-libs.pri +++ b/wallet/wallet-libs.pri @@ -290,6 +290,16 @@ LIBS += -lsodium windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX macx: LIBS += -lcurl +!windows:!macx { + LIBS += -lhidapi-libusb + LIBS += -lusb-1.0 +} else { + LIBS += -lhidapi + win32 { + LIBS += -lhid -lsetupapi + } +} + # For Fedora unix:INCLUDEPATH += /usr/include/libdb4/ unix:LIBS += -L/usr/lib64/libdb4/ @@ -338,5 +348,6 @@ contains(RELEASE, 1) { !windows:!macx { DEFINES += LINUX + LIBS += -ludev LIBS += -lrt -ldl } diff --git a/wallet/wallet.pro b/wallet/wallet.pro index a55cb49f2..2910595a4 100644 --- a/wallet/wallet.pro +++ b/wallet/wallet.pro @@ -18,19 +18,6 @@ mac { QMAKE_INFO_PLIST = $${NEBLIO_ROOT}/wallet/qt/res/Info.plist } -_BOOST_PATH = $${NEBLIO_ROOT}/boost_1_65_1 -INCLUDEPATH += "$${_BOOST_PATH}" -LIBS += -L$${_BOOST_PATH}/stage/lib - -_OPENSSL_PATH = $${NEBLIO_ROOT}/openssl_build -INCLUDEPATH += "$${_OPENSSL_PATH}/include" -LIBS += -L$${_OPENSSL_PATH}/lib - -_HIDAPI_PATH = /usr/local/ -INCLUDEPATH += "$${_HIDAPI_PATH}/include/hidapi" -LIBS += -L$${_HIDAPI_PATH}/lib/libhidapi-libusb.so -LIBS += -lhidapi-libusb - # use: qmake "NEBLIO_REST=1" contains(NEBLIO_REST, 1) { DEFINES += NEBLIO_REST From e3e119a7dcdfaf390bd68d4c6d7b6beb272d4679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 11:53:11 +0200 Subject: [PATCH 104/129] Add build cache Dockerfile --- docker/build-cache.Dockerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker/build-cache.Dockerfile diff --git a/docker/build-cache.Dockerfile b/docker/build-cache.Dockerfile new file mode 100644 index 000000000..11fb4338f --- /dev/null +++ b/docker/build-cache.Dockerfile @@ -0,0 +1,12 @@ +FROM neblioteam/nebliod-build-ccache + +RUN apt update +# install project dependencies +RUN apt install -y --no-install-recommends libudev-dev libusb-1.0-0-dev libhidapi-dev + +WORKDIR /root +RUN rm -rf boost_build +RUN git clone -b fix-build-v2 https://github.com/vacuumlabs/neblio +RUN python neblio/build_scripts/CompileBoost-Linux.py +RUN rm -rf neblio +WORKDIR / \ No newline at end of file From 2cc3fbf6002ef81d70d5616f9ba61f69681ace1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 11:55:00 +0200 Subject: [PATCH 105/129] Use updated Docker container in Linux CI script --- ci_scripts/test_linux-daemon-gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci_scripts/test_linux-daemon-gui.py b/ci_scripts/test_linux-daemon-gui.py index b29ae50d3..e621a79bd 100644 --- a/ci_scripts/test_linux-daemon-gui.py +++ b/ci_scripts/test_linux-daemon-gui.py @@ -39,7 +39,7 @@ nci.call_with_err_code('mv ' + os.path.join(working_dir,'.ccache', '') + ' ' + os.path.join(deploy_dir,'.ccache', '')) # Start Docker Container to Build nebliod or neblio-Qt -nci.call_with_err_code('sudo docker run -e BUILD=' + build_target + ' -v ' + os.environ['BUILD_DIR'] + ':/root/vol -t neblioteam/nebliod-build-ccache') +nci.call_with_err_code('sudo docker run -e BUILD=' + build_target + ' -v ' + os.environ['BUILD_DIR'] + ':/root/vol -t gabrielkerekesvl/nebliod-build-ccache') nci.call_with_err_code('sleep 15 && sudo docker kill $(sudo docker ps -q);exit 0') # move .ccache folder back to ccache dir From 2be3a762fc5b6a578cccc1a06694870694855c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 11:57:35 +0200 Subject: [PATCH 106/129] Switch GH actions runners to Ubuntu 22.04 --- .github/workflows/ci_cd.yml | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index a2294150e..e745eb937 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -18,16 +18,16 @@ env: jobs: linux_daemon_cmake: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_daemon_cmake-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_daemon_cmake- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake- - name: Set target_v env var run: | echo "target_v=linux_daemon_cmake" >> $GITHUB_ENV @@ -42,16 +42,16 @@ jobs: path: /tmp/bitcoin* linux_daemon: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_daemon-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_daemon- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon- - name: Set target_v env var run: | echo "target_v=linux_daemon" >> $GITHUB_ENV @@ -70,16 +70,16 @@ jobs: linux_wallet: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_wallet-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_wallet- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet- - name: Set target_v env var run: | echo "target_v=linux_wallet" >> $GITHUB_ENV @@ -97,17 +97,17 @@ jobs: linux_wallet_test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_wallet_test-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_wallet_test- - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-linux_wallet- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet- - name: Set target_v env var run: | echo "target_v=linux_wallet_test" >> $GITHUB_ENV @@ -117,16 +117,16 @@ jobs: windows_x86: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-windows_x86-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-windows_x86- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86- - name: Build neblio-Qt for Windows x86 run: | python -u ci_scripts/test_win_x86-gui_wallet.py @@ -240,16 +240,16 @@ jobs: rpi_wallet: # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_wallet-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_wallet- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- - name: Set target_v env var run: | echo "target_v=rpi_wallet" >> $GITHUB_ENV @@ -269,17 +269,17 @@ jobs: rpi_wallet_test: # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_wallet_test-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_wallet_test- - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_wallet- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- - name: Set target_v env var run: | echo "target_v=rpi_wallet_test" >> $GITHUB_ENV @@ -297,16 +297,16 @@ jobs: rpi_daemon: # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 - name: ccache uses: actions/cache@v2 with: path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_daemon-${{ github.sha }}-${{ github.run_id }} + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon-${{ github.sha }}-${{ github.run_id }} restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-18.04-rpi_daemon- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon- - name: Set target_v env var run: | echo "target_v=rpi_daemon" >> $GITHUB_ENV From e29785a3c9769e10f5461b16a6297af071e43a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 7 Apr 2023 09:49:29 +0200 Subject: [PATCH 107/129] Fix `CompileBoost-Linux.py` script --- build_scripts/CompileBoost-Linux.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build_scripts/CompileBoost-Linux.py b/build_scripts/CompileBoost-Linux.py index 4ee0b796c..53a09920d 100644 --- a/build_scripts/CompileBoost-Linux.py +++ b/build_scripts/CompileBoost-Linux.py @@ -86,14 +86,9 @@ def download_boost(): call("tar -xf " + filename, shell=True) #extract the .tar.gz file -dirname_bin = dirname + "_build" +dirname_bin = dirname final_dirname = "boost_build" -try: - shutil.rmtree(dirname_bin) -except: - pass - try: shutil.rmtree(final_dirname) except: From 84197abb2a963d58b5c88df9b417db58ff73a5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 12:00:18 +0200 Subject: [PATCH 108/129] Enable only Linux CI actions --- .github/workflows/ci_cd.yml | 464 ++++++++++++++++++------------------ 1 file changed, 232 insertions(+), 232 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index e745eb937..09ebef980 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -17,29 +17,29 @@ env: jobs: - linux_daemon_cmake: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake- - - name: Set target_v env var - run: | - echo "target_v=linux_daemon_cmake" >> $GITHUB_ENV - - name: Build nebliod for Linux using cmake - run: | - python -u ci_scripts/test_linux-daemon-cmake.py - - name: Upload test artifacts on failure - uses: actions/upload-artifact@v2 - if: failure() - with: - name: test-artifacts - path: /tmp/bitcoin* + # linux_daemon_cmake: + # runs-on: ubuntu-22.04 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake- + # - name: Set target_v env var + # run: | + # echo "target_v=linux_daemon_cmake" >> $GITHUB_ENV + # - name: Build nebliod for Linux using cmake + # run: | + # python -u ci_scripts/test_linux-daemon-cmake.py + # - name: Upload test artifacts on failure + # uses: actions/upload-artifact@v2 + # if: failure() + # with: + # name: test-artifacts + # path: /tmp/bitcoin* linux_daemon: runs-on: ubuntu-22.04 @@ -96,230 +96,230 @@ jobs: release-tag: ${{ env.RELEASE_TAG }} - linux_wallet_test: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test- - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet- - - name: Set target_v env var - run: | - echo "target_v=linux_wallet_test" >> $GITHUB_ENV - - name: Build neblio-Qt for Linux and Run Tests - run: | - python -u ci_scripts/test_linux-daemon-gui.py -t + # linux_wallet_test: + # runs-on: ubuntu-22.04 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test- + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet- + # - name: Set target_v env var + # run: | + # echo "target_v=linux_wallet_test" >> $GITHUB_ENV + # - name: Build neblio-Qt for Linux and Run Tests + # run: | + # python -u ci_scripts/test_linux-daemon-gui.py -t - windows_x86: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86- - - name: Build neblio-Qt for Windows x86 - run: | - python -u ci_scripts/test_win_x86-gui_wallet.py - echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - - name: If Release, Upload Release - if: startsWith(github.ref, 'refs/tags/v') - uses: AButler/upload-release-assets@v1.0 - with: - files: ${{ env.SOURCE_PATH }} - repo-token: ${{ secrets.GHAT2 }} - release-tag: ${{ env.RELEASE_TAG }} + # windows_x86: + # runs-on: ubuntu-22.04 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86- + # - name: Build neblio-Qt for Windows x86 + # run: | + # python -u ci_scripts/test_win_x86-gui_wallet.py + # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + # - name: If Release, Upload Release + # if: startsWith(github.ref, 'refs/tags/v') + # uses: AButler/upload-release-assets@v1.0 + # with: + # files: ${{ env.SOURCE_PATH }} + # repo-token: ${{ secrets.GHAT2 }} + # release-tag: ${{ env.RELEASE_TAG }} - osx: - runs-on: macos-10.15 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- - - name: Switch Xcode Version - run: | - sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer - - name: Build neblio-Qt for macOS - run: | - python -u ci_scripts/test_osx-gui_wallet.py - echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - - name: If Release, Upload Release - if: startsWith(github.ref, 'refs/tags/v') - uses: AButler/upload-release-assets@v1.0 - with: - files: ${{ env.SOURCE_PATH }} - repo-token: ${{ secrets.GHAT2 }} - release-tag: ${{ env.RELEASE_TAG }} + # osx: + # runs-on: macos-10.15 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- + # - name: Switch Xcode Version + # run: | + # sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer + # - name: Build neblio-Qt for macOS + # run: | + # python -u ci_scripts/test_osx-gui_wallet.py + # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + # - name: If Release, Upload Release + # if: startsWith(github.ref, 'refs/tags/v') + # uses: AButler/upload-release-assets@v1.0 + # with: + # files: ${{ env.SOURCE_PATH }} + # repo-token: ${{ secrets.GHAT2 }} + # release-tag: ${{ env.RELEASE_TAG }} - osx_test: - runs-on: macos-10.15 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test- - ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- - - name: Switch Xcode Version - run: | - sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer - - name: Build neblio-Qt for macOS and Run Tests - run: | - python -u ci_scripts/test_osx-gui_wallet.py -t + # osx_test: + # runs-on: macos-10.15 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test- + # ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- + # - name: Switch Xcode Version + # run: | + # sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer + # - name: Build neblio-Qt for macOS and Run Tests + # run: | + # python -u ci_scripts/test_osx-gui_wallet.py -t - docker: - # Only Build Docker on Daily Scheduled Runs and releases to save time (no cache) - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-latest - steps: - - name: Checkout Docker Files - uses: actions/checkout@v1 - with: - repository: NeblioTeam/docker-nebliod - ref: refs/heads/master - - name: Move Docker Files - run: | - cd .. - rm -r neblio - mv docker-nebliod neblio - - name: Build and Publish to Docker Repository - uses: elgohr/Publish-Docker-Github-Action@master - with: - name: neblioteam/nebliod-build - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - workdir: docker-nebliod-build - buildargs: GITHUB_SHA + # docker: + # # Only Build Docker on Daily Scheduled Runs and releases to save time (no cache) + # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + # runs-on: ubuntu-latest + # steps: + # - name: Checkout Docker Files + # uses: actions/checkout@v1 + # with: + # repository: NeblioTeam/docker-nebliod + # ref: refs/heads/master + # - name: Move Docker Files + # run: | + # cd .. + # rm -r neblio + # mv docker-nebliod neblio + # - name: Build and Publish to Docker Repository + # uses: elgohr/Publish-Docker-Github-Action@master + # with: + # name: neblioteam/nebliod-build + # username: ${{ secrets.DOCKER_USERNAME }} + # password: ${{ secrets.DOCKER_PASSWORD }} + # workdir: docker-nebliod-build + # buildargs: GITHUB_SHA - rpi_docker: - # Only Build RPi Docker on Daily Scheduled Runs and releases as it takes 5 Hours - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-latest - steps: - - name: Checkout Docker Files - uses: actions/checkout@v1 - with: - repository: NeblioTeam/docker-nebliod - ref: refs/heads/master - - name: Move Docker Files - run: | - cd .. - rm -r neblio - mv docker-nebliod neblio - - name: Build and Publish to Docker Repository - uses: elgohr/Publish-Docker-Github-Action@master - with: - name: neblioteam/nebliod-build-rpi - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - workdir: docker-nebliod-build-rpi - buildargs: GITHUB_SHA + # rpi_docker: + # # Only Build RPi Docker on Daily Scheduled Runs and releases as it takes 5 Hours + # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + # runs-on: ubuntu-latest + # steps: + # - name: Checkout Docker Files + # uses: actions/checkout@v1 + # with: + # repository: NeblioTeam/docker-nebliod + # ref: refs/heads/master + # - name: Move Docker Files + # run: | + # cd .. + # rm -r neblio + # mv docker-nebliod neblio + # - name: Build and Publish to Docker Repository + # uses: elgohr/Publish-Docker-Github-Action@master + # with: + # name: neblioteam/nebliod-build-rpi + # username: ${{ secrets.DOCKER_USERNAME }} + # password: ${{ secrets.DOCKER_PASSWORD }} + # workdir: docker-nebliod-build-rpi + # buildargs: GITHUB_SHA - rpi_wallet: - # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- - - name: Set target_v env var - run: | - echo "target_v=rpi_wallet" >> $GITHUB_ENV - - name: Build neblio-Qt for Raspberry Pi - run: | - python -u ci_scripts/test_rpi-daemon-gui.py - echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - - name: If Release, Upload Release - if: startsWith(github.ref, 'refs/tags/v') - uses: AButler/upload-release-assets@v1.0 - with: - files: ${{ env.SOURCE_PATH }} - repo-token: ${{ secrets.GHAT2 }} - release-tag: ${{ env.RELEASE_TAG }} + # rpi_wallet: + # # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time + # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + # runs-on: ubuntu-22.04 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- + # - name: Set target_v env var + # run: | + # echo "target_v=rpi_wallet" >> $GITHUB_ENV + # - name: Build neblio-Qt for Raspberry Pi + # run: | + # python -u ci_scripts/test_rpi-daemon-gui.py + # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + # - name: If Release, Upload Release + # if: startsWith(github.ref, 'refs/tags/v') + # uses: AButler/upload-release-assets@v1.0 + # with: + # files: ${{ env.SOURCE_PATH }} + # repo-token: ${{ secrets.GHAT2 }} + # release-tag: ${{ env.RELEASE_TAG }} - rpi_wallet_test: - # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test- - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- - - name: Set target_v env var - run: | - echo "target_v=rpi_wallet_test" >> $GITHUB_ENV - - name: Build neblio-Qt for Raspberry Pi and Run Tests - run: | - python -u ci_scripts/test_rpi-daemon-gui.py -t - - name: Upload test artifacts on failure - uses: actions/upload-artifact@v2 - if: failure() - with: - name: test-artifacts - path: /home/runner/work/neblio/neblio/deploy/debug.log + # rpi_wallet_test: + # # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time + # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + # runs-on: ubuntu-22.04 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test- + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- + # - name: Set target_v env var + # run: | + # echo "target_v=rpi_wallet_test" >> $GITHUB_ENV + # - name: Build neblio-Qt for Raspberry Pi and Run Tests + # run: | + # python -u ci_scripts/test_rpi-daemon-gui.py -t + # - name: Upload test artifacts on failure + # uses: actions/upload-artifact@v2 + # if: failure() + # with: + # name: test-artifacts + # path: /home/runner/work/neblio/neblio/deploy/debug.log - rpi_daemon: - # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v1 - - name: ccache - uses: actions/cache@v2 - with: - path: .ccache - key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon-${{ github.sha }}-${{ github.run_id }} - restore-keys: | - ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon- - - name: Set target_v env var - run: | - echo "target_v=rpi_daemon" >> $GITHUB_ENV - - name: Build nebliod for Raspberry Pi - run: | - python -u ci_scripts/test_rpi-daemon-gui.py - echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - - name: If Release, Upload Release - if: startsWith(github.ref, 'refs/tags/v') - uses: AButler/upload-release-assets@v1.0 - with: - files: ${{ env.SOURCE_PATH }} - repo-token: ${{ secrets.GHAT2 }} - release-tag: ${{ env.RELEASE_TAG }} + # rpi_daemon: + # # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time + # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + # runs-on: ubuntu-22.04 + # steps: + # - uses: actions/checkout@v1 + # - name: ccache + # uses: actions/cache@v2 + # with: + # path: .ccache + # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon-${{ github.sha }}-${{ github.run_id }} + # restore-keys: | + # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon- + # - name: Set target_v env var + # run: | + # echo "target_v=rpi_daemon" >> $GITHUB_ENV + # - name: Build nebliod for Raspberry Pi + # run: | + # python -u ci_scripts/test_rpi-daemon-gui.py + # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + # - name: If Release, Upload Release + # if: startsWith(github.ref, 'refs/tags/v') + # uses: AButler/upload-release-assets@v1.0 + # with: + # files: ${{ env.SOURCE_PATH }} + # repo-token: ${{ secrets.GHAT2 }} + # release-tag: ${{ env.RELEASE_TAG }} From a6ad24281612baee15e041a64ee03fbba8548616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 12:02:55 +0200 Subject: [PATCH 109/129] Update unix makefile for Ledger --- wallet/makefile.unix | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/wallet/makefile.unix b/wallet/makefile.unix index ab3f200a5..6b00ccbe8 100644 --- a/wallet/makefile.unix +++ b/wallet/makefile.unix @@ -79,7 +79,9 @@ LIBS += \ -l ssl \ -l crypto \ -l z \ - -l sodium + -l sodium \ + -l hidapi-libusb \ + -l usb-1.0 ifdef NEBLIO_REST LIBS += -l restbed @@ -96,7 +98,8 @@ endif LIBS+= \ -Wl,-B$(LMODE2) \ -l dl \ - -l pthread + -l pthread \ + -l udev # Hardening # Make some classes of vulnerabilities unexploitable in case one is discovered. @@ -248,7 +251,16 @@ OBJS= \ obj/blockreject.o \ obj/blockmetadata.o \ obj/blockindexlrucache.o \ - obj/proposal.o + obj/proposal.o \ + obj/ledger/bip32.o \ + obj/ledger/error.o \ + obj/ledger/hid.o \ + obj/ledger/ledger.o \ + obj/ledger/speculos.o \ + obj/ledger/transport.o \ + obj/ledger/tx.o \ + obj/ledger/utils.o \ + obj/ledgerBridge.o ifdef NEBLIO_REST @@ -301,6 +313,14 @@ obj/ntp1/%.o: ntp1/%.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) +obj/ledger/%.o: ledger/%.cpp + ${MKDIR_P} obj obj/ledger + $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + obj/%.o: logging/%.cpp ${MKDIR_P} obj $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @@ -339,11 +359,13 @@ clean: -rm -f obj/*.o -rm -f obj-test/*.o -rm -f obj/ntp1/*.o + -rm -f obj/ledger/*.o -rm -f obj/json/*.o -rm -f obj/db/*.o -rm -f obj/*.P -rm -f obj-test/*.P -rm -f obj/ntp1/*.P + -rm -f obj/ledger/*.P -rm -f obj/json/*.P -rm -f obj/build.h -rm -rf obj From 244494ca0f1df6f04cefc701c5813cff31f7dbb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 12:07:42 +0200 Subject: [PATCH 110/129] Remove unused imports in CI script --- ci_scripts/test_linux-daemon-gui.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ci_scripts/test_linux-daemon-gui.py b/ci_scripts/test_linux-daemon-gui.py index e621a79bd..8aa14eecf 100644 --- a/ci_scripts/test_linux-daemon-gui.py +++ b/ci_scripts/test_linux-daemon-gui.py @@ -1,6 +1,4 @@ import os -import urllib2 -import multiprocessing as mp import neblio_ci_libs as nci nci.setup_travis_or_gh_actions_env_vars() From 6ad63d1f355c21a88641974afb57d3244569ccc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 12:51:25 +0200 Subject: [PATCH 111/129] Update CI actions to upload artifacts --- .github/workflows/ci_cd.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 09ebef980..84fcabbed 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -67,6 +67,11 @@ jobs: files: ${{ env.SOURCE_PATH }} repo-token: ${{ secrets.GHAT2 }} release-tag: ${{ env.RELEASE_TAG }} + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: nebliod + path: ${{ github.workspace }}/deploy/*nebliod*.tar.gz linux_wallet: @@ -94,6 +99,11 @@ jobs: files: ${{ env.SOURCE_PATH }} repo-token: ${{ secrets.GHAT2 }} release-tag: ${{ env.RELEASE_TAG }} + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: neblio-qt + path: ${{ github.workspace }}/deploy/*neblio-Qt*.tar.gz # linux_wallet_test: From da393dc76f9f7d93394140496b848829e0da692a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Fri, 14 Apr 2023 15:36:56 +0200 Subject: [PATCH 112/129] Enable (and fix) Windows CI action --- .github/workflows/ci_cd.yml | 49 ++++++++------- ci_scripts/test_win_x86-gui_wallet.py | 4 +- wallet/ledger/speculos.cpp | 86 --------------------------- wallet/ledger/speculos.h | 25 -------- wallet/ledger/transport.cpp | 4 +- wallet/wallet.pri | 2 - 6 files changed, 30 insertions(+), 140 deletions(-) delete mode 100644 wallet/ledger/speculos.cpp delete mode 100644 wallet/ledger/speculos.h diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 84fcabbed..4f66d10b4 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -126,28 +126,33 @@ jobs: # python -u ci_scripts/test_linux-daemon-gui.py -t - # windows_x86: - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86- - # - name: Build neblio-Qt for Windows x86 - # run: | - # python -u ci_scripts/test_win_x86-gui_wallet.py - # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - # - name: If Release, Upload Release - # if: startsWith(github.ref, 'refs/tags/v') - # uses: AButler/upload-release-assets@v1.0 - # with: - # files: ${{ env.SOURCE_PATH }} - # repo-token: ${{ secrets.GHAT2 }} - # release-tag: ${{ env.RELEASE_TAG }} + windows_x86: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-windows_x86- + - name: Build neblio-Qt for Windows x86 + run: | + python -u ci_scripts/test_win_x86-gui_wallet.py + echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + - name: If Release, Upload Release + if: startsWith(github.ref, 'refs/tags/v') + uses: AButler/upload-release-assets@v1.0 + with: + files: ${{ env.SOURCE_PATH }} + repo-token: ${{ secrets.GHAT2 }} + release-tag: ${{ env.RELEASE_TAG }} + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: neblio-qt + path: ${{ github.workspace }}/deploy/*neblio-Qt*.zip # osx: diff --git a/ci_scripts/test_win_x86-gui_wallet.py b/ci_scripts/test_win_x86-gui_wallet.py index 58dad0acd..6546d87b1 100644 --- a/ci_scripts/test_win_x86-gui_wallet.py +++ b/ci_scripts/test_win_x86-gui_wallet.py @@ -26,9 +26,9 @@ mxe_path = "/mxe/mxe/" # download the toolchain for windows -nci.call_with_err_code("wget --progress=dot:giga https://assets.nebl.io/dependencies/mxe.tar.gz") +nci.call_with_err_code("wget --progress=dot:giga https://neblio-mxe.s3.eu-central-1.amazonaws.com/mxe.tar.xz") # extract it -nci.call_with_err_code("tar -xf mxe.tar.gz") +nci.call_with_err_code("tar -xf mxe.tar.xz") # move it to /mxe, where it was built the first time nci.call_with_err_code("sudo mv mxe /") diff --git a/wallet/ledger/speculos.cpp b/wallet/ledger/speculos.cpp deleted file mode 100644 index c93e55dfa..000000000 --- a/wallet/ledger/speculos.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "ledger/error.h" -#include "ledger/speculos.h" -#include "ledger/utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ledger -{ - void Speculos::open() - { - if (!opened) - { - auto _sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (_sockfd < 0) - throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); - - auto server = "localhost"; - auto port = "9999"; - - struct sockaddr_in serv_addr; - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - serv_addr.sin_port = htons(9999); - if (connect(_sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) - throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); - - this->sockfd = _sockfd; - opened = true; - } - } - - int Speculos::send(const bytes &data) - { - bytes d; - AppendUint32(d, data.size()); - AppendVector(d, data); - auto result = write(sockfd, d.data(), d.size()); - if (result == -1) - return -1; - return result; - } - - int Speculos::receive(bytes &rdata) - { - uint8_t lengthB[4]; - if (read(sockfd, lengthB, sizeof(lengthB)) == -1) - return -1; - - auto length = BytesToInt(bytes(lengthB, lengthB + sizeof(lengthB))); - uint8_t d[length]; - if (read(sockfd, d, sizeof(d)) == -1) - return -1; - - uint8_t sw[2]; - if (read(sockfd, sw, sizeof(sw)) == -1) - return -1; - - rdata = bytes(d, d + sizeof(d)); - return BytesToInt(bytes(sw, sw + sizeof(sw))); - } - - void Speculos::close() noexcept - { - if (opened) - { - ::close(sockfd); - opened = false; - } - } - - bool Speculos::isOpen() const - { - return opened; - } -} // namespace ledger diff --git a/wallet/ledger/speculos.h b/wallet/ledger/speculos.h deleted file mode 100644 index dd65fdeed..000000000 --- a/wallet/ledger/speculos.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef LEDGER_SPECULOS_H -#define LEDGER_SPECULOS_H - -#include "ledger/comm.h" - -#include "hidapi/hidapi.h" - -namespace ledger -{ - class Speculos final : public Comm - { - public: - void open() override; - int send(const bytes &data) override; - int receive(bytes &rdata) override; - void close() noexcept override; - [[nodiscard]] bool isOpen() const override; - - private: - int sockfd = -1; - bool opened = false; - }; -} // namespace ledger - -#endif // LEDGER_SPECULOS_H diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 51c76f695..43c364895 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -1,6 +1,5 @@ #include "ledger/error.h" #include "ledger/hid.h" -#include "ledger/speculos.h" #include "ledger/transport.h" #include "ledger/utils.h" @@ -14,8 +13,7 @@ namespace ledger comm = std::unique_ptr(new HID()); break; case TransportType::SPECULOS: - comm = std::unique_ptr(new Speculos()); - break; + throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); } } diff --git a/wallet/wallet.pri b/wallet/wallet.pri index 9deccd643..177525e06 100644 --- a/wallet/wallet.pri +++ b/wallet/wallet.pri @@ -7,7 +7,6 @@ HEADERS += qt/bitcoingui.h \ ledger/error.h \ ledger/hid.h \ ledger/ledger.h \ - ledger/speculos.h \ ledger/transport.h \ ledger/tx.h \ ledger/utils.h \ @@ -217,7 +216,6 @@ SOURCES += qt/bitcoin.cpp \ ledger/error.cpp \ ledger/hid.cpp \ ledger/ledger.cpp \ - ledger/speculos.cpp \ ledger/transport.cpp \ ledger/tx.cpp \ ledger/utils.cpp \ From 0248c150f64857904b87743cc6ca5161b1c82f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Sat, 15 Apr 2023 11:23:17 +0200 Subject: [PATCH 113/129] Fix some imports, refactor keystore (to fix Windows build) --- wallet/key.h | 17 ++++++ wallet/keystore.h | 109 ++++++++++++++++++++++++++++++++++++++ wallet/ledger/ledger.cpp | 12 ++--- wallet/ledger/ledger.h | 8 +-- wallet/ledger/utils.cpp | 2 +- wallet/ledgerBridge.cpp | 1 - wallet/ledgerBridge.h | 2 - wallet/script.cpp | 37 +++---------- wallet/wallet.cpp | 11 +++- wallet/wallet.h | 110 +-------------------------------------- wallet/wallet_ismine.h | 2 +- wallet/walletdb.h | 17 ------ 12 files changed, 157 insertions(+), 171 deletions(-) diff --git a/wallet/key.h b/wallet/key.h index 73dca0d10..d68a404bc 100644 --- a/wallet/key.h +++ b/wallet/key.h @@ -179,4 +179,21 @@ class CKey /** Check that required EC support is available at runtime */ bool ECC_InitSanityCheck(void); +class CLedgerKey +{ +public: + CPubKey vchPubKey; + CKeyID accountPubKeyID; + uint32_t account; + bool isChange; + uint32_t index; + + CLedgerKey() {} + + CLedgerKey(const CPubKey& vchPubKeyIn, const CKeyID& accountPubKeyIDIn, uint32_t accountIn, bool isChangeIn, uint32_t indexIn) : + vchPubKey(vchPubKeyIn), accountPubKeyID(accountPubKeyIDIn), account(accountIn), isChange(isChangeIn), index(indexIn) {}; + + IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(accountPubKeyID); READWRITE(account); READWRITE(isChange); READWRITE(index);) +}; + #endif diff --git a/wallet/keystore.h b/wallet/keystore.h index ab369bbf4..93442ea56 100644 --- a/wallet/keystore.h +++ b/wallet/keystore.h @@ -34,6 +34,14 @@ class CKeyStore virtual bool HaveCScript(const CScriptID &hash) const =0; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; + virtual bool HaveLedgerKey(const CKeyID &address) const =0; + virtual bool AddLedgerKey(const CLedgerKey& ledgerKey) =0; + virtual bool LoadLedgerKey(const CLedgerKey& ledgerKey) =0; + virtual void GetLedgerKeys(std::set &setAddress) const =0; + virtual bool GetLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut) const =0; + virtual bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const =0; + virtual bool IsLedgerChangeKey(const CKeyID &address) const =0; + virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const { CKey key; @@ -46,6 +54,7 @@ class CKeyStore typedef std::map > KeyMap; typedef std::map ScriptMap; +typedef std::map LedgerKeysMap; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore @@ -53,6 +62,7 @@ class CBasicKeyStore : public CKeyStore protected: KeyMap mapKeys; ScriptMap mapScripts; + LedgerKeysMap mapLedgerKeys; public: bool AddKey(const CKey& key); @@ -92,6 +102,105 @@ class CBasicKeyStore : public CKeyStore } return false; } + + bool HaveLedgerKey(const CKeyID &address) const + { + bool result; + { + LOCK(cs_KeyStore); + result = (mapLedgerKeys.count(address) > 0); + } + return result; + } + + bool AddLedgerKey(const CLedgerKey& ledgerKey) + { + { + LOCK(cs_KeyStore); + mapLedgerKeys[ledgerKey.vchPubKey.GetID()] = ledgerKey; + } + } + + bool LoadLedgerKey(const CLedgerKey& ledgerKey) + { + { + LOCK(cs_KeyStore); + mapLedgerKeys[ledgerKey.vchPubKey.GetID()] = ledgerKey; + } + return true; + } + + void GetLedgerKeys(std::set &setAddress) const + { + setAddress.clear(); + { + LOCK(cs_KeyStore); + std::map ::const_iterator mi = mapLedgerKeys.begin(); + while (mi != mapLedgerKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + } + + bool GetLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut) const + { + { + LOCK(cs_KeyStore); + std::map ::const_iterator mi = mapLedgerKeys.find(address); + if (mi != mapLedgerKeys.end()) + { + ledgerKeyOut = (*mi).second; + return true; + } + } + return false; + } + + /* + Returns the "other" Ledger key for a given key. "Other" means that for a payment key + we return the corresponding change key and vice versa. + */ + bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const + { + { + LOCK(cs_KeyStore); + CLedgerKey ledgerKey; + if (!GetLedgerKey(address, ledgerKey)) { + return false; + } + + if (ledgerKey.isChange != isChange) { + throw std::runtime_error("Function called with wrong isChange parameter"); + } + + auto it = std::find_if(mapLedgerKeys.begin(), mapLedgerKeys.end(), + [&ledgerKey](const std::pair& entry) { + auto candidateKey = entry.second; + return candidateKey.isChange != ledgerKey.isChange + && candidateKey.accountPubKeyID == ledgerKey.accountPubKeyID + && candidateKey.index == ledgerKey.index; + }); + + if (it != mapLedgerKeys.end()) { + ledgerKeyOut = it->second; + return true; + } + } + return false; + } + + bool IsLedgerChangeKey(const CKeyID &address) const + { + CLedgerKey ledgerKey; + if (GetLedgerKey(address, ledgerKey)) + { + return ledgerKey.isChange; + } + return false; + } + virtual bool AddCScript(const CScript& redeemScript); virtual bool HaveCScript(const CScriptID &hash) const; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 0233ffbbf..9d34f4491 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -1,10 +1,8 @@ -#include "ledger.h" -#include "error.h" -#include "hash.h" -#include "utils.h" -#include "base58.h" -#include "bip32.h" -#include "tx.h" +#include "ledger/ledger.h" +#include "ledger/error.h" +#include "ledger/utils.h" +#include "ledger/bip32.h" +#include "ledger/tx.h" #include #include diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index bd08358cc..721d93388 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -1,10 +1,10 @@ #ifndef LEDGER_LEDGER_H #define LEDGER_LEDGER_H -#include "bip32.h" -#include "bytes.h" -#include "transport.h" -#include "tx.h" +#include "ledger/bip32.h" +#include "ledger/bytes.h" +#include "ledger/transport.h" +#include "ledger/tx.h" namespace ledger { diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index 33c8a36bb..0b1c2e49d 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -1,4 +1,4 @@ -#include "utils.h" +#include "ledger/utils.h" #include #include diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index a1dea565e..0f720c5bc 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -2,7 +2,6 @@ #include "ledger/utils.h" #include "ledgerBridge.h" #include "key.h" -#include "script.h" #include #include diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 0ac27919f..883fadeca 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -1,5 +1,4 @@ #ifndef LEDGERBRIDGE_H -#define LEDGERBRIDGE_H #include "ledger/bip32.h" #include "ledger/tx.h" @@ -9,7 +8,6 @@ #include "itxdb.h" #include "wallet.h" -#include "script.h" #include diff --git a/wallet/script.cpp b/wallet/script.cpp index b57c8ee16..b7936af23 100644 --- a/wallet/script.cpp +++ b/wallet/script.cpp @@ -17,7 +17,6 @@ using namespace boost; #include "script.h" #include "sync.h" #include "util.h" -#include "wallet.h" template std::vector ToByteVector(const T& in) @@ -1618,7 +1617,11 @@ class CKeyStoreIsMineVisitor : public boost::static_visitor isminetype operator()(const CNoDestination& /*dest*/) const { return isminetype::ISMINE_NO; } isminetype operator()(const CKeyID& keyID) const { - return keystore->HaveKey(keyID) ? isminetype::ISMINE_SPENDABLE : isminetype::ISMINE_NO; + return keystore->HaveKey(keyID) + ? isminetype::ISMINE_SPENDABLE + : keystore->HaveLedgerKey(keyID) + ? isminetype::ISMINE_LEDGER + : isminetype::ISMINE_NO; } isminetype operator()(const CScriptID& scriptID) const { @@ -1626,31 +1629,9 @@ class CKeyStoreIsMineVisitor : public boost::static_visitor } }; -class CWalletIsMineVisitor : public boost::static_visitor -{ -private: - const CWallet* wallet; - -public: - CWalletIsMineVisitor(const CWallet* walletIn): wallet(walletIn){}; - isminetype operator()(const CNoDestination& /*dest*/) const { return isminetype::ISMINE_NO; } - isminetype operator()(const CKeyID& keyID) const - { - return wallet->HaveLedgerKey(keyID) ? isminetype::ISMINE_LEDGER : isminetype::ISMINE_NO; - } - isminetype operator()(const CScriptID& scriptID) const {return isminetype::ISMINE_NO; } -}; - isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest) { - auto isMineKey = boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); - if (isMineKey != isminetype::ISMINE_NO) - { - return isMineKey; - } - - const CWallet *wallet = (CWallet*) &keystore; - return boost::apply_visitor(CWalletIsMineVisitor(wallet), dest); + return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest); } isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) @@ -1660,8 +1641,6 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) if (!Solver(CTxDB(), scriptPubKey, whichType, vSolutions)) return isminetype::ISMINE_NO; - // TODO GK - refactor - const CWallet *wallet = (CWallet*) &keystore; CKeyID keyID; switch (whichType) { case TX_NONSTANDARD: @@ -1671,14 +1650,14 @@ isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) keyID = CPubKey(vSolutions[0]).GetID(); if (keystore.HaveKey(keyID)) return isminetype::ISMINE_SPENDABLE; - if (wallet->HaveLedgerKey(keyID)) + if (keystore.HaveLedgerKey(keyID)) return isminetype::ISMINE_LEDGER; break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return isminetype::ISMINE_SPENDABLE; - if (wallet->HaveLedgerKey(keyID)) + if (keystore.HaveLedgerKey(keyID)) return isminetype::ISMINE_LEDGER; break; case TX_SCRIPTHASH: { diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index fb0b77491..6484eeb27 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -22,7 +22,6 @@ #include "ledger/ledger.h" #include "ledger/utils.h" #include "ledgerBridge.h" -#include "script.h" using namespace std; @@ -118,6 +117,16 @@ bool CWallet::AddKey(const CKey& key) return true; } +bool CWallet::AddLedgerKey(const CLedgerKey& ledgerKey) +{ + CBasicKeyStore::AddLedgerKey(ledgerKey); + if (!fFileBacked) + return true; + + // TODO DM do we need the lock here? + return CWalletDB(strWalletFile).WriteLedgerKey(ledgerKey); +} + bool CWallet::AddCryptedKey(const CPubKey& vchPubKey, const vector& vchCryptedSecret) { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) diff --git a/wallet/wallet.h b/wallet/wallet.h index 94c26d7c0..363a4a518 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -221,6 +221,7 @@ class CWallet : public CCryptoKeyStore CPubKey GenerateNewKey(); // Adds a key to the store, and saves it to disk. bool AddKey(const CKey& key); + bool AddLedgerKey(const CLedgerKey& ledgerKey); // Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); } // Load metadata (used by LoadWallet) @@ -376,7 +377,7 @@ class CWallet : public CCryptoKeyStore * @note called with lock cs_wallet held. */ boost::signals2::signal NotifyAddressBookChanged; @@ -442,113 +443,6 @@ class CWallet : public CCryptoKeyStore bool IsLabelUsableForLedger(const std::string& label); LabelAvailability CheckLabelAvailability(const std::string& label, bool isLedgerAddress); std::string ImportLedgerKey(int accountIndex, int addressIndex); - - mutable CCriticalSection cs_LedgerKeyStore; - - // TODO GK - LedgerKeyStore class? (this is copied from keystore) - std::map ledgerKeys; - bool HaveLedgerKey(const CKeyID &address) const - { - bool result; - { - LOCK(cs_LedgerKeyStore); - result = (ledgerKeys.count(address) > 0); - } - return result; - } - - bool AddLedgerKey(const CLedgerKey& ledgerKey) - { - { - LOCK(cs_LedgerKeyStore); - ledgerKeys[ledgerKey.vchPubKey.GetID()] = ledgerKey; - } - if (!fFileBacked) - return true; - - // TODO DM do we need the lock here? - return CWalletDB(strWalletFile).WriteLedgerKey(ledgerKey); - } - - bool LoadLedgerKey(const CLedgerKey& ledgerKey) - { - { - LOCK(cs_LedgerKeyStore); - ledgerKeys[ledgerKey.vchPubKey.GetID()] = ledgerKey; - } - return true; - } - - void GetLedgerKeys(std::set &setAddress) const - { - setAddress.clear(); - { - LOCK(cs_LedgerKeyStore); - std::map ::const_iterator mi = ledgerKeys.begin(); - while (mi != ledgerKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } - } - } - - bool GetLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut) const - { - { - LOCK(cs_LedgerKeyStore); - std::map ::const_iterator mi = ledgerKeys.find(address); - if (mi != ledgerKeys.end()) - { - ledgerKeyOut = (*mi).second; - return true; - } - } - return false; - } - - /* - Returns the "other" Ledger key for a given key. "Other" means that for a payment key - we return the corresponding change key and vice versa. - */ - bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const - { - { - LOCK(cs_LedgerKeyStore); - std::map ::const_iterator mi = ledgerKeys.find(address); - if (mi != ledgerKeys.end()) - { - auto key = (*mi).second; - if (key.isChange != isChange) { - throw std::runtime_error("Function called with wrong isChange parameter"); - } - - auto it = std::find_if(ledgerKeys.begin(), ledgerKeys.end(), - [&key](const std::pair& entry) { - auto candidateKey = entry.second; - return candidateKey.isChange != key.isChange - && candidateKey.accountPubKeyID == key.accountPubKeyID - && candidateKey.index == key.index; - }); - - if (it != ledgerKeys.end()) { - ledgerKeyOut = it->second; - return true; - } - } - } - return false; - } - - bool IsLedgerChangeKey(const CKeyID &address) const - { - CLedgerKey ledgerKey; - if (GetLedgerKey(address, ledgerKey)) - { - return ledgerKey.isChange; - } - return false; - } }; /** A key allocated from the key pool. */ diff --git a/wallet/wallet_ismine.h b/wallet/wallet_ismine.h index 58913bce7..829f2f25d 100644 --- a/wallet/wallet_ismine.h +++ b/wallet/wallet_ismine.h @@ -14,7 +14,7 @@ class CKeyStore; class CScript; /** IsMine() return codes */ -enum isminetype : uint +enum isminetype : uint_fast16_t { ISMINE_NO = 0, //! Indicates that we dont know how to create a scriptSig that would solve this if we were given the diff --git a/wallet/walletdb.h b/wallet/walletdb.h index 19c55495d..10882d7e2 100644 --- a/wallet/walletdb.h +++ b/wallet/walletdb.h @@ -38,23 +38,6 @@ class CKeyMetadata void SetNull(); }; -class CLedgerKey -{ -public: - CPubKey vchPubKey; - CKeyID accountPubKeyID; - uint32_t account; - bool isChange; - uint32_t index; - - CLedgerKey() {} - - CLedgerKey(const CPubKey& vchPubKeyIn, const CKeyID& accountPubKeyIDIn, uint32_t accountIn, bool isChangeIn, uint32_t indexIn) : - vchPubKey(vchPubKeyIn), accountPubKeyID(accountPubKeyIDIn), account(accountIn), isChange(isChangeIn), index(indexIn) {}; - - IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(accountPubKeyID); READWRITE(account); READWRITE(isChange); READWRITE(index);) -}; - /** Access to the wallet database (wallet.dat) */ class CWalletDB : public CDB { From d4afd2d222ac0ef758a74e4a119c235b4bcc5203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Sat, 15 Apr 2023 16:20:55 +0200 Subject: [PATCH 114/129] Remove Speculos files to fix linux build --- wallet/makefile.unix | 1 - 1 file changed, 1 deletion(-) diff --git a/wallet/makefile.unix b/wallet/makefile.unix index 6b00ccbe8..eb1734a06 100644 --- a/wallet/makefile.unix +++ b/wallet/makefile.unix @@ -256,7 +256,6 @@ OBJS= \ obj/ledger/error.o \ obj/ledger/hid.o \ obj/ledger/ledger.o \ - obj/ledger/speculos.o \ obj/ledger/transport.o \ obj/ledger/tx.o \ obj/ledger/utils.o \ From 016d5054bfc939f9c9859e3099fb55cb1f2e9a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Sat, 15 Apr 2023 16:21:29 +0200 Subject: [PATCH 115/129] Fix windows compilation error regarding `LedgerException.GetMessage` --- wallet/ledger/error.cpp | 26 ++++++++++++++++++++++++-- wallet/ledger/error.h | 22 ---------------------- wallet/wallet.cpp | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index 3c2f11ccd..22517df63 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -2,10 +2,32 @@ namespace ledger { - LedgerException::LedgerException(ErrorCode errorCodeIn) : errorCode(errorCodeIn), message(GetMessage(errorCodeIn)) {} + std::string GetLedgerErrorCodeMessage(ErrorCode errorCode) + { + switch (errorCode) + { + case ErrorCode::DEVICE_NOT_FOUND: + return "Ledger Not Found"; + case ErrorCode::DEVICE_OPEN_FAIL: + return "Failed to open Ledger"; + case ErrorCode::DEVICE_DATA_SEND_FAIL: + return "Failed to send data to Ledger"; + case ErrorCode::DEVICE_DATA_RECV_FAIL: + return "Failed to receive data from Ledger"; + case ErrorCode::APDU_INVALID_CMD: + return "Invalid Ledger data"; + case ErrorCode::INVALID_TRUSTED_INPUT: + return "Invalid trusted input"; + case ErrorCode::UNRECOGNIZED_ERROR: + default: + return "Unrecognized error"; + } + } + + LedgerException::LedgerException(ErrorCode errorCodeIn) : errorCode(errorCodeIn), message(GetLedgerErrorCodeMessage(errorCodeIn)) {} ErrorCode LedgerException::GetErrorCode() const { return errorCode; } std::string LedgerException::GetMessage() const { return message; } - const char *LedgerException::what() const noexcept { return message.c_str(); } + const char *LedgerException::what() const noexcept { return message.c_str(); } } diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index 9c2d86383..ca40b607a 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -16,28 +16,6 @@ namespace ledger { class LedgerException : public std::exception { public: - static std::string GetMessage(ErrorCode errorCode) - { - switch (errorCode) - { - case ErrorCode::DEVICE_NOT_FOUND: - return "Ledger Not Found"; - case ErrorCode::DEVICE_OPEN_FAIL: - return "Failed to open Ledger"; - case ErrorCode::DEVICE_DATA_SEND_FAIL: - return "Failed to send data to Ledger"; - case ErrorCode::DEVICE_DATA_RECV_FAIL: - return "Failed to receive data from Ledger"; - case ErrorCode::APDU_INVALID_CMD: - return "Invalid Ledger data"; - case ErrorCode::INVALID_TRUSTED_INPUT: - return "Invalid trusted input"; - case ErrorCode::UNRECOGNIZED_ERROR: - default: - return "Unrecognized error"; - } - } - LedgerException(ErrorCode errorCodeIn); ~LedgerException() noexcept override = default; diff --git a/wallet/wallet.cpp b/wallet/wallet.cpp index 6484eeb27..c4ca48843 100644 --- a/wallet/wallet.cpp +++ b/wallet/wallet.cpp @@ -2536,7 +2536,7 @@ bool CWallet::CreateTransaction(const ITxDB& txdb, const vector 0); } catch (const ledger::LedgerException& e) { - CreateErrorMsg(errorMsg, "Error while signing Ledger transaction: " + e.GetMessage()); + CreateErrorMsg(errorMsg, "Error while signing Ledger transaction."); return false; } } From d0ef81eda85e9d5d430f7217f01f11e5e2113a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Sat, 15 Apr 2023 18:29:14 +0200 Subject: [PATCH 116/129] Rename ci artifacts --- .github/workflows/ci_cd.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 4f66d10b4..404018450 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -70,7 +70,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: nebliod + name: nebliod-linux.tar.gz path: ${{ github.workspace }}/deploy/*nebliod*.tar.gz @@ -102,7 +102,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: neblio-qt + name: neblio-qt-linux.tar.gz path: ${{ github.workspace }}/deploy/*neblio-Qt*.tar.gz @@ -151,7 +151,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: neblio-qt + name: neblio-qt-windows.zip path: ${{ github.workspace }}/deploy/*neblio-Qt*.zip From fda745aec90d726f00d846012f6e7a1e02b746a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 17 Apr 2023 14:38:58 +0200 Subject: [PATCH 117/129] Fix CMake build --- .github/workflows/ci_cd.yml | 46 ++--- CMakeLists.txt | 15 ++ ci_scripts/test_linux-daemon-cmake.py | 7 +- cmake/FindHIDAPI.cmake | 231 ++++++++++++++++++++++++++ wallet/test/CMakeLists.txt | 1 + 5 files changed, 275 insertions(+), 25 deletions(-) create mode 100644 cmake/FindHIDAPI.cmake diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 404018450..385ed04b4 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -17,29 +17,29 @@ env: jobs: - # linux_daemon_cmake: - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake- - # - name: Set target_v env var - # run: | - # echo "target_v=linux_daemon_cmake" >> $GITHUB_ENV - # - name: Build nebliod for Linux using cmake - # run: | - # python -u ci_scripts/test_linux-daemon-cmake.py - # - name: Upload test artifacts on failure - # uses: actions/upload-artifact@v2 - # if: failure() - # with: - # name: test-artifacts - # path: /tmp/bitcoin* + linux_daemon_cmake: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_daemon_cmake- + - name: Set target_v env var + run: | + echo "target_v=linux_daemon_cmake" >> $GITHUB_ENV + - name: Build nebliod for Linux using cmake + run: | + python -u ci_scripts/test_linux-daemon-cmake.py + - name: Upload test artifacts on failure + uses: actions/upload-artifact@v2 + if: failure() + with: + name: test-artifacts + path: /tmp/bitcoin* linux_daemon: runs-on: ubuntu-22.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4923aa8bd..f91aca607 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,7 @@ find_package(PkgConfig REQUIRED) find_package(Boost 1.65 COMPONENTS system random filesystem thread regex program_options iostreams atomic REQUIRED) find_package(Threads REQUIRED) find_package(sodium REQUIRED) +find_package(HIDAPI REQUIRED) set(ATOMIC32_TEST_CODE " #include @@ -595,6 +596,8 @@ add_library(gui_lib STATIC wallet/qt/votesdatamodel.cpp wallet/qt/votesdataview.cpp wallet/qt/votestablecelldelegate.cpp + wallet/qt/ledger_ui/ledgermessagebox.cpp + wallet/qt/ledger_ui/ledgeruiutils.cpp ${QRCODE_SOURCES} ) @@ -625,6 +628,8 @@ add_library(ui_lib STATIC wallet/qt/ui_sendcoinsentry.h wallet/qt/ui_signverifymessagedialog.h wallet/qt/ui_transactiondescdialog.h + wallet/qt/ledger_ui/ledgermessagebox.h + wallet/qt/ledger_ui/ledgeruiutils.h ) target_link_libraries(ui_lib @@ -740,6 +745,15 @@ add_library(core_lib STATIC wallet/blockmetadata.cpp wallet/blockindexlrucache.cpp wallet/proposal.cpp + wallet/ledger/bytes.h + wallet/ledger/bip32.cpp + wallet/ledger/error.cpp + wallet/ledger/hid.cpp + wallet/ledger/ledger.cpp + wallet/ledger/transport.cpp + wallet/ledger/tx.cpp + wallet/ledger/utils.cpp + wallet/ledgerBridge.cpp ) target_link_libraries(core_lib @@ -750,6 +764,7 @@ target_link_libraries(core_lib db_lib utils_lib ${ATOMIC_LINKER_LIBS} + ${HIDAPI_LIBRARIES} ) add_library(curltools_lib STATIC diff --git a/ci_scripts/test_linux-daemon-cmake.py b/ci_scripts/test_linux-daemon-cmake.py index 7ece0d95a..11b3e3d70 100644 --- a/ci_scripts/test_linux-daemon-cmake.py +++ b/ci_scripts/test_linux-daemon-cmake.py @@ -14,7 +14,7 @@ packages_to_install = \ [ "ccache", -"qt5-default", +"qtbase5-dev", "qt5-qmake", "qtbase5-dev-tools", "qttools5-dev-tools", @@ -43,7 +43,10 @@ "python3-setuptools", "wget", "cmake", -"nano" +"nano", +"libudev-dev", +"libusb-1.0-0-dev", +"libhidapi-dev" ] nci.install_packages_debian(packages_to_install) diff --git a/cmake/FindHIDAPI.cmake b/cmake/FindHIDAPI.cmake new file mode 100644 index 000000000..932bb3600 --- /dev/null +++ b/cmake/FindHIDAPI.cmake @@ -0,0 +1,231 @@ +#.rst: +# FindHIDAPI +# ---------- +# +# Try to find HIDAPI library, from http://www.signal11.us/oss/hidapi/ +# +# Cache Variables: (probably not for direct use in your scripts) +# HIDAPI_INCLUDE_DIR +# HIDAPI_LIBRARY +# +# Non-cache variables you might use in your CMakeLists.txt: +# HIDAPI_FOUND +# HIDAPI_INCLUDE_DIRS +# HIDAPI_LIBRARIES +# +# COMPONENTS +# ^^^^^^^^^^ +# +# This module respects several COMPONENTS specifying the backend you prefer: +# ``any`` (the default), ``libusb``, and ``hidraw``. +# The availablility of the latter two depends on your platform. +# +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` target ``HIDAPI::hidapi`` (in all cases or +# if no components specified), ``HIDAPI::hidapi-libusb`` (if you requested the libusb component), +# and ``HIDAPI::hidapi-hidraw`` (if you requested the hidraw component), +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# ``HIDAPI_FOUND`` +# True if HIDAPI or the requested components (if any) were found. +# +# We recommend using the imported targets instead of the following. +# +# ``HIDAPI_INCLUDE_DIRS`` +# ``HIDAPI_LIBRARIES`` +# +# Original Author: +# 2009-2021 Ryan Pavlik +# http://academic.cleardefinition.com +# +# Copyright 2009-2010, Iowa State University +# Copyright 2019-2021, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HIDAPI_ROOT_DIR + "${HIDAPI_ROOT_DIR}" + CACHE PATH "Root to search for HIDAPI") + +# Clean up components +if(HIDAPI_FIND_COMPONENTS) + if(WIN32 OR APPLE) + # This makes no sense on Windows or Mac, which have native APIs + list(REMOVE HIDAPI_FIND_COMPONENTS libusb) + endif() + + if(NOT ${CMAKE_SYSTEM} MATCHES "Linux") + # hidraw is only on linux + list(REMOVE HIDAPI_FIND_COMPONENTS hidraw) + endif() +endif() +if(NOT HIDAPI_FIND_COMPONENTS) + # Default to any + set(HIDAPI_FIND_COMPONENTS any) +endif() + +# Ask pkg-config for hints +if(NOT ANDROID) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + set(_old_prefix_path "${CMAKE_PREFIX_PATH}") + # So pkg-config uses HIDAPI_ROOT_DIR too. + if(HIDAPI_ROOT_DIR) + list(APPEND CMAKE_PREFIX_PATH ${HIDAPI_ROOT_DIR}) + endif() + pkg_check_modules(PC_HIDAPI_LIBUSB QUIET hidapi-libusb) + pkg_check_modules(PC_HIDAPI_HIDRAW QUIET hidapi-hidraw) + # Restore + set(CMAKE_PREFIX_PATH "${_old_prefix_path}") + endif() +endif() + +# Actually search +find_library( + HIDAPI_UNDECORATED_LIBRARY + NAMES hidapi + PATHS "${HIDAPI_ROOT_DIR}" + PATH_SUFFIXES lib) + +find_library( + HIDAPI_LIBUSB_LIBRARY + NAMES hidapi hidapi-libusb + PATHS "${HIDAPI_ROOT_DIR}" + PATH_SUFFIXES lib + HINTS ${PC_HIDAPI_LIBUSB_LIBRARY_DIRS}) + +if(CMAKE_SYSTEM MATCHES "Linux") + find_library( + HIDAPI_HIDRAW_LIBRARY + NAMES hidapi-hidraw + HINTS ${PC_HIDAPI_HIDRAW_LIBRARY_DIRS}) +endif() + +find_path( + HIDAPI_INCLUDE_DIR + NAMES hidapi.h + PATHS "${HIDAPI_ROOT_DIR}" + PATH_SUFFIXES hidapi include include/hidapi + HINTS ${PC_HIDAPI_HIDRAW_INCLUDE_DIRS} ${PC_HIDAPI_LIBUSB_INCLUDE_DIRS}) + +find_package(Threads QUIET) + +### +# Compute the "I don't care which backend" library +### +set(HIDAPI_LIBRARY) + +# First, try to use a preferred backend if supplied +if("${HIDAPI_FIND_COMPONENTS}" MATCHES "libusb" + AND HIDAPI_LIBUSB_LIBRARY + AND NOT HIDAPI_LIBRARY) + set(HIDAPI_LIBRARY ${HIDAPI_LIBUSB_LIBRARY}) +endif() +if("${HIDAPI_FIND_COMPONENTS}" MATCHES "hidraw" + AND HIDAPI_HIDRAW_LIBRARY + AND NOT HIDAPI_LIBRARY) + set(HIDAPI_LIBRARY ${HIDAPI_HIDRAW_LIBRARY}) +endif() + +# Then, if we don't have a preferred one, settle for anything. +if(NOT HIDAPI_LIBRARY) + if(HIDAPI_LIBUSB_LIBRARY) + set(HIDAPI_LIBRARY ${HIDAPI_LIBUSB_LIBRARY}) + elseif(HIDAPI_HIDRAW_LIBRARY) + set(HIDAPI_LIBRARY ${HIDAPI_HIDRAW_LIBRARY}) + elseif(HIDAPI_UNDECORATED_LIBRARY) + set(HIDAPI_LIBRARY ${HIDAPI_UNDECORATED_LIBRARY}) + endif() +endif() + +### +# Determine if the various requested components are found. +### +set(_hidapi_component_required_vars) + +foreach(_comp IN LISTS HIDAPI_FIND_COMPONENTS) + if("${_comp}" STREQUAL "any") + list(APPEND _hidapi_component_required_vars HIDAPI_INCLUDE_DIR + HIDAPI_LIBRARY) + if(HIDAPI_INCLUDE_DIR AND EXISTS "${HIDAPI_LIBRARY}") + set(HIDAPI_any_FOUND TRUE) + mark_as_advanced(HIDAPI_INCLUDE_DIR) + else() + set(HIDAPI_any_FOUND FALSE) + endif() + + elseif("${_comp}" STREQUAL "libusb") + list(APPEND _hidapi_component_required_vars HIDAPI_INCLUDE_DIR + HIDAPI_LIBUSB_LIBRARY) + if(HIDAPI_INCLUDE_DIR AND EXISTS "${HIDAPI_LIBUSB_LIBRARY}") + set(HIDAPI_libusb_FOUND TRUE) + mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBUSB_LIBRARY) + else() + set(HIDAPI_libusb_FOUND FALSE) + endif() + + elseif("${_comp}" STREQUAL "hidraw") + list(APPEND _hidapi_component_required_vars HIDAPI_INCLUDE_DIR + HIDAPI_HIDRAW_LIBRARY) + if(HIDAPI_INCLUDE_DIR AND EXISTS "${HIDAPI_HIDRAW_LIBRARY}") + set(HIDAPI_hidraw_FOUND TRUE) + mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_HIDRAW_LIBRARY) + else() + set(HIDAPI_hidraw_FOUND FALSE) + endif() + + else() + message(WARNING "${_comp} is not a recognized HIDAPI component") + set(HIDAPI_${_comp}_FOUND FALSE) + endif() +endforeach() +unset(_comp) + +### +# FPHSA call +### +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + HIDAPI + REQUIRED_VARS ${_hidapi_component_required_vars} THREADS_FOUND + HANDLE_COMPONENTS) +if(HIDAPI_FOUND) + set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") + set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") + if(NOT TARGET HIDAPI::hidapi) + add_library(HIDAPI::hidapi UNKNOWN IMPORTED) + set_target_properties( + HIDAPI::hidapi + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${HIDAPI_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${HIDAPI_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LIBRARIES Threads::Threads) + endif() +endif() + +if(HIDAPI_libusb_FOUND AND NOT TARGET HIDAPI::hidapi-libusb) + add_library(HIDAPI::hidapi-libusb UNKNOWN IMPORTED) + set_target_properties( + HIDAPI::hidapi-libusb + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${HIDAPI_LIBUSB_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${HIDAPI_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LIBRARIES Threads::Threads) +endif() + +if(HIDAPI_hidraw_FOUND AND NOT TARGET HIDAPI::hidapi-hidraw) + add_library(HIDAPI::hidapi-hidraw UNKNOWN IMPORTED) + set_target_properties( + HIDAPI::hidapi-hidraw + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${HIDAPI_HIDRAW_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${HIDAPI_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LIBRARIES Threads::Threads) +endif() \ No newline at end of file diff --git a/wallet/test/CMakeLists.txt b/wallet/test/CMakeLists.txt index b59957cce..5b9c761b6 100644 --- a/wallet/test/CMakeLists.txt +++ b/wallet/test/CMakeLists.txt @@ -74,6 +74,7 @@ target_link_libraries(neblio-tests ${CURL_LIBS} ${OPENSSL_LIBS} ${ZLIB_LIBRARIES} + ${HIDAPI_LIBRARIES} ) set_target_properties(neblio-tests PROPERTIES COMPILE_FLAGS -Wno-unused-function) From 0c21bf8fa0f41be45ecb1719889900193fd15b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 17 Apr 2023 14:39:58 +0200 Subject: [PATCH 118/129] Re-enable other jobs --- .github/workflows/ci_cd.yml | 374 ++++++++++++++++++------------------ 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 385ed04b4..49d7542fa 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -106,24 +106,24 @@ jobs: path: ${{ github.workspace }}/deploy/*neblio-Qt*.tar.gz - # linux_wallet_test: - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test- - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet- - # - name: Set target_v env var - # run: | - # echo "target_v=linux_wallet_test" >> $GITHUB_ENV - # - name: Build neblio-Qt for Linux and Run Tests - # run: | - # python -u ci_scripts/test_linux-daemon-gui.py -t + linux_wallet_test: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet_test- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-linux_wallet- + - name: Set target_v env var + run: | + echo "target_v=linux_wallet_test" >> $GITHUB_ENV + - name: Build neblio-Qt for Linux and Run Tests + run: | + python -u ci_scripts/test_linux-daemon-gui.py -t windows_x86: @@ -155,186 +155,186 @@ jobs: path: ${{ github.workspace }}/deploy/*neblio-Qt*.zip - # osx: - # runs-on: macos-10.15 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- - # - name: Switch Xcode Version - # run: | - # sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer - # - name: Build neblio-Qt for macOS - # run: | - # python -u ci_scripts/test_osx-gui_wallet.py - # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - # - name: If Release, Upload Release - # if: startsWith(github.ref, 'refs/tags/v') - # uses: AButler/upload-release-assets@v1.0 - # with: - # files: ${{ env.SOURCE_PATH }} - # repo-token: ${{ secrets.GHAT2 }} - # release-tag: ${{ env.RELEASE_TAG }} + osx: + runs-on: macos-10.15 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- + - name: Switch Xcode Version + run: | + sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer + - name: Build neblio-Qt for macOS + run: | + python -u ci_scripts/test_osx-gui_wallet.py + echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + - name: If Release, Upload Release + if: startsWith(github.ref, 'refs/tags/v') + uses: AButler/upload-release-assets@v1.0 + with: + files: ${{ env.SOURCE_PATH }} + repo-token: ${{ secrets.GHAT2 }} + release-tag: ${{ env.RELEASE_TAG }} - # osx_test: - # runs-on: macos-10.15 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test- - # ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- - # - name: Switch Xcode Version - # run: | - # sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer - # - name: Build neblio-Qt for macOS and Run Tests - # run: | - # python -u ci_scripts/test_osx-gui_wallet.py -t + osx_test: + runs-on: macos-10.15 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-osx_test- + ${{ runner.OS }}-${{ env.CACHE_VER }}-osx- + - name: Switch Xcode Version + run: | + sudo xcode-select -s /Applications/Xcode_11.2.1.app/Contents/Developer + - name: Build neblio-Qt for macOS and Run Tests + run: | + python -u ci_scripts/test_osx-gui_wallet.py -t - # docker: - # # Only Build Docker on Daily Scheduled Runs and releases to save time (no cache) - # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - # runs-on: ubuntu-latest - # steps: - # - name: Checkout Docker Files - # uses: actions/checkout@v1 - # with: - # repository: NeblioTeam/docker-nebliod - # ref: refs/heads/master - # - name: Move Docker Files - # run: | - # cd .. - # rm -r neblio - # mv docker-nebliod neblio - # - name: Build and Publish to Docker Repository - # uses: elgohr/Publish-Docker-Github-Action@master - # with: - # name: neblioteam/nebliod-build - # username: ${{ secrets.DOCKER_USERNAME }} - # password: ${{ secrets.DOCKER_PASSWORD }} - # workdir: docker-nebliod-build - # buildargs: GITHUB_SHA + docker: + # Only Build Docker on Daily Scheduled Runs and releases to save time (no cache) + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - name: Checkout Docker Files + uses: actions/checkout@v1 + with: + repository: NeblioTeam/docker-nebliod + ref: refs/heads/master + - name: Move Docker Files + run: | + cd .. + rm -r neblio + mv docker-nebliod neblio + - name: Build and Publish to Docker Repository + uses: elgohr/Publish-Docker-Github-Action@master + with: + name: neblioteam/nebliod-build + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + workdir: docker-nebliod-build + buildargs: GITHUB_SHA - # rpi_docker: - # # Only Build RPi Docker on Daily Scheduled Runs and releases as it takes 5 Hours - # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - # runs-on: ubuntu-latest - # steps: - # - name: Checkout Docker Files - # uses: actions/checkout@v1 - # with: - # repository: NeblioTeam/docker-nebliod - # ref: refs/heads/master - # - name: Move Docker Files - # run: | - # cd .. - # rm -r neblio - # mv docker-nebliod neblio - # - name: Build and Publish to Docker Repository - # uses: elgohr/Publish-Docker-Github-Action@master - # with: - # name: neblioteam/nebliod-build-rpi - # username: ${{ secrets.DOCKER_USERNAME }} - # password: ${{ secrets.DOCKER_PASSWORD }} - # workdir: docker-nebliod-build-rpi - # buildargs: GITHUB_SHA + rpi_docker: + # Only Build RPi Docker on Daily Scheduled Runs and releases as it takes 5 Hours + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - name: Checkout Docker Files + uses: actions/checkout@v1 + with: + repository: NeblioTeam/docker-nebliod + ref: refs/heads/master + - name: Move Docker Files + run: | + cd .. + rm -r neblio + mv docker-nebliod neblio + - name: Build and Publish to Docker Repository + uses: elgohr/Publish-Docker-Github-Action@master + with: + name: neblioteam/nebliod-build-rpi + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + workdir: docker-nebliod-build-rpi + buildargs: GITHUB_SHA - # rpi_wallet: - # # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time - # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- - # - name: Set target_v env var - # run: | - # echo "target_v=rpi_wallet" >> $GITHUB_ENV - # - name: Build neblio-Qt for Raspberry Pi - # run: | - # python -u ci_scripts/test_rpi-daemon-gui.py - # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - # - name: If Release, Upload Release - # if: startsWith(github.ref, 'refs/tags/v') - # uses: AButler/upload-release-assets@v1.0 - # with: - # files: ${{ env.SOURCE_PATH }} - # repo-token: ${{ secrets.GHAT2 }} - # release-tag: ${{ env.RELEASE_TAG }} + rpi_wallet: + # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- + - name: Set target_v env var + run: | + echo "target_v=rpi_wallet" >> $GITHUB_ENV + - name: Build neblio-Qt for Raspberry Pi + run: | + python -u ci_scripts/test_rpi-daemon-gui.py + echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + - name: If Release, Upload Release + if: startsWith(github.ref, 'refs/tags/v') + uses: AButler/upload-release-assets@v1.0 + with: + files: ${{ env.SOURCE_PATH }} + repo-token: ${{ secrets.GHAT2 }} + release-tag: ${{ env.RELEASE_TAG }} - # rpi_wallet_test: - # # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time - # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test- - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- - # - name: Set target_v env var - # run: | - # echo "target_v=rpi_wallet_test" >> $GITHUB_ENV - # - name: Build neblio-Qt for Raspberry Pi and Run Tests - # run: | - # python -u ci_scripts/test_rpi-daemon-gui.py -t - # - name: Upload test artifacts on failure - # uses: actions/upload-artifact@v2 - # if: failure() - # with: - # name: test-artifacts - # path: /home/runner/work/neblio/neblio/deploy/debug.log + rpi_wallet_test: + # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet_test- + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_wallet- + - name: Set target_v env var + run: | + echo "target_v=rpi_wallet_test" >> $GITHUB_ENV + - name: Build neblio-Qt for Raspberry Pi and Run Tests + run: | + python -u ci_scripts/test_rpi-daemon-gui.py -t + - name: Upload test artifacts on failure + uses: actions/upload-artifact@v2 + if: failure() + with: + name: test-artifacts + path: /home/runner/work/neblio/neblio/deploy/debug.log - # rpi_daemon: - # # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time - # if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v1 - # - name: ccache - # uses: actions/cache@v2 - # with: - # path: .ccache - # key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon-${{ github.sha }}-${{ github.run_id }} - # restore-keys: | - # ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon- - # - name: Set target_v env var - # run: | - # echo "target_v=rpi_daemon" >> $GITHUB_ENV - # - name: Build nebliod for Raspberry Pi - # run: | - # python -u ci_scripts/test_rpi-daemon-gui.py - # echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - # - name: If Release, Upload Release - # if: startsWith(github.ref, 'refs/tags/v') - # uses: AButler/upload-release-assets@v1.0 - # with: - # files: ${{ env.SOURCE_PATH }} - # repo-token: ${{ secrets.GHAT2 }} - # release-tag: ${{ env.RELEASE_TAG }} + rpi_daemon: + # Only Build RPi on Daily Scheduled Runs and releases as it takes a long time + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v1 + - name: ccache + uses: actions/cache@v2 + with: + path: .ccache + key: ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon-${{ github.sha }}-${{ github.run_id }} + restore-keys: | + ${{ runner.OS }}-${{ env.CACHE_VER }}-22.04-rpi_daemon- + - name: Set target_v env var + run: | + echo "target_v=rpi_daemon" >> $GITHUB_ENV + - name: Build nebliod for Raspberry Pi + run: | + python -u ci_scripts/test_rpi-daemon-gui.py + echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV + - name: If Release, Upload Release + if: startsWith(github.ref, 'refs/tags/v') + uses: AButler/upload-release-assets@v1.0 + with: + files: ${{ env.SOURCE_PATH }} + repo-token: ${{ secrets.GHAT2 }} + release-tag: ${{ env.RELEASE_TAG }} From 2172c4c5d4f1382f9ee758a23b0f2675ecf2e68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Kereke=C5=A1?= Date: Mon, 17 Apr 2023 22:26:22 +0200 Subject: [PATCH 119/129] Format files modified during of Ledger integration --- wallet/coincontrol.h | 2 +- wallet/key.h | 17 +- wallet/keystore.h | 135 ++++---- wallet/ledger/bip32.cpp | 245 ++++++-------- wallet/ledger/bip32.h | 73 ++--- wallet/ledger/bytes.h | 5 +- wallet/ledger/comm.h | 21 +- wallet/ledger/error.cpp | 53 +-- wallet/ledger/error.h | 43 +-- wallet/ledger/hid.cpp | 275 ++++++++-------- wallet/ledger/hid.h | 33 +- wallet/ledger/ledger.cpp | 398 +++++++++++------------ wallet/ledger/ledger.h | 76 ++--- wallet/ledger/transport.cpp | 100 +++--- wallet/ledger/transport.h | 37 +-- wallet/ledger/tx.cpp | 68 ++-- wallet/ledger/tx.h | 111 ++++--- wallet/ledger/utils.cpp | 343 +++++++++---------- wallet/ledger/utils.h | 39 ++- wallet/ledgerBridge.cpp | 185 +++++------ wallet/ledgerBridge.h | 63 ++-- wallet/qt/addresstablemodel.cpp | 95 +++--- wallet/qt/addresstablemodel.h | 43 +-- wallet/qt/coincontroldialog.cpp | 5 +- wallet/qt/coincontroldialog.h | 2 +- wallet/qt/editaddressdialog.cpp | 150 ++++----- wallet/qt/editaddressdialog.h | 26 +- wallet/qt/ledger_ui/ledgermessagebox.cpp | 52 +-- wallet/qt/ledger_ui/ledgermessagebox.h | 33 +- wallet/qt/ledger_ui/ledgeruiutils.cpp | 14 +- wallet/qt/ledger_ui/ledgeruiutils.h | 4 +- wallet/qt/newstakedelegationdialog.h | 2 +- wallet/qt/ntp1/issuenewntp1tokendialog.h | 2 +- wallet/qt/sendcoinsdialog.cpp | 47 +-- wallet/qt/sendcoinsdialog.h | 20 +- wallet/qt/sendcoinsentry.cpp | 7 +- wallet/qt/transactiondesc.cpp | 16 +- wallet/qt/transactionrecord.cpp | 6 +- wallet/qt/transactionview.cpp | 5 +- wallet/qt/walletmodel.cpp | 9 +- wallet/qt/walletmodel.h | 2 +- wallet/rpcdump.cpp | 32 +- wallet/rpcwallet.cpp | 28 +- wallet/script.cpp | 8 +- wallet/transaction.h | 3 +- wallet/wallet.cpp | 110 ++++--- wallet/wallet.h | 17 +- wallet/wallet_ismine.h | 5 +- 48 files changed, 1482 insertions(+), 1583 deletions(-) diff --git a/wallet/coincontrol.h b/wallet/coincontrol.h index 28194d0de..529ad3507 100644 --- a/wallet/coincontrol.h +++ b/wallet/coincontrol.h @@ -15,7 +15,7 @@ class CCoinControl void SetNull() { destChange = CNoDestination(); - fLedgerTx = false; + fLedgerTx = false; setSelected.clear(); } diff --git a/wallet/key.h b/wallet/key.h index d68a404bc..bd65d4ff3 100644 --- a/wallet/key.h +++ b/wallet/key.h @@ -84,7 +84,7 @@ class CPubKey IMPLEMENT_SERIALIZE(READWRITE(vchPubKey);) - void SetRaw(std::vector rawKey) {vchPubKey = rawKey;} + void SetRaw(std::vector rawKey) { vchPubKey = rawKey; } CKeyID GetID() const { return CKeyID(Hash160(vchPubKey)); } @@ -182,18 +182,21 @@ bool ECC_InitSanityCheck(void); class CLedgerKey { public: - CPubKey vchPubKey; - CKeyID accountPubKeyID; + CPubKey vchPubKey; + CKeyID accountPubKeyID; uint32_t account; - bool isChange; + bool isChange; uint32_t index; CLedgerKey() {} - CLedgerKey(const CPubKey& vchPubKeyIn, const CKeyID& accountPubKeyIDIn, uint32_t accountIn, bool isChangeIn, uint32_t indexIn) : - vchPubKey(vchPubKeyIn), accountPubKeyID(accountPubKeyIDIn), account(accountIn), isChange(isChangeIn), index(indexIn) {}; + CLedgerKey(const CPubKey& vchPubKeyIn, const CKeyID& accountPubKeyIDIn, uint32_t accountIn, + bool isChangeIn, uint32_t indexIn) + : vchPubKey(vchPubKeyIn), accountPubKeyID(accountPubKeyIDIn), account(accountIn), + isChange(isChangeIn), index(indexIn){}; - IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(accountPubKeyID); READWRITE(account); READWRITE(isChange); READWRITE(index);) + IMPLEMENT_SERIALIZE(READWRITE(vchPubKey); READWRITE(accountPubKeyID); READWRITE(account); + READWRITE(isChange); READWRITE(index);) }; #endif diff --git a/wallet/keystore.h b/wallet/keystore.h index 93442ea56..efda22624 100644 --- a/wallet/keystore.h +++ b/wallet/keystore.h @@ -21,28 +21,29 @@ class CKeyStore virtual ~CKeyStore() {} // Add a key to the store. - virtual bool AddKey(const CKey& key) =0; + virtual bool AddKey(const CKey& key) = 0; // Check whether a key corresponding to a given address is present in the store. - virtual bool HaveKey(const CKeyID &address) const =0; - virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0; - virtual void GetKeys(std::set &setAddress) const =0; - virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + virtual bool HaveKey(const CKeyID& address) const = 0; + virtual bool GetKey(const CKeyID& address, CKey& keyOut) const = 0; + virtual void GetKeys(std::set& setAddress) const = 0; + virtual bool GetPubKey(const CKeyID& address, CPubKey& vchPubKeyOut) const; // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 - virtual bool AddCScript(const CScript& redeemScript) =0; - virtual bool HaveCScript(const CScriptID &hash) const =0; - virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; - - virtual bool HaveLedgerKey(const CKeyID &address) const =0; - virtual bool AddLedgerKey(const CLedgerKey& ledgerKey) =0; - virtual bool LoadLedgerKey(const CLedgerKey& ledgerKey) =0; - virtual void GetLedgerKeys(std::set &setAddress) const =0; - virtual bool GetLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut) const =0; - virtual bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const =0; - virtual bool IsLedgerChangeKey(const CKeyID &address) const =0; - - virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const + virtual bool AddCScript(const CScript& redeemScript) = 0; + virtual bool HaveCScript(const CScriptID& hash) const = 0; + virtual bool GetCScript(const CScriptID& hash, CScript& redeemScriptOut) const = 0; + + virtual bool HaveLedgerKey(const CKeyID& address) const = 0; + virtual bool AddLedgerKey(const CLedgerKey& ledgerKey) = 0; + virtual bool LoadLedgerKey(const CLedgerKey& ledgerKey) = 0; + virtual void GetLedgerKeys(std::set& setAddress) const = 0; + virtual bool GetLedgerKey(const CKeyID& address, CLedgerKey& ledgerKeyOut) const = 0; + virtual bool GetOtherLedgerKey(const CKeyID& address, CLedgerKey& ledgerKeyOut, + bool isChange) const = 0; + virtual bool IsLedgerChangeKey(const CKeyID& address) const = 0; + + virtual bool GetSecret(const CKeyID& address, CSecret& vchSecret, bool& fCompressed) const { CKey key; if (!GetKey(address, key)) @@ -52,21 +53,21 @@ class CKeyStore } }; -typedef std::map > KeyMap; -typedef std::map ScriptMap; -typedef std::map LedgerKeysMap; +typedef std::map> KeyMap; +typedef std::map ScriptMap; +typedef std::map LedgerKeysMap; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore { protected: - KeyMap mapKeys; - ScriptMap mapScripts; + KeyMap mapKeys; + ScriptMap mapScripts; LedgerKeysMap mapLedgerKeys; public: bool AddKey(const CKey& key); - bool HaveKey(const CKeyID &address) const + bool HaveKey(const CKeyID& address) const { bool result; { @@ -75,26 +76,24 @@ class CBasicKeyStore : public CKeyStore } return result; } - void GetKeys(std::set &setAddress) const + void GetKeys(std::set& setAddress) const { setAddress.clear(); { LOCK(cs_KeyStore); KeyMap::const_iterator mi = mapKeys.begin(); - while (mi != mapKeys.end()) - { + while (mi != mapKeys.end()) { setAddress.insert((*mi).first); mi++; } } } - bool GetKey(const CKeyID &address, CKey &keyOut) const + bool GetKey(const CKeyID& address, CKey& keyOut) const { { LOCK(cs_KeyStore); KeyMap::const_iterator mi = mapKeys.find(address); - if (mi != mapKeys.end()) - { + if (mi != mapKeys.end()) { keyOut.Reset(); keyOut.SetSecret((*mi).second.first, (*mi).second.second); return true; @@ -103,7 +102,7 @@ class CBasicKeyStore : public CKeyStore return false; } - bool HaveLedgerKey(const CKeyID &address) const + bool HaveLedgerKey(const CKeyID& address) const { bool result; { @@ -130,27 +129,25 @@ class CBasicKeyStore : public CKeyStore return true; } - void GetLedgerKeys(std::set &setAddress) const + void GetLedgerKeys(std::set& setAddress) const { setAddress.clear(); { LOCK(cs_KeyStore); - std::map ::const_iterator mi = mapLedgerKeys.begin(); - while (mi != mapLedgerKeys.end()) - { + std::map::const_iterator mi = mapLedgerKeys.begin(); + while (mi != mapLedgerKeys.end()) { setAddress.insert((*mi).first); mi++; } } } - bool GetLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut) const + bool GetLedgerKey(const CKeyID& address, CLedgerKey& ledgerKeyOut) const { { LOCK(cs_KeyStore); - std::map ::const_iterator mi = mapLedgerKeys.find(address); - if (mi != mapLedgerKeys.end()) - { + std::map::const_iterator mi = mapLedgerKeys.find(address); + if (mi != mapLedgerKeys.end()) { ledgerKeyOut = (*mi).second; return true; } @@ -162,7 +159,7 @@ class CBasicKeyStore : public CKeyStore Returns the "other" Ledger key for a given key. "Other" means that for a payment key we return the corresponding change key and vice versa. */ - bool GetOtherLedgerKey(const CKeyID &address, CLedgerKey &ledgerKeyOut, bool isChange) const + bool GetOtherLedgerKey(const CKeyID& address, CLedgerKey& ledgerKeyOut, bool isChange) const { { LOCK(cs_KeyStore); @@ -170,18 +167,19 @@ class CBasicKeyStore : public CKeyStore if (!GetLedgerKey(address, ledgerKey)) { return false; } - + if (ledgerKey.isChange != isChange) { throw std::runtime_error("Function called with wrong isChange parameter"); } - auto it = std::find_if(mapLedgerKeys.begin(), mapLedgerKeys.end(), - [&ledgerKey](const std::pair& entry) { - auto candidateKey = entry.second; - return candidateKey.isChange != ledgerKey.isChange - && candidateKey.accountPubKeyID == ledgerKey.accountPubKeyID - && candidateKey.index == ledgerKey.index; - }); + auto it = + std::find_if(mapLedgerKeys.begin(), mapLedgerKeys.end(), + [&ledgerKey](const std::pair& entry) { + auto candidateKey = entry.second; + return candidateKey.isChange != ledgerKey.isChange && + candidateKey.accountPubKeyID == ledgerKey.accountPubKeyID && + candidateKey.index == ledgerKey.index; + }); if (it != mapLedgerKeys.end()) { ledgerKeyOut = it->second; @@ -191,22 +189,21 @@ class CBasicKeyStore : public CKeyStore return false; } - bool IsLedgerChangeKey(const CKeyID &address) const + bool IsLedgerChangeKey(const CKeyID& address) const { CLedgerKey ledgerKey; - if (GetLedgerKey(address, ledgerKey)) - { + if (GetLedgerKey(address, ledgerKey)) { return ledgerKey.isChange; } return false; } virtual bool AddCScript(const CScript& redeemScript); - virtual bool HaveCScript(const CScriptID &hash) const; - virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; + virtual bool HaveCScript(const CScriptID& hash) const; + virtual bool GetCScript(const CScriptID& hash, CScript& redeemScriptOut) const; }; -typedef std::map > > CryptedKeyMap; +typedef std::map>> CryptedKeyMap; /** Keystore which keeps the private keys encrypted. * It derives from the basic key store, which is used if no encryption is active. @@ -231,14 +228,9 @@ class CCryptoKeyStore : public CBasicKeyStore bool Unlock(const CKeyingMaterial& vMasterKeyIn); public: - CCryptoKeyStore() : fUseCrypto(false) - { - } + CCryptoKeyStore() : fUseCrypto(false) {} - bool IsCrypted() const - { - return fUseCrypto; - } + bool IsCrypted() const { return fUseCrypto; } bool IsLocked() const { @@ -254,9 +246,10 @@ class CCryptoKeyStore : public CBasicKeyStore bool Lock(); - virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); - bool AddKey(const CKey& key); - bool HaveKey(const CKeyID &address) const + virtual bool AddCryptedKey(const CPubKey& vchPubKey, + const std::vector& vchCryptedSecret); + bool AddKey(const CKey& key); + bool HaveKey(const CKeyID& address) const { { LOCK(cs_KeyStore); @@ -266,19 +259,17 @@ class CCryptoKeyStore : public CBasicKeyStore } return false; } - bool GetKey(const CKeyID &address, CKey& keyOut) const; - bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - void GetKeys(std::set &setAddress) const + bool GetKey(const CKeyID& address, CKey& keyOut) const; + bool GetPubKey(const CKeyID& address, CPubKey& vchPubKeyOut) const; + void GetKeys(std::set& setAddress) const { - if (!IsCrypted()) - { + if (!IsCrypted()) { CBasicKeyStore::GetKeys(setAddress); return; } setAddress.clear(); CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - while (mi != mapCryptedKeys.end()) - { + while (mi != mapCryptedKeys.end()) { setAddress.insert((*mi).first); mi++; } @@ -287,7 +278,7 @@ class CCryptoKeyStore : public CBasicKeyStore /* Wallet status (encrypted, locked) changed. * Note: Called without locks held. */ - boost::signals2::signal NotifyStatusChanged; + boost::signals2::signal NotifyStatusChanged; }; #endif diff --git a/wallet/ledger/bip32.cpp b/wallet/ledger/bip32.cpp index a14c9e191..91d1a1039 100644 --- a/wallet/ledger/bip32.cpp +++ b/wallet/ledger/bip32.cpp @@ -3,177 +3,146 @@ #include -namespace ledger -{ - uint32_t Bip32Path::Harden(uint32_t n) const - { - return n | 0x80000000; - } +namespace ledger { +uint32_t Bip32Path::Harden(uint32_t n) const { return n | 0x80000000; } - uint32_t Bip32Path::Unharden(uint32_t n) const - { - return n & ~(0x80000000); - } +uint32_t Bip32Path::Unharden(uint32_t n) const { return n & ~(0x80000000); } - bool Bip32Path::IsHardened(uint32_t n) const - { - return n == Harden(n); +bool Bip32Path::IsHardened(uint32_t n) const { return n == Harden(n); } + +Bip32Path Bip32Path::ToChangePath() const +{ + if (type == Bip32PathType::Account) { + throw std::runtime_error("Account keypath cannot be converted to change path!"); } - Bip32Path Bip32Path::ToChangePath() const - { - if (type == Bip32PathType::Account) - { - throw std::runtime_error("Account keypath cannot be converted to change path!"); - } + return Bip32Path(components[ACCOUNT_INDEX], true, components[ADDRESS_INDEX_INDEX]); +} - return Bip32Path(components[ACCOUNT_INDEX], true, components[ADDRESS_INDEX_INDEX]); - } - - Bip32Path::Bip32Path(const std::string &keyPathStr) - { - std::vector _components; - std::stringstream ss(keyPathStr); - std::string item; - bool first = true; - while (std::getline(ss, item, '/')) - { - if (item.compare("m") == 0) - { - if (first) - { - first = false; - continue; - } - throw std::runtime_error("Invalid keypath"); - } - // Finds whether it is hardened - uint32_t path = 0; - size_t pos = item.find("'"); - if (pos != std::string::npos) - { - // The hardened tick can only be in the last index of the string - if (pos != item.size() - 1) - { - throw std::runtime_error("Invalid keypath"); - } - path |= 0x80000000; - item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick +Bip32Path::Bip32Path(const std::string& keyPathStr) +{ + std::vector _components; + std::stringstream ss(keyPathStr); + std::string item; + bool first = true; + while (std::getline(ss, item, '/')) { + if (item.compare("m") == 0) { + if (first) { + first = false; + continue; } - - // Ensure this is only numbers - if (item.find_first_not_of("0123456789") != std::string::npos) - { + throw std::runtime_error("Invalid keypath"); + } + // Finds whether it is hardened + uint32_t path = 0; + size_t pos = item.find("'"); + if (pos != std::string::npos) { + // The hardened tick can only be in the last index of the string + if (pos != item.size() - 1) { throw std::runtime_error("Invalid keypath"); } - - _components.push_back(std::stoul(item) | path); - - first = false; + path |= 0x80000000; + item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick } - if (_components.size() == 3) - { - type = Bip32PathType::Account; - } - else if (_components.size() == 5) - { - type = Bip32PathType::Address; - } - else - { - throw std::runtime_error("Invalid keypath size"); + // Ensure this is only numbers + if (item.find_first_not_of("0123456789") != std::string::npos) { + throw std::runtime_error("Invalid keypath"); } - if (_components[PURPOSE_INDEX] != Harden(BIP32_PURPOSE)) - { - throw std::runtime_error("Invalid keypath purpose"); - } + _components.push_back(std::stoul(item) | path); - if (_components[COIN_TYPE_INDEX] != Harden(BIP32_COIN_TYPE)) - { - throw std::runtime_error("Invalid keypath coin type"); - } + first = false; + } - if (!IsHardened(_components[ACCOUNT_INDEX])) - { - throw std::runtime_error("Invalid keypath account"); - } + if (_components.size() == 3) { + type = Bip32PathType::Account; + } else if (_components.size() == 5) { + type = Bip32PathType::Address; + } else { + throw std::runtime_error("Invalid keypath size"); + } - if (type == Bip32PathType::Address) - { - if (IsHardened(_components[CHANGE_INDEX])) - { - throw std::runtime_error("Invalid keypath change"); - } + if (_components[PURPOSE_INDEX] != Harden(BIP32_PURPOSE)) { + throw std::runtime_error("Invalid keypath purpose"); + } - if (IsHardened(_components[ADDRESS_INDEX_INDEX])) - { - throw std::runtime_error("Invalid keypath index"); - } - } + if (_components[COIN_TYPE_INDEX] != Harden(BIP32_COIN_TYPE)) { + throw std::runtime_error("Invalid keypath coin type"); + } - components.push_back(BIP32_PURPOSE); - components.push_back(BIP32_COIN_TYPE); - components.push_back(_components[ACCOUNT_INDEX]); + if (!IsHardened(_components[ACCOUNT_INDEX])) { + throw std::runtime_error("Invalid keypath account"); + } - if (type == Bip32PathType::Address) - { - components.push_back(_components[CHANGE_INDEX]); - components.push_back(_components[ADDRESS_INDEX_INDEX]); + if (type == Bip32PathType::Address) { + if (IsHardened(_components[CHANGE_INDEX])) { + throw std::runtime_error("Invalid keypath change"); + } + + if (IsHardened(_components[ADDRESS_INDEX_INDEX])) { + throw std::runtime_error("Invalid keypath index"); } } - Bip32Path::Bip32Path(uint32_t account) - { - type = Bip32PathType::Account; + components.push_back(BIP32_PURPOSE); + components.push_back(BIP32_COIN_TYPE); + components.push_back(_components[ACCOUNT_INDEX]); - components.push_back(BIP32_PURPOSE); - components.push_back(BIP32_COIN_TYPE); - components.push_back(account); + if (type == Bip32PathType::Address) { + components.push_back(_components[CHANGE_INDEX]); + components.push_back(_components[ADDRESS_INDEX_INDEX]); } +} - Bip32Path::Bip32Path(const std::string &account, bool isChange, const std::string &index) - : Bip32Path(std::stoul(account), isChange, std::stoul(index)) {}; +Bip32Path::Bip32Path(uint32_t account) +{ + type = Bip32PathType::Account; - Bip32Path::Bip32Path(uint32_t account, bool isChange, uint32_t index) - : Bip32Path(account) - { - type = Bip32PathType::Address; + components.push_back(BIP32_PURPOSE); + components.push_back(BIP32_COIN_TYPE); + components.push_back(account); +} - components.push_back(isChange ? 1 : 0); - components.push_back(index); - }; +Bip32Path::Bip32Path(const std::string& account, bool isChange, const std::string& index) + : Bip32Path(std::stoul(account), isChange, std::stoul(index)){}; - bytes Bip32Path::Serialize() const - { - bytes serializedKeyPath; +Bip32Path::Bip32Path(uint32_t account, bool isChange, uint32_t index) : Bip32Path(account) +{ + type = Bip32PathType::Address; - AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); - AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); - AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); + components.push_back(isChange ? 1 : 0); + components.push_back(index); +}; - if (type == Bip32PathType::Address) - { - AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); - AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); - } +bytes Bip32Path::Serialize() const +{ + bytes serializedKeyPath; - return serializedKeyPath; + AppendUint32(serializedKeyPath, Harden(components[PURPOSE_INDEX])); + AppendUint32(serializedKeyPath, Harden(components[COIN_TYPE_INDEX])); + AppendUint32(serializedKeyPath, Harden(components[ACCOUNT_INDEX])); + + if (type == Bip32PathType::Address) { + AppendUint32(serializedKeyPath, components[CHANGE_INDEX]); + AppendUint32(serializedKeyPath, components[ADDRESS_INDEX_INDEX]); } - std::string Bip32Path::ToString() const - { - std::stringstream ss; + return serializedKeyPath; +} - ss << "m/" << components[PURPOSE_INDEX] << "'/" << components[COIN_TYPE_INDEX] << "'/" << components[ACCOUNT_INDEX] << "'"; +std::string Bip32Path::ToString() const +{ + std::stringstream ss; - if (type == Bip32PathType::Address) - { - ss << "/" << components[CHANGE_INDEX]; - ss << "/" << components[ADDRESS_INDEX_INDEX]; - } + ss << "m/" << components[PURPOSE_INDEX] << "'/" << components[COIN_TYPE_INDEX] << "'/" + << components[ACCOUNT_INDEX] << "'"; + + if (type == Bip32PathType::Address) { + ss << "/" << components[CHANGE_INDEX]; + ss << "/" << components[ADDRESS_INDEX_INDEX]; + } - return ss.str(); - } + return ss.str(); +} } // namespace ledger diff --git a/wallet/ledger/bip32.h b/wallet/ledger/bip32.h index f39ad59df..7c2434f12 100644 --- a/wallet/ledger/bip32.h +++ b/wallet/ledger/bip32.h @@ -4,47 +4,46 @@ #include "ledger/bytes.h" #include -#include #include +#include -namespace ledger +namespace ledger { +class Bip32Path { - class Bip32Path +public: + const int PURPOSE_INDEX = 0; + const int COIN_TYPE_INDEX = 1; + const int ACCOUNT_INDEX = 2; + const int CHANGE_INDEX = 3; + const int ADDRESS_INDEX_INDEX = 4; + + const int BIP32_PURPOSE = 44; + const int BIP32_COIN_TYPE = 146; + + Bip32Path(const std::string& keyPath); + Bip32Path(uint32_t account); + Bip32Path(const std::string& account, bool isChange, const std::string& index); + Bip32Path(uint32_t account, bool isChange, uint32_t index); + + uint32_t Harden(uint32_t n) const; + uint32_t Unharden(uint32_t n) const; + bool IsHardened(uint32_t n) const; + + Bip32Path ToChangePath() const; + + bytes Serialize() const; + std::string ToString() const; + +private: + enum class Bip32PathType { - public: - const int PURPOSE_INDEX = 0; - const int COIN_TYPE_INDEX = 1; - const int ACCOUNT_INDEX = 2; - const int CHANGE_INDEX = 3; - const int ADDRESS_INDEX_INDEX = 4; - - const int BIP32_PURPOSE = 44; - const int BIP32_COIN_TYPE = 146; - - Bip32Path(const std::string &keyPath); - Bip32Path(uint32_t account); - Bip32Path(const std::string &account, bool isChange, const std::string &index); - Bip32Path(uint32_t account, bool isChange, uint32_t index); - - uint32_t Harden(uint32_t n) const; - uint32_t Unharden(uint32_t n) const; - bool IsHardened(uint32_t n) const; - - Bip32Path ToChangePath() const; - - bytes Serialize() const; - std::string ToString() const; - - private: - enum class Bip32PathType - { - Account, - Address - }; - - Bip32PathType type; - std::vector components; + Account, + Address }; -} + + Bip32PathType type; + std::vector components; +}; +} // namespace ledger #endif // LEDGER_BIP32_H diff --git a/wallet/ledger/bytes.h b/wallet/ledger/bytes.h index 903f4b9f3..df9091fbe 100644 --- a/wallet/ledger/bytes.h +++ b/wallet/ledger/bytes.h @@ -4,9 +4,8 @@ #include #include -namespace ledger -{ - typedef std::vector bytes; +namespace ledger { +typedef std::vector bytes; } #endif // LEDGER_BYTES_H diff --git a/wallet/ledger/comm.h b/wallet/ledger/comm.h index 73f20fa45..fb2740117 100644 --- a/wallet/ledger/comm.h +++ b/wallet/ledger/comm.h @@ -6,19 +6,18 @@ #include "ledger/bytes.h" -namespace ledger +namespace ledger { +class Comm { - class Comm - { - public: - virtual ~Comm() = default; +public: + virtual ~Comm() = default; - virtual void open() = 0; - virtual int send(const bytes &data) = 0; - virtual int receive(bytes &rdata) = 0; - virtual void close() = 0; - [[nodiscard]] virtual bool isOpen() const = 0; - }; + virtual void open() = 0; + virtual int send(const bytes& data) = 0; + virtual int receive(bytes& rdata) = 0; + virtual void close() = 0; + [[nodiscard]] virtual bool isOpen() const = 0; +}; } // namespace ledger #endif // LEDGER_COMM_H diff --git a/wallet/ledger/error.cpp b/wallet/ledger/error.cpp index 22517df63..91af26acd 100644 --- a/wallet/ledger/error.cpp +++ b/wallet/ledger/error.cpp @@ -1,33 +1,34 @@ #include "ledger/error.h" -namespace ledger +namespace ledger { +std::string GetLedgerErrorCodeMessage(ErrorCode errorCode) { - std::string GetLedgerErrorCodeMessage(ErrorCode errorCode) - { - switch (errorCode) - { - case ErrorCode::DEVICE_NOT_FOUND: - return "Ledger Not Found"; - case ErrorCode::DEVICE_OPEN_FAIL: - return "Failed to open Ledger"; - case ErrorCode::DEVICE_DATA_SEND_FAIL: - return "Failed to send data to Ledger"; - case ErrorCode::DEVICE_DATA_RECV_FAIL: - return "Failed to receive data from Ledger"; - case ErrorCode::APDU_INVALID_CMD: - return "Invalid Ledger data"; - case ErrorCode::INVALID_TRUSTED_INPUT: - return "Invalid trusted input"; - case ErrorCode::UNRECOGNIZED_ERROR: - default: - return "Unrecognized error"; - } + switch (errorCode) { + case ErrorCode::DEVICE_NOT_FOUND: + return "Ledger Not Found"; + case ErrorCode::DEVICE_OPEN_FAIL: + return "Failed to open Ledger"; + case ErrorCode::DEVICE_DATA_SEND_FAIL: + return "Failed to send data to Ledger"; + case ErrorCode::DEVICE_DATA_RECV_FAIL: + return "Failed to receive data from Ledger"; + case ErrorCode::APDU_INVALID_CMD: + return "Invalid Ledger data"; + case ErrorCode::INVALID_TRUSTED_INPUT: + return "Invalid trusted input"; + case ErrorCode::UNRECOGNIZED_ERROR: + default: + return "Unrecognized error"; } +} - LedgerException::LedgerException(ErrorCode errorCodeIn) : errorCode(errorCodeIn), message(GetLedgerErrorCodeMessage(errorCodeIn)) {} +LedgerException::LedgerException(ErrorCode errorCodeIn) + : errorCode(errorCodeIn), message(GetLedgerErrorCodeMessage(errorCodeIn)) +{ +} - ErrorCode LedgerException::GetErrorCode() const { return errorCode; } - std::string LedgerException::GetMessage() const { return message; } +ErrorCode LedgerException::GetErrorCode() const { return errorCode; } +std::string LedgerException::GetMessage() const { return message; } - const char *LedgerException::what() const noexcept { return message.c_str(); } -} +const char* LedgerException::what() const noexcept { return message.c_str(); } +} // namespace ledger diff --git a/wallet/ledger/error.h b/wallet/ledger/error.h index ca40b607a..4cd08e6da 100644 --- a/wallet/ledger/error.h +++ b/wallet/ledger/error.h @@ -4,29 +4,32 @@ #include namespace ledger { - enum class ErrorCode { - DEVICE_NOT_FOUND, - DEVICE_OPEN_FAIL, - DEVICE_DATA_SEND_FAIL, - DEVICE_DATA_RECV_FAIL, - APDU_INVALID_CMD, - INVALID_TRUSTED_INPUT, - UNRECOGNIZED_ERROR = 999, - }; +enum class ErrorCode +{ + DEVICE_NOT_FOUND, + DEVICE_OPEN_FAIL, + DEVICE_DATA_SEND_FAIL, + DEVICE_DATA_RECV_FAIL, + APDU_INVALID_CMD, + INVALID_TRUSTED_INPUT, + UNRECOGNIZED_ERROR = 999, +}; - class LedgerException : public std::exception { - public: - LedgerException(ErrorCode errorCodeIn); - ~LedgerException() noexcept override = default; +class LedgerException : public std::exception +{ +public: + LedgerException(ErrorCode errorCodeIn); + ~LedgerException() noexcept override = default; - ErrorCode GetErrorCode() const; - std::string GetMessage() const; + ErrorCode GetErrorCode() const; + std::string GetMessage() const; - const char *what() const noexcept override; - private: - ErrorCode errorCode; - std::string message; - }; + const char* what() const noexcept override; + +private: + ErrorCode errorCode; + std::string message; +}; } // namespace ledger #endif // LEDGER_ERROR_H diff --git a/wallet/ledger/hid.cpp b/wallet/ledger/hid.cpp index 9893b38ec..1f70d469c 100644 --- a/wallet/ledger/hid.cpp +++ b/wallet/ledger/hid.cpp @@ -1,152 +1,137 @@ -#include "ledger/error.h" #include "ledger/hid.h" +#include "ledger/error.h" #include "ledger/utils.h" #include -namespace ledger +namespace ledger { +void HID::open() +{ + if (!opened) { + auto devices = enumerateDevices(vendorId); + if (devices.empty()) { + throw LedgerException(ErrorCode::DEVICE_NOT_FOUND); + } + + path = devices.at(0); + device = hid_open_path(path.c_str()); + if (!device) { + throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); + } + + hid_set_nonblocking(device, true); + + opened = true; + } +} + +int HID::send(const bytes& data) +{ + if (data.empty()) + return -1; + + auto dataNew = IntToBytes(data.size(), 2); + dataNew.insert(dataNew.end(), data.begin(), data.end()); + + size_t offset = 0; + size_t seqIdx = 0; + size_t length = 0; + + while (offset < dataNew.size()) { + // Header: channel (0x0101), tag (0x05), sequence index + bytes header{0x01, 0x01, 0x05}; + + auto seqIdxBytes = IntToBytes(seqIdx, 2); + header.insert(header.end(), seqIdxBytes.begin(), seqIdxBytes.end()); + + bytes::iterator it; + if (dataNew.size() - offset < 64 - header.size()) { + it = dataNew.end(); + } else { + it = dataNew.begin() + offset + 64 - header.size(); + } + + bytes dataChunk{dataNew.begin() + offset, it}; + dataChunk.insert(dataChunk.begin(), header.begin(), header.end()); + dataChunk.insert(dataChunk.begin(), 0x00); + + if (hid_write(device, dataChunk.data(), dataChunk.size()) == -1) + return -1; + + length += dataChunk.size(); + offset += 64 - header.size(); + seqIdx += 1; + } + + return length; +} + +int HID::receive(bytes& rdata) +{ + int seqIdx = 0; + uint8_t buf[64]; + + hid_set_nonblocking(device, false); + if (hid_read_timeout(device, buf, sizeof(buf), timeoutMs) <= 0) + return -1; + hid_set_nonblocking(device, true); + + bytes dataChunk(buf, buf + sizeof(buf)); + + assert(dataChunk[0] == 0x01); + assert(dataChunk[1] == 0x01); + assert(dataChunk[2] == 0x05); + + auto seqIdxBytes = IntToBytes(seqIdx, 2); + assert(seqIdxBytes[0] == dataChunk[3]); + assert(seqIdxBytes[1] == dataChunk[4]); + + auto dataLen = BytesToInt(bytes(dataChunk.begin() + 5, dataChunk.begin() + 7)); + bytes data(dataChunk.begin() + 7, dataChunk.end()); + + while (data.size() < dataLen) { + uint8_t readBytes[64]; + if (hid_read_timeout(device, readBytes, sizeof(readBytes), 1000) == -1) + return -1; + bytes tmp(readBytes, readBytes + sizeof(readBytes)); + data.insert(data.end(), tmp.begin() + 5, tmp.end()); + } + + auto sw = BytesToInt(bytes(data.begin() + dataLen - 2, data.begin() + dataLen)); + rdata = bytes(data.begin(), data.begin() + dataLen - 2); + + return sw; +} + +void HID::close() noexcept +{ + if (opened) { + hid_close(device); + opened = false; + } + hid_exit(); +} + +bool HID::isOpen() const { return opened; } + +std::vector HID::enumerateDevices(unsigned short vendorId) noexcept { - void HID::open() - { - if (!opened) - { - auto devices = enumerateDevices(vendorId); - if (devices.empty()) - { - throw LedgerException(ErrorCode::DEVICE_NOT_FOUND); - } - - path = devices.at(0); - device = hid_open_path(path.c_str()); - if (!device) - { - throw LedgerException(ErrorCode::DEVICE_OPEN_FAIL); - } - - hid_set_nonblocking(device, true); - - opened = true; - } - } - - int HID::send(const bytes &data) - { - if (data.empty()) - return -1; - - auto dataNew = IntToBytes(data.size(), 2); - dataNew.insert(dataNew.end(), data.begin(), data.end()); - - size_t offset = 0; - size_t seqIdx = 0; - size_t length = 0; - - while (offset < dataNew.size()) - { - // Header: channel (0x0101), tag (0x05), sequence index - bytes header{0x01, 0x01, 0x05}; - - auto seqIdxBytes = IntToBytes(seqIdx, 2); - header.insert(header.end(), seqIdxBytes.begin(), seqIdxBytes.end()); - - bytes::iterator it; - if (dataNew.size() - offset < 64 - header.size()) - { - it = dataNew.end(); - } - else - { - it = dataNew.begin() + offset + 64 - header.size(); - } - - bytes dataChunk{dataNew.begin() + offset, it}; - dataChunk.insert(dataChunk.begin(), header.begin(), header.end()); - dataChunk.insert(dataChunk.begin(), 0x00); - - if (hid_write(device, dataChunk.data(), dataChunk.size()) == -1) - return -1; - - length += dataChunk.size(); - offset += 64 - header.size(); - seqIdx += 1; - } - - return length; - } - - int HID::receive(bytes &rdata) - { - int seqIdx = 0; - uint8_t buf[64]; - - hid_set_nonblocking(device, false); - if (hid_read_timeout(device, buf, sizeof(buf), timeoutMs) <= 0) - return -1; - hid_set_nonblocking(device, true); - - bytes dataChunk(buf, buf + sizeof(buf)); - - assert(dataChunk[0] == 0x01); - assert(dataChunk[1] == 0x01); - assert(dataChunk[2] == 0x05); - - auto seqIdxBytes = IntToBytes(seqIdx, 2); - assert(seqIdxBytes[0] == dataChunk[3]); - assert(seqIdxBytes[1] == dataChunk[4]); - - auto dataLen = BytesToInt(bytes(dataChunk.begin() + 5, dataChunk.begin() + 7)); - bytes data(dataChunk.begin() + 7, dataChunk.end()); - - while (data.size() < dataLen) - { - uint8_t readBytes[64]; - if (hid_read_timeout(device, readBytes, sizeof(readBytes), 1000) == -1) - return -1; - bytes tmp(readBytes, readBytes + sizeof(readBytes)); - data.insert(data.end(), tmp.begin() + 5, tmp.end()); - } - - auto sw = BytesToInt(bytes(data.begin() + dataLen - 2, data.begin() + dataLen)); - rdata = bytes(data.begin(), data.begin() + dataLen - 2); - - return sw; - } - - void HID::close() noexcept - { - if (opened) - { - hid_close(device); - opened = false; - } - hid_exit(); - } - - bool HID::isOpen() const - { - return opened; - } - - std::vector HID::enumerateDevices(unsigned short vendorId) noexcept - { - std::vector devices; - - struct hid_device_info *devs, *curDev; - - devs = hid_enumerate(vendorId, 0x0); - curDev = devs; - while (curDev) - { - if (curDev->interface_number == 0 || - // MacOS specific - curDev->usage_page == 0xffa0) - { - devices.emplace_back(curDev->path); - } - curDev = curDev->next; - } - hid_free_enumeration(devs); - - return devices; - } + std::vector devices; + + struct hid_device_info *devs, *curDev; + + devs = hid_enumerate(vendorId, 0x0); + curDev = devs; + while (curDev) { + if (curDev->interface_number == 0 || + // MacOS specific + curDev->usage_page == 0xffa0) { + devices.emplace_back(curDev->path); + } + curDev = curDev->next; + } + hid_free_enumeration(devs); + + return devices; +} } // namespace ledger diff --git a/wallet/ledger/hid.h b/wallet/ledger/hid.h index d123a4d35..696ccc462 100644 --- a/wallet/ledger/hid.h +++ b/wallet/ledger/hid.h @@ -5,26 +5,25 @@ #include "hidapi/hidapi.h" -namespace ledger +namespace ledger { +class HID final : public Comm { - class HID final : public Comm - { - public: - void open() override; - int send(const bytes &data) override; - int receive(bytes &rdata) override; - void close() noexcept override; - [[nodiscard]] bool isOpen() const override; +public: + void open() override; + int send(const bytes& data) override; + int receive(bytes& rdata) override; + void close() noexcept override; + [[nodiscard]] bool isOpen() const override; - private: - static std::vector enumerateDevices(unsigned short vendor_id = 0x2c97) noexcept; +private: + static std::vector enumerateDevices(unsigned short vendor_id = 0x2c97) noexcept; - hid_device *device = nullptr; - std::string path = {}; - bool opened = false; - const int timeoutMs = 60 * 1000; - unsigned short vendorId = 0x2c97; // Ledger Vendor ID - }; + hid_device* device = nullptr; + std::string path = {}; + bool opened = false; + const int timeoutMs = 60 * 1000; + unsigned short vendorId = 0x2c97; // Ledger Vendor ID +}; } // namespace ledger #endif // LEDGER_HID_H diff --git a/wallet/ledger/ledger.cpp b/wallet/ledger/ledger.cpp index 9d34f4491..d2c9597d2 100644 --- a/wallet/ledger/ledger.cpp +++ b/wallet/ledger/ledger.cpp @@ -1,229 +1,223 @@ #include "ledger/ledger.h" -#include "ledger/error.h" -#include "ledger/utils.h" #include "ledger/bip32.h" +#include "ledger/error.h" #include "ledger/tx.h" +#include "ledger/utils.h" #include #include #include -namespace ledger +namespace ledger { +Ledger::Ledger(Transport::TransportType transportType) +{ + this->transport = std::unique_ptr(new Transport(transportType)); +} + +Ledger::~Ledger() { transport->close(); } + +void Ledger::open() { transport->open(); } + +std::tuple Ledger::GetPublicKey(const Bip32Path path, bool confirm) +{ + auto payload = bytes(); + + auto pathBytes = path.Serialize(); + payload.push_back(pathBytes.size() / 4); + AppendVector(payload, pathBytes); + + // 0x00 = P2_LEGACY (base58) + auto buffer = transport->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); + + auto offset = 1; + auto pubKeyLen = (int)buffer[offset] * 16 + 1; + auto pubKey = Splice(buffer, offset, pubKeyLen); + offset += pubKeyLen; + + auto addressLen = (int)buffer[offset]; + offset++; + auto address = Splice(buffer, offset, addressLen); + offset += addressLen; + + auto chainCode = Splice(buffer, offset, 32); + offset += 32; + + if (offset != buffer.size()) + throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); + + return std::make_tuple(pubKey, std::string(address.begin(), address.end()), chainCode); +} + +bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes& transactionData) +{ + return transport->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, + transactionData); +} + +bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) +{ + bytes firstRoundData; + AppendUint32(firstRoundData, indexLookup); + AppendUint32(firstRoundData, utxoTx.version, true); + AppendUint32(firstRoundData, utxoTx.time, true); + AppendVector(firstRoundData, CreateVarint(utxoTx.inputs.size())); + + GetTrustedInputRaw(true, firstRoundData); + + for (auto input : utxoTx.inputs) { + bytes inputData; + AppendVector(inputData, input.prevout.hash); + AppendUint32(inputData, input.prevout.index, true); + AppendVector(inputData, CreateVarint(input.script.size())); + + GetTrustedInputRaw(false, inputData); + + bytes inputScriptData; + AppendVector(inputScriptData, input.script); + AppendUint32(inputScriptData, input.sequence); + + GetTrustedInputRaw(false, inputScriptData); + } + + GetTrustedInputRaw(false, CreateVarint(utxoTx.outputs.size())); + + for (auto output : utxoTx.outputs) { + bytes outputData; + AppendUint64(outputData, output.amount, true); + AppendVector(outputData, CreateVarint(output.script.size())); + GetTrustedInputRaw(false, outputData); + + bytes outputScriptData; + AppendVector(outputScriptData, output.script); + + GetTrustedInputRaw(false, outputScriptData); + } + + return GetTrustedInputRaw(false, IntToBytes(utxoTx.locktime, 4)); +} + +void Ledger::UntrustedHashTxInputFinalize(const Tx& tx, bool hasChange, const Bip32Path changePath) +{ + auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE; + auto p2 = 0x00; + + auto p1 = 0xFF; + if (hasChange) { + auto serializedChangePath = changePath.Serialize(); + + bytes changePathData; + changePathData.push_back(serializedChangePath.size() / 4); + AppendVector(changePathData, serializedChangePath); + + transport->exchange(APDU::CLA, ins, p1, p2, changePathData); + } else { + transport->exchange(APDU::CLA, ins, p1, p2, {0x00}); + } + + p1 = 0x00; + transport->exchange(APDU::CLA, ins, p1, p2, CreateVarint(tx.outputs.size())); + + for (auto i = 0; i < tx.outputs.size(); i++) { + p1 = i < tx.outputs.size() - 1 ? 0x00 : 0x80; + + auto output = tx.outputs[i]; + bytes outputData; + AppendUint64(outputData, output.amount, true); + AppendVector(outputData, CreateVarint(output.script.size())); + AppendVector(outputData, output.script); + + transport->exchange(APDU::CLA, ins, p1, p2, outputData); + } +} + +void Ledger::UntrustedHashTxInputStart(const Tx& tx, const std::vector& trustedInputs, + int inputIndex, bytes script, bool isNewTransaction) { - Ledger::Ledger(Transport::TransportType transportType) { this->transport = std::unique_ptr(new Transport(transportType)); } + auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START; + auto p1 = 0x00; + auto p2 = isNewTransaction ? 0x00 : 0x80; + + bytes data; + AppendUint32(data, tx.version, true); + AppendUint32(data, tx.time, true); + AppendVector(data, CreateVarint(trustedInputs.size())); + + transport->exchange(APDU::CLA, ins, p1, p2, data); - Ledger::~Ledger() { transport->close(); } + p1 = 0x80; + for (auto i = 0; i < trustedInputs.size(); i++) { + auto trustedInput = trustedInputs[i]; + auto _script = i == inputIndex ? script : bytes(); - void Ledger::open() { transport->open(); } + bytes _data; + _data.push_back(0x01); + _data.push_back(trustedInput.serialized.size()); + AppendVector(_data, trustedInput.serialized); + AppendVector(_data, CreateVarint(_script.size())); - std::tuple Ledger::GetPublicKey(const Bip32Path path, bool confirm) - { - auto payload = bytes(); + transport->exchange(APDU::CLA, ins, p1, p2, _data); - auto pathBytes = path.Serialize(); - payload.push_back(pathBytes.size() / 4); - AppendVector(payload, pathBytes); + bytes scriptData; + AppendVector(scriptData, _script); + AppendUint32(scriptData, 0xffffffff, true); - // 0x00 = P2_LEGACY (base58) - auto buffer = transport->exchange(APDU::CLA, APDU::INS_GET_PUBLIC_KEY, confirm, 0x00, payload); + transport->exchange(APDU::CLA, ins, p1, p2, scriptData); + } +} + +std::vector Ledger::SignTransaction(const Tx& tx, bool hasChange, const Bip32Path changePath, + const std::vector& signPaths, + const std::vector& utxos) +{ + assert(tx.inputs.size() == signPaths.size()); + assert(tx.inputs.size() == utxos.size()); - auto offset = 1; - auto pubKeyLen = (int)buffer[offset] * 16 + 1; - auto pubKey = Splice(buffer, offset, pubKeyLen); - offset += pubKeyLen; + // get trusted inputs + std::vector trustedInputs; + for (auto i = 0; i < utxos.size(); i++) { + const auto& utxo = utxos[i]; - auto addressLen = (int)buffer[offset]; - offset++; - auto address = Splice(buffer, offset, addressLen); - offset += addressLen; + const auto serializedTrustedInput = GetTrustedInput(utxo.tx, utxo.outputIndex); + const auto trustedInput = ledger::DeserializeTrustedInput(serializedTrustedInput); - auto chainCode = Splice(buffer, offset, 32); - offset += 32; + assert(trustedInput.prevTxId == tx.inputs[i].prevout.hash); - if (offset != buffer.size()) - throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); - - return std::make_tuple(pubKey, std::string(address.begin(), address.end()), chainCode); - } + trustedInputs.push_back(trustedInput); + } - bytes Ledger::GetTrustedInputRaw(bool firstRound, const bytes &transactionData) - { - return transport->exchange(APDU::CLA, APDU::INS_GET_TRUSTED_INPUT, firstRound ? 0x00 : 0x80, 0x00, transactionData); - } - - bytes Ledger::GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup) - { - bytes firstRoundData; - AppendUint32(firstRoundData, indexLookup); - AppendUint32(firstRoundData, utxoTx.version, true); - AppendUint32(firstRoundData, utxoTx.time, true); - AppendVector(firstRoundData, CreateVarint(utxoTx.inputs.size())); - - GetTrustedInputRaw(true, firstRoundData); + std::vector signatures; + for (auto i = 0; i < tx.inputs.size(); i++) { + auto& script = utxos[i].tx.outputs[utxos[i].outputIndex].script; + UntrustedHashTxInputStart(tx, trustedInputs, i, script, i == 0); - for (auto input : utxoTx.inputs) - { - bytes inputData; - AppendVector(inputData, input.prevout.hash); - AppendUint32(inputData, input.prevout.index, true); - AppendVector(inputData, CreateVarint(input.script.size())); + UntrustedHashTxInputFinalize(tx, hasChange, changePath); - GetTrustedInputRaw(false, inputData); - - bytes inputScriptData; - AppendVector(inputScriptData, input.script); - AppendUint32(inputScriptData, input.sequence); - - GetTrustedInputRaw(false, inputScriptData); - } - - GetTrustedInputRaw(false, CreateVarint(utxoTx.outputs.size())); - - for (auto output : utxoTx.outputs) - { - bytes outputData; - AppendUint64(outputData, output.amount, true); - AppendVector(outputData, CreateVarint(output.script.size())); - GetTrustedInputRaw(false, outputData); - - bytes outputScriptData; - AppendVector(outputScriptData, output.script); + auto ins = INS_UNTRUSTED_HASH_SIGN; + auto p1 = 0x00; + auto p2 = 0x00; - GetTrustedInputRaw(false, outputScriptData); - } - - return GetTrustedInputRaw(false, IntToBytes(utxoTx.locktime, 4)); - } + auto serializedSignPath = signPaths[i].Serialize(); - void Ledger::UntrustedHashTxInputFinalize(const Tx &tx, bool hasChange, const Bip32Path changePath) - { - auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE; - auto p2 = 0x00; + bytes data; + data.push_back(serializedSignPath.size() / 4); + AppendVector(data, serializedSignPath); + data.push_back(0x00); + AppendUint32(data, tx.locktime); + data.push_back(0x01); - auto p1 = 0xFF; - if (hasChange) - { - auto serializedChangePath = changePath.Serialize(); + auto buffer = transport->exchange(APDU::CLA, ins, p1, p2, data); + if (buffer[0] & 0x01) { + bytes data; + data.push_back(0x30); + AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); + signatures.push_back(data); + } else { + throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); + } + } - bytes changePathData; - changePathData.push_back(serializedChangePath.size() / 4); - AppendVector(changePathData, serializedChangePath); + return signatures; +} - transport->exchange(APDU::CLA, ins, p1, p2, changePathData); - } - else - { - transport->exchange(APDU::CLA, ins, p1, p2, {0x00}); - } - - p1 = 0x00; - transport->exchange(APDU::CLA, ins, p1, p2, CreateVarint(tx.outputs.size())); - - for (auto i = 0; i < tx.outputs.size(); i++) - { - p1 = i < tx.outputs.size() - 1 ? 0x00 : 0x80; - - auto output = tx.outputs[i]; - bytes outputData; - AppendUint64(outputData, output.amount, true); - AppendVector(outputData, CreateVarint(output.script.size())); - AppendVector(outputData, output.script); - - transport->exchange(APDU::CLA, ins, p1, p2, outputData); - } - } - - void Ledger::UntrustedHashTxInputStart(const Tx &tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction) - { - auto ins = APDU::INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START; - auto p1 = 0x00; - auto p2 = isNewTransaction ? 0x00 : 0x80; - - bytes data; - AppendUint32(data, tx.version, true); - AppendUint32(data, tx.time, true); - AppendVector(data, CreateVarint(trustedInputs.size())); - - transport->exchange(APDU::CLA, ins, p1, p2, data); - - p1 = 0x80; - for (auto i = 0; i < trustedInputs.size(); i++) - { - auto trustedInput = trustedInputs[i]; - auto _script = i == inputIndex ? script : bytes(); - - bytes _data; - _data.push_back(0x01); - _data.push_back(trustedInput.serialized.size()); - AppendVector(_data, trustedInput.serialized); - AppendVector(_data, CreateVarint(_script.size())); - - transport->exchange(APDU::CLA, ins, p1, p2, _data); - - bytes scriptData; - AppendVector(scriptData, _script); - AppendUint32(scriptData, 0xffffffff, true); - - transport->exchange(APDU::CLA, ins, p1, p2, scriptData); - } - } - - std::vector Ledger::SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector& signPaths, const std::vector &utxos) - { - assert(tx.inputs.size() == signPaths.size()); - assert(tx.inputs.size() == utxos.size()); - - // get trusted inputs - std::vector trustedInputs; - for (auto i = 0; i < utxos.size(); i++) - { - const auto &utxo = utxos[i]; - - const auto serializedTrustedInput = GetTrustedInput(utxo.tx, utxo.outputIndex); - const auto trustedInput = ledger::DeserializeTrustedInput(serializedTrustedInput); - - assert(trustedInput.prevTxId == tx.inputs[i].prevout.hash); - - trustedInputs.push_back(trustedInput); - } - - std::vector signatures; - for (auto i = 0; i < tx.inputs.size(); i++) - { - auto &script = utxos[i].tx.outputs[utxos[i].outputIndex].script; - UntrustedHashTxInputStart(tx, trustedInputs, i, script, i == 0); - - UntrustedHashTxInputFinalize(tx, hasChange, changePath); - - auto ins = INS_UNTRUSTED_HASH_SIGN; - auto p1 = 0x00; - auto p2 = 0x00; - - auto serializedSignPath = signPaths[i].Serialize(); - - bytes data; - data.push_back(serializedSignPath.size() / 4); - AppendVector(data, serializedSignPath); - data.push_back(0x00); - AppendUint32(data, tx.locktime); - data.push_back(0x01); - - auto buffer = transport->exchange(APDU::CLA, ins, p1, p2, data); - if (buffer[0] & 0x01) - { - bytes data; - data.push_back(0x30); - AppendVector(data, bytes(buffer.begin() + 1, buffer.end())); - signatures.push_back(data); - } - else - { - throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); - } - } - - return signatures; - } - - void Ledger::close() { return transport->close(); } +void Ledger::close() { return transport->close(); } } // namespace ledger diff --git a/wallet/ledger/ledger.h b/wallet/ledger/ledger.h index 721d93388..5944a38e5 100644 --- a/wallet/ledger/ledger.h +++ b/wallet/ledger/ledger.h @@ -6,43 +6,45 @@ #include "ledger/transport.h" #include "ledger/tx.h" -namespace ledger +namespace ledger { +class Ledger { - class Ledger - { - enum APDU : uint8_t - { - CLA = 0xe0, - INS_GET_APP_CONFIGURATION = 0x01, - INS_GET_PUBLIC_KEY = 0x40, - INS_SIGN = 0x03, - INS_GET_TRUSTED_INPUT = 0x42, - INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START = 0x44, - INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE = 0x4A, - INS_UNTRUSTED_HASH_SIGN = 0x48 - }; - - public: - Ledger(Transport::TransportType transportType = Transport::TransportType::HID); - ~Ledger(); - - void open(); - - std::tuple GetPublicKey(const Bip32Path path, bool confirm); - std::vector SignTransaction(const Tx &tx, bool hasChange, const Bip32Path changePath, const std::vector &signPaths, const std::vector &utxos); - - void close(); - - private: - std::unique_ptr transport; - - bytes ProcessScriptBlocks(const bytes &script, uint32_t sequence); - bytes GetTrustedInput(const Tx &utxoTx, uint32_t indexLookup); - bytes GetTrustedInputRaw(bool firstRound, const bytes &data); - void UntrustedHashTxInputFinalize(const Tx &tx, bool hasChange, const Bip32Path changePath); - void UntrustedHashTxInputStart(const Tx &tx, const std::vector &trustedInputs, int inputIndex, bytes script, bool isNewTransaction); - TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); - }; -} + enum APDU : uint8_t + { + CLA = 0xe0, + INS_GET_APP_CONFIGURATION = 0x01, + INS_GET_PUBLIC_KEY = 0x40, + INS_SIGN = 0x03, + INS_GET_TRUSTED_INPUT = 0x42, + INS_UNTRUSTED_HASH_TRANSACTION_INPUT_START = 0x44, + INS_UNTRUSTED_HASH_TRANSACTION_INPUT_FINALIZE = 0x4A, + INS_UNTRUSTED_HASH_SIGN = 0x48 + }; + +public: + Ledger(Transport::TransportType transportType = Transport::TransportType::HID); + ~Ledger(); + + void open(); + + std::tuple GetPublicKey(const Bip32Path path, bool confirm); + std::vector SignTransaction(const Tx& tx, bool hasChange, const Bip32Path changePath, + const std::vector& signPaths, + const std::vector& utxos); + + void close(); + +private: + std::unique_ptr transport; + + bytes ProcessScriptBlocks(const bytes& script, uint32_t sequence); + bytes GetTrustedInput(const Tx& utxoTx, uint32_t indexLookup); + bytes GetTrustedInputRaw(bool firstRound, const bytes& data); + void UntrustedHashTxInputFinalize(const Tx& tx, bool hasChange, const Bip32Path changePath); + void UntrustedHashTxInputStart(const Tx& tx, const std::vector& trustedInputs, + int inputIndex, bytes script, bool isNewTransaction); + TrustedInput DeserializeTrustedInput(const bytes& serializedTrustedInput); +}; +} // namespace ledger #endif // LEDGER_LEDGER_H diff --git a/wallet/ledger/transport.cpp b/wallet/ledger/transport.cpp index 43c364895..bc586c68c 100644 --- a/wallet/ledger/transport.cpp +++ b/wallet/ledger/transport.cpp @@ -1,70 +1,62 @@ +#include "ledger/transport.h" #include "ledger/error.h" #include "ledger/hid.h" -#include "ledger/transport.h" #include "ledger/utils.h" -namespace ledger +namespace ledger { +Transport::Transport(TransportType type) { - Transport::Transport(TransportType type) - { - switch (type) - { - case TransportType::HID: - comm = std::unique_ptr(new HID()); - break; - case TransportType::SPECULOS: - throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); - } - } - - void Transport::open() - { - return comm->open(); - } - - bytes Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) - { - int length = this->send(cla, ins, p1, p2, cdata); - if (length < 0) - throw LedgerException(ErrorCode::DEVICE_DATA_SEND_FAIL); + switch (type) { + case TransportType::HID: + comm = std::unique_ptr(new HID()); + break; + case TransportType::SPECULOS: + throw LedgerException(ErrorCode::UNRECOGNIZED_ERROR); + } +} + +void Transport::open() { return comm->open(); } + +bytes Transport::exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes& cdata) +{ + int length = this->send(cla, ins, p1, p2, cdata); + if (length < 0) + throw LedgerException(ErrorCode::DEVICE_DATA_SEND_FAIL); - bytes buffer; - int sw = this->receive(buffer); - if (sw < 0) - throw LedgerException(ErrorCode::DEVICE_DATA_RECV_FAIL); + bytes buffer; + int sw = this->receive(buffer); + if (sw < 0) + throw LedgerException(ErrorCode::DEVICE_DATA_RECV_FAIL); - if (sw != 0x9000) - throw LedgerException(ErrorCode::APDU_INVALID_CMD); + if (sw != 0x9000) + throw LedgerException(ErrorCode::APDU_INVALID_CMD); - return buffer; - } + return buffer; +} - void Transport::close() noexcept - { - return comm->close(); - } +void Transport::close() noexcept { return comm->close(); } - int Transport::send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata) - { - if (!comm->isOpen()) - return -1; +int Transport::send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes& cdata) +{ + if (!comm->isOpen()) + return -1; - auto header = apduHeader(cla, ins, p1, p2, cdata.size()); - header.insert(header.end(), cdata.begin(), cdata.end()); + auto header = apduHeader(cla, ins, p1, p2, cdata.size()); + header.insert(header.end(), cdata.begin(), cdata.end()); - return comm->send(header); - } + return comm->send(header); +} - int Transport::receive(bytes &rdata) - { - if (!comm->isOpen()) - return -1; +int Transport::receive(bytes& rdata) +{ + if (!comm->isOpen()) + return -1; - return comm->receive(rdata); - } + return comm->receive(rdata); +} - bytes Transport::apduHeader(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) - { - return bytes{cla, ins, p1, p2, lc}; - } +bytes Transport::apduHeader(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc) +{ + return bytes{cla, ins, p1, p2, lc}; +} } // namespace ledger diff --git a/wallet/ledger/transport.h b/wallet/ledger/transport.h index 7eec39cf7..b71f1cb14 100644 --- a/wallet/ledger/transport.h +++ b/wallet/ledger/transport.h @@ -6,29 +6,28 @@ #include -namespace ledger +namespace ledger { +class Transport { - class Transport - { - public: - enum class TransportType : int - { - HID = 0, - SPECULOS = 1, - }; +public: + enum class TransportType : int + { + HID = 0, + SPECULOS = 1, + }; - Transport(TransportType type); - void open(); - bytes exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); - void close() noexcept; + Transport(TransportType type); + void open(); + bytes exchange(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes& cdata); + void close() noexcept; - private: - int send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes &cdata); - int receive(bytes &rdata); - static bytes apduHeader(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc); +private: + int send(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const bytes& cdata); + int receive(bytes& rdata); + static bytes apduHeader(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc); - std::unique_ptr comm; - }; + std::unique_ptr comm; +}; } // namespace ledger #endif // LEDGER_TRANSPORT_H diff --git a/wallet/ledger/tx.cpp b/wallet/ledger/tx.cpp index ef8e110fb..3008fc170 100644 --- a/wallet/ledger/tx.cpp +++ b/wallet/ledger/tx.cpp @@ -1,17 +1,16 @@ -#include "ledger/error.h" #include "ledger/tx.h" +#include "ledger/error.h" #include "ledger/utils.h" -namespace ledger +namespace ledger { +bytes SerializeTransaction(const Tx& tx) { -bytes SerializeTransaction(const Tx& tx) { bytes serializedTransaction; AppendUint32(serializedTransaction, tx.version, true); AppendUint32(serializedTransaction, tx.time, true); AppendVector(serializedTransaction, CreateVarint(tx.inputs.size())); - for (auto input : tx.inputs) - { + for (auto input : tx.inputs) { AppendVector(serializedTransaction, input.prevout.hash); AppendUint32(serializedTransaction, input.prevout.index, true); AppendVector(serializedTransaction, CreateVarint(input.script.size())); @@ -20,8 +19,7 @@ bytes SerializeTransaction(const Tx& tx) { } AppendVector(serializedTransaction, CreateVarint(tx.outputs.size())); - for (auto output : tx.outputs) - { + for (auto output : tx.outputs) { AppendUint64(serializedTransaction, output.amount, true); AppendVector(serializedTransaction, CreateVarint(output.script.size())); AppendVector(serializedTransaction, output.script); @@ -125,44 +123,44 @@ Tx DeserializeTransaction(const bytes& transaction) tx.locktime = BytesToInt(Splice(transaction, offset, 4)); return tx; - } +} - TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput) - { - TrustedInput trustedInput; +TrustedInput DeserializeTrustedInput(const bytes& serializedTrustedInput) +{ + TrustedInput trustedInput; - AppendVector(trustedInput.serialized, serializedTrustedInput); + AppendVector(trustedInput.serialized, serializedTrustedInput); - auto offset = 0; + auto offset = 0; - auto trustedInputMagic = serializedTrustedInput[offset]; - if (trustedInputMagic != 0x32) - throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); - offset += 1; + auto trustedInputMagic = serializedTrustedInput[offset]; + if (trustedInputMagic != 0x32) + throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); + offset += 1; - auto zeroByte = serializedTrustedInput[offset]; - if (zeroByte != 0x00) - throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); - offset += 1; + auto zeroByte = serializedTrustedInput[offset]; + if (zeroByte != 0x00) + throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); + offset += 1; - trustedInput.random = BytesToInt(Splice(serializedTrustedInput, offset, 2)); - offset += 2; + trustedInput.random = BytesToInt(Splice(serializedTrustedInput, offset, 2)); + offset += 2; - trustedInput.prevTxId = Splice(serializedTrustedInput, offset, 32); - offset += 32; + trustedInput.prevTxId = Splice(serializedTrustedInput, offset, 32); + offset += 32; - trustedInput.outIndex = BytesToInt(Splice(serializedTrustedInput, offset, 4), true); - offset += 4; + trustedInput.outIndex = BytesToInt(Splice(serializedTrustedInput, offset, 4), true); + offset += 4; - trustedInput.amount = BytesToInt(Splice(serializedTrustedInput, offset, 8), true); - offset += 8; + trustedInput.amount = BytesToInt(Splice(serializedTrustedInput, offset, 8), true); + offset += 8; - trustedInput.hmac = Splice(serializedTrustedInput, offset, 8); - offset += 8; + trustedInput.hmac = Splice(serializedTrustedInput, offset, 8); + offset += 8; - if (offset != serializedTrustedInput.size()) - throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); + if (offset != serializedTrustedInput.size()) + throw LedgerException(ErrorCode::INVALID_TRUSTED_INPUT); - return trustedInput; - } + return trustedInput; +} } // namespace ledger diff --git a/wallet/ledger/tx.h b/wallet/ledger/tx.h index 48a6311b5..d9ef1e175 100644 --- a/wallet/ledger/tx.h +++ b/wallet/ledger/tx.h @@ -4,71 +4,70 @@ #include "ledger/bytes.h" #include "ledger/transport.h" -namespace ledger +namespace ledger { +struct TxPrevout { - struct TxPrevout - { - bytes hash; - uint32_t index; - }; + bytes hash; + uint32_t index; +}; - struct TxInput - { - TxPrevout prevout; - bytes script; - uint32_t sequence; - }; +struct TxInput +{ + TxPrevout prevout; + bytes script; + uint32_t sequence; +}; - struct TxOutput - { - uint64_t amount; - bytes script; - }; +struct TxOutput +{ + uint64_t amount; + bytes script; +}; - struct ScriptWitness - { - std::vector stack; - }; +struct ScriptWitness +{ + std::vector stack; +}; - struct TxInWitness - { - ScriptWitness scriptWitness; - }; +struct TxInWitness +{ + ScriptWitness scriptWitness; +}; - struct TxWitness - { - std::vector txInWitnesses; - }; +struct TxWitness +{ + std::vector txInWitnesses; +}; - struct Tx - { - uint32_t version; - uint32_t time; - std::vector inputs; - std::vector outputs; - uint32_t locktime; - TxWitness witness; - }; +struct Tx +{ + uint32_t version; + uint32_t time; + std::vector inputs; + std::vector outputs; + uint32_t locktime; + TxWitness witness; +}; - struct TrustedInput - { - bytes serialized; - uint16_t random; - bytes prevTxId; - uint32_t outIndex; - uint64_t amount; - bytes hmac; - }; +struct TrustedInput +{ + bytes serialized; + uint16_t random; + bytes prevTxId; + uint32_t outIndex; + uint64_t amount; + bytes hmac; +}; - struct Utxo - { - Tx tx; - uint32_t outputIndex; - }; +struct Utxo +{ + Tx tx; + uint32_t outputIndex; +}; - bytes SerializeTransaction(const Tx &tx); - Tx DeserializeTransaction(const bytes &transaction); - TrustedInput DeserializeTrustedInput(const bytes &serializedTrustedInput); -} +bytes SerializeTransaction(const Tx& tx); +Tx DeserializeTransaction(const bytes& transaction); +TrustedInput DeserializeTrustedInput(const bytes& serializedTrustedInput); +} // namespace ledger #endif // LEDGER_TX_H diff --git a/wallet/ledger/utils.cpp b/wallet/ledger/utils.cpp index 0b1c2e49d..893ad2f85 100644 --- a/wallet/ledger/utils.cpp +++ b/wallet/ledger/utils.cpp @@ -1,193 +1,168 @@ #include "ledger/utils.h" #include -#include #include +#include #include #include -namespace ledger +namespace ledger { +std::tuple DeserializeVarint(const bytes& data, uint32_t offset) +{ + if (data[offset] < 0xfd) { + return std::make_tuple(data[offset], 1); + } + + if (data[offset] == 0xfd) { + return std::make_tuple((data[offset + 2] << 8) + data[offset + 1], 3); + } + + if (data[offset] == 0xfe) { + return std::make_tuple((data[offset + 4] << 24) + (data[offset + 3] << 16) + + (data[offset + 2] << 8) + data[offset + 1], + 5); + } +} + +bytes CreateVarint(uint32_t value) +{ + bytes data; + if (value < 0xfd) { + data.push_back(value); + } else if (value <= 0xffff) { + data.push_back(0xfd); + data.push_back(value & 0xff); + data.push_back((value >> 8) & 0xff); + } else { + data.push_back(0xfd); + data.push_back(value & 0xff); + data.push_back((value >> 8) & 0xff); + data.push_back((value >> 16) & 0xff); + data.push_back((value >> 24) & 0xff); + } + + return data; +} + +std::string BytesToHex(const bytes& vec) +{ + std::stringstream ss; + for (int i = 0; i < vec.size(); i++) { + ss << std::hex << std::setfill('0') << std::setw(2) << (int)vec[i]; + } + + return ss.str(); +} + +bytes HexToBytes(const std::string& data) +{ + std::stringstream ss; + ss << data; + + bytes resBytes; + size_t count = 0; + const auto len = data.size(); + while (ss.good() && count < len) { + unsigned short num; + char hexNum[2]; + ss.read(hexNum, 2); + sscanf(hexNum, "%2hX", &num); + resBytes.push_back(num); + count += 2; + } + return resBytes; +} + +uint64_t BytesToUint64(const bytes& _bytes, bool littleEndian) +{ + auto bytesToConvert = _bytes; + if (littleEndian) { + bytesToConvert = bytes(bytesToConvert.rbegin(), bytesToConvert.rend()); + } + + uint64_t value = 0; + for (const uint8_t& byte : bytesToConvert) { + value = (value << 8) + byte; + } + return value; +} + +int BytesToInt(const bytes& _bytes, bool littleEndian) +{ + auto bytesToConvert = _bytes; + if (littleEndian) { + bytesToConvert = bytes(bytesToConvert.rbegin(), bytesToConvert.rend()); + } + + int value = 0; + for (const uint8_t& byte : bytesToConvert) { + value = (value << 8) + byte; + } + return value; +} + +bytes IntToBytes(uint32_t n, uint32_t length, bool littleEndian) +{ + bytes bytes; + bytes.reserve(length); + for (auto i = 0; i < length; i++) { + bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); + } + + if (littleEndian) { + std::reverse(bytes.begin(), bytes.end()); + } + + return bytes; +} + +void AppendUint32(bytes& vector, uint32_t n, bool littleEndian) { - std::tuple DeserializeVarint(const bytes &data, uint32_t offset) - { - if (data[offset] < 0xfd) - { - return std::make_tuple(data[offset], 1); - } - - if (data[offset] == 0xfd) - { - return std::make_tuple((data[offset + 2] << 8) + data[offset + 1], 3); - } - - if (data[offset] == 0xfe) - { - return std::make_tuple( - (data[offset + 4] << 24) + - (data[offset + 3] << 16) + - (data[offset + 2] << 8) + - data[offset + 1], - 5 - ); - } - } - - bytes CreateVarint(uint32_t value) - { - bytes data; - if (value < 0xfd) - { - data.push_back(value); - } - else if (value <= 0xffff) - { - data.push_back(0xfd); - data.push_back(value & 0xff); - data.push_back((value >> 8) & 0xff); - } - else - { - data.push_back(0xfd); - data.push_back(value & 0xff); - data.push_back((value >> 8) & 0xff); - data.push_back((value >> 16) & 0xff); - data.push_back((value >> 24) & 0xff); - } - - return data; - } - - std::string BytesToHex(const bytes &vec) - { - std::stringstream ss; - for (int i = 0; i < vec.size(); i++) - { - ss << std::hex << std::setfill('0') << std::setw(2) << (int)vec[i]; - } - - return ss.str(); - } - - bytes HexToBytes(const std::string &data) - { - std::stringstream ss; - ss << data; - - bytes resBytes; - size_t count = 0; - const auto len = data.size(); - while (ss.good() && count < len) - { - unsigned short num; - char hexNum[2]; - ss.read(hexNum, 2); - sscanf(hexNum, "%2hX", &num); - resBytes.push_back(num); - count += 2; - } - return resBytes; - } - - uint64_t BytesToUint64(const bytes &_bytes, bool littleEndian) - { - auto bytesToConvert = _bytes; - if (littleEndian) - { - bytesToConvert = bytes(bytesToConvert.rbegin(), bytesToConvert.rend()); - } - - uint64_t value = 0; - for (const uint8_t &byte : bytesToConvert) - { - value = (value << 8) + byte; - } - return value; - } - - int BytesToInt(const bytes &_bytes, bool littleEndian) - { - auto bytesToConvert = _bytes; - if (littleEndian) - { - bytesToConvert = bytes(bytesToConvert.rbegin(), bytesToConvert.rend()); - } - - int value = 0; - for (const uint8_t &byte : bytesToConvert) - { - value = (value << 8) + byte; - } - return value; - } - - bytes IntToBytes(uint32_t n, uint32_t length, bool littleEndian) - { - bytes bytes; - bytes.reserve(length); - for (auto i = 0; i < length; i++) - { - bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); - } - - if (littleEndian) - { - std::reverse(bytes.begin(), bytes.end()); - } - - return bytes; - } - - void AppendUint32(bytes &vector, uint32_t n, bool littleEndian) - { - AppendVector(vector, IntToBytes(n, 4, littleEndian)); - } - - bytes Uint64ToBytes(uint64_t n, uint32_t length, bool littleEndian) - { - bytes bytes; - bytes.reserve(length); - for (auto i = 0; i < length; i++) - { - bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); - } - - if (littleEndian) - { - std::reverse(bytes.begin(), bytes.end()); - } - - return bytes; - } - - void AppendUint64(bytes &vector, uint64_t n, bool littleEndian) - { - AppendVector(vector, Uint64ToBytes(n, 8, littleEndian)); - } - - bytes Splice(const bytes &vec, int start, int length) - { - bytes result(length); - copy(vec.begin() + start, vec.begin() + start + length, result.begin()); - - return result; - } - - bytes CompressPubKey(const bytes &pubKey) - { - if (pubKey.size() != 65) - { - throw std::runtime_error("Invalid public key length"); - } - - if (pubKey[0] != 0x04) - { - throw std::runtime_error("Invalid public key format"); - } - - bytes compressedPubKey(33); - compressedPubKey[0] = pubKey[64] & 1 ? 0x03 : 0x02; - copy(pubKey.begin() + 1, pubKey.begin() + 33, compressedPubKey.begin() + 1); - - return compressedPubKey; - } + AppendVector(vector, IntToBytes(n, 4, littleEndian)); +} + +bytes Uint64ToBytes(uint64_t n, uint32_t length, bool littleEndian) +{ + bytes bytes; + bytes.reserve(length); + for (auto i = 0; i < length; i++) { + bytes.emplace_back((n >> 8 * (length - 1 - i)) & 0xFF); + } + + if (littleEndian) { + std::reverse(bytes.begin(), bytes.end()); + } + + return bytes; +} + +void AppendUint64(bytes& vector, uint64_t n, bool littleEndian) +{ + AppendVector(vector, Uint64ToBytes(n, 8, littleEndian)); +} + +bytes Splice(const bytes& vec, int start, int length) +{ + bytes result(length); + copy(vec.begin() + start, vec.begin() + start + length, result.begin()); + + return result; +} + +bytes CompressPubKey(const bytes& pubKey) +{ + if (pubKey.size() != 65) { + throw std::runtime_error("Invalid public key length"); + } + + if (pubKey[0] != 0x04) { + throw std::runtime_error("Invalid public key format"); + } + + bytes compressedPubKey(33); + compressedPubKey[0] = pubKey[64] & 1 ? 0x03 : 0x02; + copy(pubKey.begin() + 1, pubKey.begin() + 33, compressedPubKey.begin() + 1); + + return compressedPubKey; +} } // namespace ledger diff --git a/wallet/ledger/utils.h b/wallet/ledger/utils.h index 54c2be89b..47ed2fd0a 100644 --- a/wallet/ledger/utils.h +++ b/wallet/ledger/utils.h @@ -4,31 +4,30 @@ #include "ledger/bytes.h" #include -#include #include +#include -namespace ledger +namespace ledger { +std::tuple DeserializeVarint(const bytes& data, uint32_t offset); +bytes CreateVarint(uint32_t value); +std::string BytesToHex(const bytes& vec); +bytes HexToBytes(const std::string& data); +uint64_t BytesToUint64(const bytes& bytes, bool littleEndian = false); +int BytesToInt(const bytes& bytes, bool littleEndian = false); +bytes IntToBytes(unsigned int n, unsigned int length, bool littleEndian = false); +bytes Uint64ToBytes(uint64_t n, unsigned int length, bool littleEndian = false); +template +void AppendVector(std::vector& destination, const std::vector& source) { - std::tuple DeserializeVarint(const bytes &data, uint32_t offset); - bytes CreateVarint(uint32_t value); - std::string BytesToHex(const bytes &vec); - bytes HexToBytes(const std::string &data); - uint64_t BytesToUint64(const bytes &bytes, bool littleEndian = false); - int BytesToInt(const bytes &bytes, bool littleEndian = false); - bytes IntToBytes(unsigned int n, unsigned int length, bool littleEndian = false); - bytes Uint64ToBytes(uint64_t n, unsigned int length, bool littleEndian = false); - template - void AppendVector(std::vector &destination, const std::vector &source) - { - destination.insert(destination.end(), source.begin(), source.end()); - } - void AppendUint32(bytes &vector, uint32_t n, bool littleEndian = false); - void AppendUint64(bytes &vector, uint64_t n, bool littleEndian = false); - bytes Splice(const bytes &vec, int start, int length); - bytes CompressPubKey(const bytes &pubKey); + destination.insert(destination.end(), source.begin(), source.end()); +} +void AppendUint32(bytes& vector, uint32_t n, bool littleEndian = false); +void AppendUint64(bytes& vector, uint64_t n, bool littleEndian = false); +bytes Splice(const bytes& vec, int start, int length); +bytes CompressPubKey(const bytes& pubKey); const uint32_t MAX_RECOMMENDED_ACCOUNT = 100; -const uint32_t MAX_RECOMMENDED_INDEX = 50000; +const uint32_t MAX_RECOMMENDED_INDEX = 50000; } // namespace ledger #endif // LEDGER_UTILS_H diff --git a/wallet/ledgerBridge.cpp b/wallet/ledgerBridge.cpp index 0f720c5bc..54883dfb5 100644 --- a/wallet/ledgerBridge.cpp +++ b/wallet/ledgerBridge.cpp @@ -1,131 +1,126 @@ -#include "ledger/ledger.h" -#include "ledger/utils.h" #include "ledgerBridge.h" #include "key.h" +#include "ledger/ledger.h" +#include "ledger/utils.h" -#include #include +#include #include -namespace ledgerbridge -{ - const ledger::Transport::TransportType TRANSPORT_TYPE = ledger::Transport::TransportType::HID; +namespace ledgerbridge { +const ledger::Transport::TransportType TRANSPORT_TYPE = ledger::Transport::TransportType::HID; - LedgerBridge::LedgerBridge() {} +LedgerBridge::LedgerBridge() {} - LedgerBridge::~LedgerBridge() {} +LedgerBridge::~LedgerBridge() {} - ledger::bytes LedgerBridge::GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, bool display) - { - auto result = ledger.GetPublicKey(path, display); +ledger::bytes LedgerBridge::GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, + bool display) +{ + auto result = ledger.GetPublicKey(path, display); - return ledger::CompressPubKey(std::get<0>(result)); - } + return ledger::CompressPubKey(std::get<0>(result)); +} - ledger::bytes LedgerBridge::GetPublicKey(const ledger::Bip32Path path, bool display) - { - ledger::Ledger ledger(TRANSPORT_TYPE); - ledger.open(); - - return GetPublicKey(ledger, path, display); - } +ledger::bytes LedgerBridge::GetPublicKey(const ledger::Bip32Path path, bool display) +{ + ledger::Ledger ledger(TRANSPORT_TYPE); + ledger.open(); - ledger::bytes LedgerBridge::GetPublicKey(int account, bool isChange, int index, bool display) - { - return GetPublicKey(ledger::Bip32Path(account, isChange, index), display); - } + return GetPublicKey(ledger, path, display); +} - ledger::bytes LedgerBridge::GetAccountPublicKey(int account, bool display) - { - return GetPublicKey(ledger::Bip32Path(account), display); - } +ledger::bytes LedgerBridge::GetPublicKey(int account, bool isChange, int index, bool display) +{ + return GetPublicKey(ledger::Bip32Path(account, isChange, index), display); +} - void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos, bool hasChange) - { - std::vector signaturePaths; +ledger::bytes LedgerBridge::GetAccountPublicKey(int account, bool display) +{ + return GetPublicKey(ledger::Bip32Path(account), display); +} - // transform wallet tx to ledger tx - ledger::Tx tx = ToLedgerTx(wtxNew); +void LedgerBridge::SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx& wtxNew, + const std::vector& utxos, bool hasChange) +{ + std::vector signaturePaths; - // transform UTxOs and build signature paths - std::vector ledgerUtxos; - for (const auto &utxo : utxos) - { - ledgerUtxos.push_back({ToLedgerTx(utxo.transaction), utxo.outputIndex}); + // transform wallet tx to ledger tx + ledger::Tx tx = ToLedgerTx(wtxNew); - CKeyID keyID; - if (!ExtractKeyID(txdb, utxo.outputPubKey, keyID)) { - throw "Invalid key in script"; - } + // transform UTxOs and build signature paths + std::vector ledgerUtxos; + for (const auto& utxo : utxos) { + ledgerUtxos.push_back({ToLedgerTx(utxo.transaction), utxo.outputIndex}); - CLedgerKey ledgerKey; - auto keyFound = wallet.GetLedgerKey(keyID, ledgerKey); - if (!keyFound) { - throw "Ledger key was not found in wallet"; - } + CKeyID keyID; + if (!ExtractKeyID(txdb, utxo.outputPubKey, keyID)) { + throw "Invalid key in script"; + } - signaturePaths.push_back(ledger::Bip32Path(ledgerKey.account, ledgerKey.isChange, ledgerKey.index)); + CLedgerKey ledgerKey; + auto keyFound = wallet.GetLedgerKey(keyID, ledgerKey); + if (!keyFound) { + throw "Ledger key was not found in wallet"; } - // We only support transaction from a single Ledger address for now so all the signature paths - // should be the same. Even if that was to change this would still be a valid approach - // to determining the change path. If users would like to use a different change path - // they could leverage the coin control feature. - auto changePath = signaturePaths[0].ToChangePath(); + signaturePaths.push_back( + ledger::Bip32Path(ledgerKey.account, ledgerKey.isChange, ledgerKey.index)); + } + + // We only support transaction from a single Ledger address for now so all the signature paths + // should be the same. Even if that was to change this would still be a valid approach + // to determining the change path. If users would like to use a different change path + // they could leverage the coin control feature. + auto changePath = signaturePaths[0].ToChangePath(); - ledger::Ledger ledger(TRANSPORT_TYPE); - ledger.open(); + ledger::Ledger ledger(TRANSPORT_TYPE); + ledger.open(); - // sign tx - auto signTxResults = ledger.SignTransaction(tx, hasChange, changePath, signaturePaths, ledgerUtxos); + // sign tx + auto signTxResults = ledger.SignTransaction(tx, hasChange, changePath, signaturePaths, ledgerUtxos); - // add signatures to tx and verify - for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { - auto signature = signTxResults[sigIndex]; + // add signatures to tx and verify + for (auto sigIndex = 0; sigIndex < signTxResults.size(); sigIndex++) { + auto signature = signTxResults[sigIndex]; - auto pubKey = CPubKey(GetPublicKey(ledger, signaturePaths[sigIndex], false)); + auto pubKey = CPubKey(GetPublicKey(ledger, signaturePaths[sigIndex], false)); - // hash type - signature.push_back(0x01); + // hash type + signature.push_back(0x01); - auto txIn = &wtxNew.vin[sigIndex]; - txIn->scriptSig << signature; - txIn->scriptSig << pubKey; + auto txIn = &wtxNew.vin[sigIndex]; + txIn->scriptSig << signature; + txIn->scriptSig << pubKey; - if (!VerifyScript(txIn->scriptSig, utxos[sigIndex].outputPubKey, wtxNew, sigIndex, true, false, 0).isOk()) { - throw std::exception(); - } + if (!VerifyScript(txIn->scriptSig, utxos[sigIndex].outputPubKey, wtxNew, sigIndex, true, false, + 0) + .isOk()) { + throw std::exception(); } } +} - ledger::Tx LedgerBridge::ToLedgerTx(const CTransaction &tx) - { - ledger::Tx ledgerTx; - ledgerTx.version = tx.nVersion; - ledgerTx.time = tx.nTime; - - for (const auto& input : tx.vin){ - ledger::TxPrevout prevout = { - .hash = ledger::bytes(input.prevout.hash.begin(), input.prevout.hash.end()), - .index = input.prevout.n - }; - - ledgerTx.inputs.push_back({ - prevout, - .script = input.scriptSig, - .sequence = input.nSequence - }); - } +ledger::Tx LedgerBridge::ToLedgerTx(const CTransaction& tx) +{ + ledger::Tx ledgerTx; + ledgerTx.version = tx.nVersion; + ledgerTx.time = tx.nTime; - for (const auto& output : tx.vout) { - ledgerTx.outputs.push_back({ - .amount = (uint64_t) output.nValue, - .script = output.scriptPubKey - }); - } + for (const auto& input : tx.vin) { + ledger::TxPrevout prevout = { + .hash = ledger::bytes(input.prevout.hash.begin(), input.prevout.hash.end()), + .index = input.prevout.n}; - ledgerTx.locktime = tx.nLockTime; + ledgerTx.inputs.push_back({prevout, .script = input.scriptSig, .sequence = input.nSequence}); + } - return ledgerTx; + for (const auto& output : tx.vout) { + ledgerTx.outputs.push_back({.amount = (uint64_t)output.nValue, .script = output.scriptPubKey}); } + + ledgerTx.locktime = tx.nLockTime; + + return ledgerTx; } +} // namespace ledgerbridge diff --git a/wallet/ledgerBridge.h b/wallet/ledgerBridge.h index 883fadeca..70910111a 100644 --- a/wallet/ledgerBridge.h +++ b/wallet/ledgerBridge.h @@ -1,9 +1,9 @@ #ifndef LEDGERBRIDGE_H #include "ledger/bip32.h" -#include "ledger/tx.h" #include "ledger/bytes.h" #include "ledger/ledger.h" +#include "ledger/tx.h" #include "ledger/utils.h" #include "itxdb.h" @@ -11,39 +11,40 @@ #include -namespace ledgerbridge +namespace ledgerbridge { +struct LedgerBridgeUtxo +{ + CTransaction transaction; + uint32_t outputIndex; + CScript outputPubKey; +}; + +class LedgerBridge { - struct LedgerBridgeUtxo +public: + static bool ValidateAccountIndex(int accountIndex) { - CTransaction transaction; - uint32_t outputIndex; - CScript outputPubKey; - }; + return 0 <= accountIndex && accountIndex <= ledger::MAX_RECOMMENDED_ACCOUNT; + } - class LedgerBridge + static bool ValidateAddressIndex(int addressIndex) { - public: - static bool ValidateAccountIndex(int accountIndex) - { - return 0 <= accountIndex && accountIndex <= ledger::MAX_RECOMMENDED_ACCOUNT; - } - - static bool ValidateAddressIndex(int addressIndex) - { - return 0 <= addressIndex && addressIndex <= ledger::MAX_RECOMMENDED_INDEX; - } - - LedgerBridge(); - ~LedgerBridge(); - - ledger::bytes GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, bool display); - ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); - ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); - ledger::bytes GetAccountPublicKey(int account, bool display); - void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx &wtxNew, const std::vector &utxos, bool hasChange); - private: - ledger::Tx ToLedgerTx(const CTransaction& tx); - }; -} + return 0 <= addressIndex && addressIndex <= ledger::MAX_RECOMMENDED_INDEX; + } + + LedgerBridge(); + ~LedgerBridge(); + + ledger::bytes GetPublicKey(ledger::Ledger& ledger, const ledger::Bip32Path path, bool display); + ledger::bytes GetPublicKey(const ledger::Bip32Path path, bool display); + ledger::bytes GetPublicKey(int account, bool isChange, int index, bool display); + ledger::bytes GetAccountPublicKey(int account, bool display); + void SignTransaction(const ITxDB& txdb, const CWallet& wallet, CWalletTx& wtxNew, + const std::vector& utxos, bool hasChange); + +private: + ledger::Tx ToLedgerTx(const CTransaction& tx); +}; +} // namespace ledgerbridge #endif // LEDGERBRIDGE_H diff --git a/wallet/qt/addresstablemodel.cpp b/wallet/qt/addresstablemodel.cpp index 46cf4d392..bcd101917 100644 --- a/wallet/qt/addresstablemodel.cpp +++ b/wallet/qt/addresstablemodel.cpp @@ -14,8 +14,8 @@ #include #include -const QString AddressTableModel::Send = "S"; -const QString AddressTableModel::Receive = "R"; +const QString AddressTableModel::Send = "S"; +const QString AddressTableModel::Receive = "R"; const QString AddressTableModel::ReceiveLedger = "RL"; // TODO DM this is accidentally matched by // proxyModel->setFilterFixedString(AddressTableModel::Receive); @@ -28,9 +28,9 @@ struct AddressTableEntry ReceivingLedger }; - Type type; - QString label; - QString address; + Type type; + QString label; + QString address; uint32_t ledgerAccount; uint32_t ledgerIndex; @@ -55,13 +55,11 @@ struct AddressTableEntryLessThan AddressTableEntry::Type GetEntryType(isminetype fMine) { - if (fMine == isminetype::ISMINE_LEDGER) - { + if (fMine == isminetype::ISMINE_LEDGER) { return AddressTableEntry::ReceivingLedger; } - if (fMine) - { + if (fMine) { return AddressTableEntry::Receiving; } @@ -76,7 +74,9 @@ class AddressTablePriv QList cachedAddressTable; AddressTableModel* parent; - AddressTablePriv(CWallet* walletIn, AddressTableModel* parentIn) : wallet(walletIn), parent(parentIn) {} + AddressTablePriv(CWallet* walletIn, AddressTableModel* parentIn) : wallet(walletIn), parent(parentIn) + { + } void refreshAddressTable() { @@ -87,23 +87,18 @@ class AddressTablePriv const CBitcoinAddress& address = item.first; const std::string& strName = item.second.name; isminetype fMine = IsMine(*wallet, address.Get()); - AddressTableEntry::Type type = GetEntryType(fMine); + AddressTableEntry::Type type = GetEntryType(fMine); - CKeyID ledgerKedId; + CKeyID ledgerKedId; CLedgerKey ledgerKey; - if (type == AddressTableEntry::ReceivingLedger) - { + if (type == AddressTableEntry::ReceivingLedger) { address.GetKeyID(ledgerKedId); wallet->GetLedgerKey(ledgerKedId, ledgerKey); } - cachedAddressTable.append(AddressTableEntry( - type, - QString::fromStdString(strName), - QString::fromStdString(address.ToString()), - ledgerKey.account, - ledgerKey.index - )); + cachedAddressTable.append(AddressTableEntry(type, QString::fromStdString(strName), + QString::fromStdString(address.ToString()), + ledgerKey.account, ledgerKey.index)); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order @@ -118,24 +113,23 @@ class AddressTablePriv cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); QList::iterator upper = std::upper_bound( cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan()); - int lowerIndex = (lower - cachedAddressTable.begin()); - int upperIndex = (upper - cachedAddressTable.begin()); - bool inModel = (lower != upper); + int lowerIndex = (lower - cachedAddressTable.begin()); + int upperIndex = (upper - cachedAddressTable.begin()); + bool inModel = (lower != upper); AddressTableEntry::Type newEntryType = GetEntryType(isMine); switch (status) { case CT_NEW: if (inModel) { NLog.write(b_sev::warn, - "Warning: AddressTablePriv::updateEntry: Got CT_NEW, but entry is " - "already in model"); + "Warning: AddressTablePriv::updateEntry: Got CT_NEW, but entry is " + "already in model"); break; } { - CKeyID ledgerKeyId; + CKeyID ledgerKeyId; CLedgerKey ledgerKey; - if (newEntryType == AddressTableEntry::ReceivingLedger) - { + if (newEntryType == AddressTableEntry::ReceivingLedger) { CBitcoinAddress(address.toStdString()).GetKeyID(ledgerKeyId); wallet->GetLedgerKey(ledgerKeyId, ledgerKey); } @@ -150,8 +144,8 @@ class AddressTablePriv case CT_UPDATED: if (!inModel) { NLog.write(b_sev::warn, - "Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry " - "is not in model"); + "Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry " + "is not in model"); break; } lower->type = newEntryType; @@ -161,8 +155,8 @@ class AddressTablePriv case CT_DELETED: if (!inModel) { NLog.write(b_sev::warn, - "Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry " - "is not in model"); + "Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry " + "is not in model"); break; } parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex - 1); @@ -187,7 +181,8 @@ class AddressTablePriv AddressTableModel::AddressTableModel(CWallet* walletIn, WalletModel* parent) : QAbstractTableModel(parent), walletModel(parent), wallet(walletIn), priv(0) { - columns << tr("Label") << tr("Address") << tr("Is Ledger") << tr("Ledger Account") << tr("Ledger Index") << tr("Ledger Path"); + columns << tr("Label") << tr("Address") << tr("Is Ledger") << tr("Ledger Account") + << tr("Ledger Index") << tr("Ledger Path"); priv = new AddressTablePriv(walletIn, this); priv->refreshAddressTable(); } @@ -230,13 +225,12 @@ QVariant AddressTableModel::data(const QModelIndex& index, int role) const return isLedger ? QString::number(rec->ledgerAccount) : ""; case LedgerIndex: return isLedger ? QString::number(rec->ledgerIndex) : ""; - case LedgerPath: - { - // IsLedger, LedgerAccount and LedgerIndex are hidden, we display the info here - // (however, they still need to be above due to EditAddressDialog data mapping) - std::string path = ledger::Bip32Path(rec->ledgerAccount, false, rec->ledgerIndex).ToString(); - return isLedger ? QString::fromStdString(path) : "-"; - } + case LedgerPath: { + // IsLedger, LedgerAccount and LedgerIndex are hidden, we display the info here + // (however, they still need to be above due to EditAddressDialog data mapping) + std::string path = ledger::Bip32Path(rec->ledgerAccount, false, rec->ledgerIndex).ToString(); + return isLedger ? QString::fromStdString(path) : "-"; + } } } else if (role == Qt::FontRole) { if (index.column() == Address) { @@ -308,7 +302,8 @@ bool AddressTableModel::setData(const QModelIndex& index, const QVariant& value, editStatus = NO_CHANGES; return false; } - if (!checkLabelAvailability(value.toString(), rec->type == AddressTableEntry::ReceivingLedger)) + if (!checkLabelAvailability(value.toString(), + rec->type == AddressTableEntry::ReceivingLedger)) return false; wallet->SetAddressBookEntry(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString()); @@ -369,8 +364,7 @@ Qt::ItemFlags AddressTableModel::flags(const QModelIndex& index) const Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; // Can edit label and address for sending addresses, // and only label for receiving addresses. - switch (rec->type) - { + switch (rec->type) { case AddressTableEntry::Sending: if (index.column() == Label || index.column() == Address) retval |= Qt::ItemIsEditable; @@ -442,15 +436,15 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con } strAddress = CBitcoinAddress(newKey.GetID()).ToString(); } else if (type == ReceiveLedger) { - bool accountOk = true; - uint32_t account = ledgerAccount.toUInt(&accountOk); + bool accountOk = true; + uint32_t account = ledgerAccount.toUInt(&accountOk); if (!accountOk || !ledgerbridge::LedgerBridge::ValidateAccountIndex(account)) { editStatus = INVALID_LEDGER_ACCOUNT; return QString(); } - bool indexOk = true; - uint32_t index = ledgerIndex.toUInt(&indexOk); + bool indexOk = true; + uint32_t index = ledgerIndex.toUInt(&indexOk); if (!indexOk || !ledgerbridge::LedgerBridge::ValidateAddressIndex(index)) { editStatus = INVALID_LEDGER_INDEX; return QString(); @@ -459,7 +453,7 @@ QString AddressTableModel::addRow(const QString& type, const QString& label, con try { strAddress = wallet->ImportLedgerKey(account, index); } catch (const ledger::LedgerException& e) { - editStatus = LEDGER_ERROR; + editStatus = LEDGER_ERROR; ledgerError = e.GetErrorCode(); return QString(); } @@ -482,7 +476,8 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex& parent { Q_UNUSED(parent); AddressTableEntry* rec = priv->index(row); - if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving || rec->type == AddressTableEntry::ReceivingLedger) { + if (count != 1 || !rec || rec->type == AddressTableEntry::Receiving || + rec->type == AddressTableEntry::ReceivingLedger) { // Can only remove one row at a time, and cannot remove rows not in model. // Also refuse to remove receiving addresses. return false; diff --git a/wallet/qt/addresstablemodel.h b/wallet/qt/addresstablemodel.h index df3ad0b59..84eca3b36 100644 --- a/wallet/qt/addresstablemodel.h +++ b/wallet/qt/addresstablemodel.h @@ -1,10 +1,10 @@ #ifndef ADDRESSTABLEMODEL_H #define ADDRESSTABLEMODEL_H -#include -#include #include "ledger/error.h" #include "wallet_ismine.h" +#include +#include class AddressTablePriv; class CWallet; @@ -22,12 +22,12 @@ class AddressTableModel : public QAbstractTableModel enum ColumnIndex { - Label = 0, /**< User specified label */ - Address = 1, /**< Bitcoin address */ - IsLedger = 2, + Label = 0, /**< User specified label */ + Address = 1, /**< Bitcoin address */ + IsLedger = 2, LedgerAccount = 3, - LedgerIndex = 4, - LedgerPath = 5, + LedgerIndex = 4, + LedgerPath = 5, }; enum RoleIndex @@ -38,21 +38,21 @@ class AddressTableModel : public QAbstractTableModel /** Return status of edit/insert operation */ enum EditStatus { - OK, /**< Everything ok */ - NO_CHANGES, /**< No changes were made during edit operation */ - INVALID_ADDRESS, /**< Unparseable address */ - DUPLICATE_ADDRESS, /**< Address already in address book */ - INVALID_LEDGER_ACCOUNT, /**< Ledger account outside of recommended range */ - INVALID_LEDGER_INDEX, /**< Ledger index outside of recommended range */ - WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ - KEY_GENERATION_FAILURE, /**< Generating a new public key for a receiving address failed */ - LABEL_USED_BY_LEDGER, /**< Using this label would break Ledger's unique label requirement */ + OK, /**< Everything ok */ + NO_CHANGES, /**< No changes were made during edit operation */ + INVALID_ADDRESS, /**< Unparseable address */ + DUPLICATE_ADDRESS, /**< Address already in address book */ + INVALID_LEDGER_ACCOUNT, /**< Ledger account outside of recommended range */ + INVALID_LEDGER_INDEX, /**< Ledger index outside of recommended range */ + WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */ + KEY_GENERATION_FAILURE, /**< Generating a new public key for a receiving address failed */ + LABEL_USED_BY_LEDGER, /**< Using this label would break Ledger's unique label requirement */ LABEL_NOT_USABLE_FOR_LEDGER, /**< Ledger address requires a fresh, unique label */ LEDGER_ERROR /**< Ledger operation error */ }; - static const QString Send; /**< Specifies send address */ - static const QString Receive; /**< Specifies receive address */ + static const QString Send; /**< Specifies send address */ + static const QString Receive; /**< Specifies receive address */ static const QString ReceiveLedger; /**< Specifies Ledger address */ /** @name Methods overridden from QAbstractTableModel @@ -70,7 +70,8 @@ class AddressTableModel : public QAbstractTableModel /* Add an address to the model. Returns the added address on success, and an empty string otherwise. */ - QString addRow(const QString& type, const QString& label, const QString& address, const QString& ledgerAccount, const QString& ledgerIndex); + QString addRow(const QString& type, const QString& label, const QString& address, + const QString& ledgerAccount, const QString& ledgerIndex); /* Look up label for address in address book, if not found return empty string. */ @@ -112,8 +113,8 @@ class AddressTableModel : public QAbstractTableModel public slots: /* Update address list from core. */ - void updateEntry(const QString& address, const QString& label, isminetype isMine, const QString& purpose, - int status); + void updateEntry(const QString& address, const QString& label, isminetype isMine, + const QString& purpose, int status); friend class AddressTablePriv; }; diff --git a/wallet/qt/coincontroldialog.cpp b/wallet/qt/coincontroldialog.cpp index bceb1371f..297910d7e 100644 --- a/wallet/qt/coincontroldialog.cpp +++ b/wallet/qt/coincontroldialog.cpp @@ -34,7 +34,7 @@ CoinControlDialog::CoinControlDialog(QWidget* parent, bool fLedgerTxIn, QString { ui->setupUi(this); - fLedgerTx = fLedgerTxIn; + fLedgerTx = fLedgerTxIn; fromAccount = fromAccountIn; if (coinControl->fLedgerTx != fLedgerTx) { // the ledger-only flag has changed, reset the coin control global state @@ -726,7 +726,8 @@ void CoinControlDialog::updateView() // ledger path CLedgerKey ledgerKey; if (model->getWallet()->GetLedgerKey(boost::get(walletAddress), ledgerKey)) { - std::string path = ledger::Bip32Path(ledgerKey.account, false, ledgerKey.index).ToString(); + std::string path = + ledger::Bip32Path(ledgerKey.account, false, ledgerKey.index).ToString(); itemWalletAddress->setText(COLUMN_LEDGER_PATH, QString::fromStdString(path)); } else { itemWalletAddress->setText(COLUMN_LEDGER_PATH, "-"); diff --git a/wallet/qt/coincontroldialog.h b/wallet/qt/coincontroldialog.h index a80bd73bc..951918bf0 100644 --- a/wallet/qt/coincontroldialog.h +++ b/wallet/qt/coincontroldialog.h @@ -45,7 +45,7 @@ class CoinControlDialog : public QDialog QAction* copyTransactionOutputIndexAction; // QAction *lockAction; // QAction *unlockAction; - bool fLedgerTx; + bool fLedgerTx; QString fromAccount; QString strPad(QString, int, QString); diff --git a/wallet/qt/editaddressdialog.cpp b/wallet/qt/editaddressdialog.cpp index f73b10d5a..ee3409932 100644 --- a/wallet/qt/editaddressdialog.cpp +++ b/wallet/qt/editaddressdialog.cpp @@ -1,5 +1,4 @@ #include "editaddressdialog.h" -#include "ui_editaddressdialog.h" #include "addresstablemodel.h" #include "guiutil.h" #include "ledger/bip32.h" @@ -7,35 +6,32 @@ #include "ledger/utils.h" #include "ledger_ui/ledgermessagebox.h" #include "ledger_ui/ledgeruiutils.h" +#include "ui_editaddressdialog.h" #include -#include #include +#include -void AddLedgerRowWorker::addRow(Ui::EditAddressDialog *ui, AddressTableModel *model, QSharedPointer workerPtr) { - auto address = model->addRow( - AddressTableModel::ReceiveLedger, - ui->labelEdit->text(), - ui->addressEdit->text(), - ui->ledgerAccountEdit->text(), - ui->ledgerIndexEdit->text() - ); +void AddLedgerRowWorker::addRow(Ui::EditAddressDialog* ui, AddressTableModel* model, + QSharedPointer workerPtr) +{ + auto address = + model->addRow(AddressTableModel::ReceiveLedger, ui->labelEdit->text(), ui->addressEdit->text(), + ui->ledgerAccountEdit->text(), ui->ledgerIndexEdit->text()); emit resultReady(address); workerPtr.reset(); } -EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : - QDialog(parent), - ui(new Ui::EditAddressDialog), mapper(0), mode(modeIn), model(0) +EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget* parent) + : QDialog(parent), ui(new Ui::EditAddressDialog), mapper(0), mode(modeIn), model(0) { ui->setupUi(this); GUIUtil::setupAddressWidget(ui->addressEdit, this); bool ledgerItemsEnabled = true; - switch(modeIn) - { + switch (modeIn) { case NewReceivingAddress: setWindowTitle(tr("New receiving address")); ui->addressEdit->setEnabled(false); @@ -76,19 +72,18 @@ EditAddressDialog::EditAddressDialog(Mode modeIn, QWidget *parent) : GUIUtil::setupIntWidget(ui->ledgerAccountEdit, this, 0, ledger::MAX_RECOMMENDED_ACCOUNT); GUIUtil::setupIntWidget(ui->ledgerIndexEdit, this, 0, ledger::MAX_RECOMMENDED_INDEX); - connect(this->ui->ledgerAccountEdit, SIGNAL(textChanged(QString)), this, SLOT(updateLedgerPathLabel())); - connect(this->ui->ledgerIndexEdit, SIGNAL(textChanged(QString)), this, SLOT(updateLedgerPathLabel())); + connect(this->ui->ledgerAccountEdit, SIGNAL(textChanged(QString)), this, + SLOT(updateLedgerPathLabel())); + connect(this->ui->ledgerIndexEdit, SIGNAL(textChanged(QString)), this, + SLOT(updateLedgerPathLabel())); } -EditAddressDialog::~EditAddressDialog() -{ - delete ui; -} +EditAddressDialog::~EditAddressDialog() { delete ui; } -void EditAddressDialog::setModel(AddressTableModel *modelIn) +void EditAddressDialog::setModel(AddressTableModel* modelIn) { this->model = modelIn; - if(!modelIn) + if (!modelIn) return; mapper->setModel(modelIn); @@ -99,51 +94,37 @@ void EditAddressDialog::setModel(AddressTableModel *modelIn) mapper->addMapping(ui->ledgerIndexEdit, AddressTableModel::LedgerIndex); } -void EditAddressDialog::loadRow(int row) -{ - mapper->setCurrentIndex(row); -} +void EditAddressDialog::loadRow(int row) { mapper->setCurrentIndex(row); } bool EditAddressDialog::saveCurrentRow() { - if(!model) + if (!model) return false; bool isLedger = ui->ledgerCheckBox->isChecked(); - switch(mode) - { + switch (mode) { case NewReceivingAddress: - if (isLedger) - { + if (isLedger) { QSharedPointer worker = QSharedPointer::create(); - ledger_ui::LedgerMessageBox msgBox(this, worker); + ledger_ui::LedgerMessageBox msgBox(this, worker); connect(worker.data(), SIGNAL(resultReady(QString)), this, SLOT(setAddress(QString))); connect(worker.data(), SIGNAL(resultReady(QString)), &msgBox, SLOT(quit())); - QTimer::singleShot(0, worker.data(), [this, worker]() { worker->addRow(ui, model, worker); }); + QTimer::singleShot(0, worker.data(), + [this, worker]() { worker->addRow(ui, model, worker); }); msgBox.exec(); } else { - address = model->addRow( - AddressTableModel::Receive, - ui->labelEdit->text(), - ui->addressEdit->text(), - ui->ledgerAccountEdit->text(), - ui->ledgerIndexEdit->text() - ); + address = + model->addRow(AddressTableModel::Receive, ui->labelEdit->text(), ui->addressEdit->text(), + ui->ledgerAccountEdit->text(), ui->ledgerIndexEdit->text()); } break; case NewSendingAddress: - address = model->addRow( - AddressTableModel::Send, - ui->labelEdit->text(), - ui->addressEdit->text(), - ui->ledgerAccountEdit->text(), - ui->ledgerIndexEdit->text() - ); + address = model->addRow(AddressTableModel::Send, ui->labelEdit->text(), ui->addressEdit->text(), + ui->ledgerAccountEdit->text(), ui->ledgerIndexEdit->text()); break; case EditReceivingAddress: case EditSendingAddress: - if(mapper->submit()) - { + if (mapper->submit()) { address = ui->addressEdit->text(); } break; @@ -153,13 +134,11 @@ bool EditAddressDialog::saveCurrentRow() void EditAddressDialog::accept() { - if(!model) + if (!model) return; - if(!saveCurrentRow()) - { - switch(model->getEditStatus()) - { + if (!saveCurrentRow()) { + switch (model->getEditStatus()) { case AddressTableModel::OK: // Failed with unknown reason. Just reject. break; @@ -168,50 +147,55 @@ void EditAddressDialog::accept() break; case AddressTableModel::INVALID_ADDRESS: QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is not a valid neblio address.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); + tr("The entered address \"%1\" is not a valid neblio address.") + .arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::DUPLICATE_ADDRESS: QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); + tr("The entered address \"%1\" is already in the address book.") + .arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::INVALID_LEDGER_ACCOUNT: - QMessageBox::warning(this, windowTitle(), - tr("The entered Ledger account \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerAccountEdit->text()).arg(ledger::MAX_RECOMMENDED_ACCOUNT), + QMessageBox::warning( + this, windowTitle(), + tr("The entered Ledger account \"%1\" is not in the recommended range (0-%2).") + .arg(ui->ledgerAccountEdit->text()) + .arg(ledger::MAX_RECOMMENDED_ACCOUNT), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::INVALID_LEDGER_INDEX: - QMessageBox::warning(this, windowTitle(), - tr("The entered Ledger address index \"%1\" is not in the recommended range (0-%2).").arg(ui->ledgerIndexEdit->text()).arg(ledger::MAX_RECOMMENDED_INDEX), + QMessageBox::warning( + this, windowTitle(), + tr("The entered Ledger address index \"%1\" is not in the recommended range (0-%2).") + .arg(ui->ledgerIndexEdit->text()) + .arg(ledger::MAX_RECOMMENDED_INDEX), QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::WALLET_UNLOCK_FAILURE: - QMessageBox::critical(this, windowTitle(), - tr("Could not unlock wallet."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::critical(this, windowTitle(), tr("Could not unlock wallet."), QMessageBox::Ok, + QMessageBox::Ok); break; case AddressTableModel::KEY_GENERATION_FAILURE: - QMessageBox::critical(this, windowTitle(), - tr("New key generation failed."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::critical(this, windowTitle(), tr("New key generation failed."), QMessageBox::Ok, + QMessageBox::Ok); break; case AddressTableModel::LABEL_USED_BY_LEDGER: QMessageBox::critical(this, windowTitle(), - tr("This label is already used by a Ledger address."), - QMessageBox::Ok, QMessageBox::Ok); + tr("This label is already used by a Ledger address."), QMessageBox::Ok, + QMessageBox::Ok); break; case AddressTableModel::LABEL_NOT_USABLE_FOR_LEDGER: - QMessageBox::critical(this, windowTitle(), - tr("Ledger addresses require a unique label."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::critical(this, windowTitle(), tr("Ledger addresses require a unique label."), + QMessageBox::Ok, QMessageBox::Ok); break; case AddressTableModel::LEDGER_ERROR: - QMessageBox::critical(this, windowTitle(), + QMessageBox::critical( + this, windowTitle(), ledger_ui::GetQtErrorMessage(ledger::LedgerException(model->getLedgerError())), QMessageBox::Ok, QMessageBox::Ok); break; - } return; } @@ -221,7 +205,7 @@ void EditAddressDialog::accept() void EditAddressDialog::updateLedgerPathLabel() { QString account = ui->ledgerAccountEdit->text(); - QString index = ui->ledgerIndexEdit->text(); + QString index = ui->ledgerIndexEdit->text(); if (!ledgerItemsVisible || account.isEmpty() || index.isEmpty()) { return; @@ -230,10 +214,7 @@ void EditAddressDialog::updateLedgerPathLabel() ui->ledgerPathLabel->setText(tr("Ledger path: %1").arg(QString::fromStdString(path))); } -void EditAddressDialog::setAddress(QString addressIn) -{ - this->address = addressIn; -} +void EditAddressDialog::setAddress(QString addressIn) { this->address = addressIn; } void EditAddressDialog::on_ledgerCheckBox_toggled(bool checked) { @@ -249,12 +230,9 @@ void EditAddressDialog::on_ledgerCheckBox_toggled(bool checked) setFixedHeight(sizeHint().height()); } -QString EditAddressDialog::getAddress() const -{ - return address; -} +QString EditAddressDialog::getAddress() const { return address; } -void EditAddressDialog::setAddressEditValue(const QString &addressIn) +void EditAddressDialog::setAddressEditValue(const QString& addressIn) { this->address = addressIn; ui->addressEdit->setText(addressIn); diff --git a/wallet/qt/editaddressdialog.h b/wallet/qt/editaddressdialog.h index d99fbe387..af8f4a0c6 100644 --- a/wallet/qt/editaddressdialog.h +++ b/wallet/qt/editaddressdialog.h @@ -8,12 +8,12 @@ class QDataWidgetMapper; QT_END_NAMESPACE namespace Ui { - class EditAddressDialog; +class EditAddressDialog; } class AddressTableModel; /* Object for adding a Ledger address row in a separate thread. -*/ + */ class AddLedgerRowWorker : public QObject { Q_OBJECT @@ -21,7 +21,8 @@ class AddLedgerRowWorker : public QObject public slots: // we use the shared pointer argument to ensure that workerPtr will be deleted after doing the // retrieval - void addRow(Ui::EditAddressDialog *ui, AddressTableModel *model, QSharedPointer workerPtr); + void addRow(Ui::EditAddressDialog* ui, AddressTableModel* model, + QSharedPointer workerPtr); signals: void resultReady(QString); @@ -34,21 +35,22 @@ class EditAddressDialog : public QDialog Q_OBJECT public: - enum Mode { + enum Mode + { NewReceivingAddress, NewSendingAddress, EditReceivingAddress, EditSendingAddress, }; - explicit EditAddressDialog(Mode modeIn, QWidget *parent = 0); + explicit EditAddressDialog(Mode modeIn, QWidget* parent = 0); ~EditAddressDialog(); - void setModel(AddressTableModel *model); + void setModel(AddressTableModel* model); void loadRow(int row); QString getAddress() const; - void setAddressEditValue(const QString &addressIn); + void setAddressEditValue(const QString& addressIn); public slots: void accept(); @@ -59,11 +61,11 @@ public slots: private: bool saveCurrentRow(); - Ui::EditAddressDialog *ui; - QDataWidgetMapper *mapper; - Mode mode; - AddressTableModel *model; - bool ledgerItemsVisible; + Ui::EditAddressDialog* ui; + QDataWidgetMapper* mapper; + Mode mode; + AddressTableModel* model; + bool ledgerItemsVisible; QString address; }; diff --git a/wallet/qt/ledger_ui/ledgermessagebox.cpp b/wallet/qt/ledger_ui/ledgermessagebox.cpp index 3181d6ef7..5fe9f15d1 100644 --- a/wallet/qt/ledger_ui/ledgermessagebox.cpp +++ b/wallet/qt/ledger_ui/ledgermessagebox.cpp @@ -1,36 +1,36 @@ #include "ledger_ui/ledgermessagebox.h" -#include -#include #include +#include +#include -namespace ledger_ui +namespace ledger_ui { +LedgerMessageBox::LedgerMessageBox(QWidget* parent, QSharedPointer worker, const QString& text) + : worker_(worker), msgBox_(parent) { - LedgerMessageBox::LedgerMessageBox(QWidget *parent, QSharedPointer worker, const QString &text) : worker_(worker), msgBox_(parent) - { - worker_->moveToThread(&thread_); - thread_.start(); + worker_->moveToThread(&thread_); + thread_.start(); - msgBox_.setIcon(QMessageBox::Icon::Information); - msgBox_.setWindowTitle(parent->windowTitle()); - if (text.isEmpty()) { - msgBox_.setText("Please confirm or cancel the action on your Ledger device."); - } else { - msgBox_.setText(text); - msgBox_.setInformativeText("Please confirm or cancel the action on your Ledger device."); - } - msgBox_.setStandardButtons(QMessageBox::StandardButton::NoButton); + msgBox_.setIcon(QMessageBox::Icon::Information); + msgBox_.setWindowTitle(parent->windowTitle()); + if (text.isEmpty()) { + msgBox_.setText("Please confirm or cancel the action on your Ledger device."); + } else { + msgBox_.setText(text); + msgBox_.setInformativeText("Please confirm or cancel the action on your Ledger device."); } + msgBox_.setStandardButtons(QMessageBox::StandardButton::NoButton); +} - void LedgerMessageBox::exec() - { - msgBox_.exec(); - thread_.wait(); // to make sure that the thread is finished - } +void LedgerMessageBox::exec() +{ + msgBox_.exec(); + thread_.wait(); // to make sure that the thread is finished +} - void LedgerMessageBox::quit() - { - msgBox_.accept(); - thread_.quit(); - } +void LedgerMessageBox::quit() +{ + msgBox_.accept(); + thread_.quit(); } +} // namespace ledger_ui diff --git a/wallet/qt/ledger_ui/ledgermessagebox.h b/wallet/qt/ledger_ui/ledgermessagebox.h index 51d469cc0..3945607ea 100644 --- a/wallet/qt/ledger_ui/ledgermessagebox.h +++ b/wallet/qt/ledger_ui/ledgermessagebox.h @@ -1,30 +1,29 @@ #ifndef LEDGER_UI_LEDGERMESSAGEBOX_H #define LEDGER_UI_LEDGERMESSAGEBOX_H +#include #include -#include -#include #include -#include +#include +#include -namespace ledger_ui +namespace ledger_ui { +class LedgerMessageBox : public QObject { - class LedgerMessageBox : public QObject - { Q_OBJECT - public: - LedgerMessageBox(QWidget *parent, QSharedPointer worker, const QString &text = QString()); - void exec(); +public: + LedgerMessageBox(QWidget* parent, QSharedPointer worker, const QString& text = QString()); + void exec(); - public slots: - void quit(); +public slots: + void quit(); - private: - QThread thread_; - QSharedPointer worker_; - QMessageBox msgBox_; - }; -} +private: + QThread thread_; + QSharedPointer worker_; + QMessageBox msgBox_; +}; +} // namespace ledger_ui #endif // LEDGER_UI_LEDGERMESSAGEBOX_H diff --git a/wallet/qt/ledger_ui/ledgeruiutils.cpp b/wallet/qt/ledger_ui/ledgeruiutils.cpp index 071575f3c..77f4d630d 100644 --- a/wallet/qt/ledger_ui/ledgeruiutils.cpp +++ b/wallet/qt/ledger_ui/ledgeruiutils.cpp @@ -1,12 +1,14 @@ #include "ledger_ui/ledgeruiutils.h" -#include #include +#include -namespace ledger_ui +namespace ledger_ui { +QString GetQtErrorMessage(const ledger::LedgerException& e) { - QString GetQtErrorMessage(const ledger::LedgerException &e) - { - return QObject::tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure that your device is connected to the computer and the Neblio app is opened on the device.").arg(QString::fromStdString(e.GetMessage())); - } + return QObject:: + tr("A Ledger error occured: %1\n\nIf you did not cancel the operation intentionally, make sure " + "that your device is connected to the computer and the Neblio app is opened on the device.") + .arg(QString::fromStdString(e.GetMessage())); } +} // namespace ledger_ui diff --git a/wallet/qt/ledger_ui/ledgeruiutils.h b/wallet/qt/ledger_ui/ledgeruiutils.h index 7bcd7541c..61eb0788f 100644 --- a/wallet/qt/ledger_ui/ledgeruiutils.h +++ b/wallet/qt/ledger_ui/ledgeruiutils.h @@ -1,13 +1,13 @@ #ifndef LEDGER_UI_LEDGERUIUTILS_H #define LEDGER_UI_LEDGERUIUTILS_H -#include #include +#include #include "ledger/error.h" namespace ledger_ui { - QString GetQtErrorMessage(const ledger::LedgerException &e); +QString GetQtErrorMessage(const ledger::LedgerException& e); } #endif // LEDGER_UI_LEDGERUIUTILS_H diff --git a/wallet/qt/newstakedelegationdialog.h b/wallet/qt/newstakedelegationdialog.h index c669417f0..f9e1a4929 100644 --- a/wallet/qt/newstakedelegationdialog.h +++ b/wallet/qt/newstakedelegationdialog.h @@ -40,7 +40,7 @@ class NewStakeDelegationDialog : public QDialog QCheckBox* useDelegatedCheckbox; QFrame* paymentSeparator; - QPushButton* coinControlButton; + QPushButton* coinControlButton; MessageBoxWithTimer* timedMessageBox; QPushButton* timedMessageBox_yesButton; diff --git a/wallet/qt/ntp1/issuenewntp1tokendialog.h b/wallet/qt/ntp1/issuenewntp1tokendialog.h index e0191287c..9597d223a 100644 --- a/wallet/qt/ntp1/issuenewntp1tokendialog.h +++ b/wallet/qt/ntp1/issuenewntp1tokendialog.h @@ -72,7 +72,7 @@ class IssueNewNTP1TokenDialog : public QDialog NTP1TokenSymbolValidator* tokenSymbolValidator; - QPushButton* coinControlButton; + QPushButton* coinControlButton; WalletModel* walletModel = nullptr; diff --git a/wallet/qt/sendcoinsdialog.cpp b/wallet/qt/sendcoinsdialog.cpp index ffde966d3..050a18a05 100644 --- a/wallet/qt/sendcoinsdialog.cpp +++ b/wallet/qt/sendcoinsdialog.cpp @@ -26,17 +26,14 @@ #include "ledger_ui/ledgermessagebox.h" -void LedgerSignTxWorker::signTx( - WalletModel* model, - QList recipients, - boost::shared_ptr ntp1wallet, - const RawNTP1MetadataBeforeSend& ntp1metadata, - bool fSpendDelegated, - const CCoinControl* coinControl, - const std::string& strFromAccount, - QSharedPointer workerPtr -) { - auto sendStatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegated, coinControl, strFromAccount, true); +void LedgerSignTxWorker::signTx(WalletModel* model, QList recipients, + boost::shared_ptr ntp1wallet, + const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, + const CCoinControl* coinControl, const std::string& strFromAccount, + QSharedPointer workerPtr) +{ + auto sendStatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegated, + coinControl, strFromAccount, true); emit resultReady(sendStatus); workerPtr.reset(); @@ -294,7 +291,6 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - CCoinControl* coinControl = nullptr; if (model->getOptionsModel() && model->getOptionsModel()->getCoinControlFeatures()) coinControl = CoinControlDialog::coinControl; @@ -311,14 +307,19 @@ void SendCoinsDialog::on_sendButton_clicked() strFromAccount = ui->ledgerPayFromNameEdit->text().toStdString(); if (fLedgerTx) { - QSharedPointer worker = QSharedPointer::create(); - ledger_ui::LedgerMessageBox msgBox(this, worker); - connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), this, SLOT(setSendStatus(WalletModel::SendCoinsReturn))); - connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), &msgBox, SLOT(quit())); - QTimer::singleShot(0, worker.data(), [&]() { worker->signTx(model, recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, coinControl, strFromAccount, worker); }); - msgBox.exec(); + QSharedPointer worker = QSharedPointer::create(); + ledger_ui::LedgerMessageBox msgBox(this, worker); + connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), this, + SLOT(setSendStatus(WalletModel::SendCoinsReturn))); + connect(worker.data(), SIGNAL(resultReady(WalletModel::SendCoinsReturn)), &msgBox, SLOT(quit())); + QTimer::singleShot(0, worker.data(), [&]() { + worker->signTx(model, recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, + coinControl, strFromAccount, worker); + }); + msgBox.exec(); } else { - sendStatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, coinControl, strFromAccount, fLedgerTx); + sendStatus = model->sendCoins(recipients, ntp1wallet, ntp1metadata, fSpendDelegatedOutputs, + coinControl, strFromAccount, fLedgerTx); } switch (sendStatus.status) { @@ -469,8 +470,8 @@ SendCoinsEntry* SendCoinsDialog::addEntry() // metadata can be fixed if more recipients are added, so remove red color from the button ui->editMetadataButton->setStyleSheet(""); - bool enableNTP1Tokens = !ui->ledgerCheckBox->isChecked(); - SendCoinsEntry* entry = new SendCoinsEntry(this, enableNTP1Tokens); + bool enableNTP1Tokens = !ui->ledgerCheckBox->isChecked(); + SendCoinsEntry* entry = new SendCoinsEntry(this, enableNTP1Tokens); entry->setModel(model); ui->entries->addWidget(entry); connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); @@ -672,7 +673,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) // Coin Control: button inputs -> show actual coin control dialog void SendCoinsDialog::coinControlButtonClicked() { - bool fLedgerTx = false; + bool fLedgerTx = false; QString fromAccount; if (ui->ledgerCheckBox->isChecked()) { if (ui->ledgerPayFromNameEdit->text().isEmpty()) { @@ -682,7 +683,7 @@ void SendCoinsDialog::coinControlButtonClicked() ui->ledgerPayFromNameEdit->setStyleSheet(STYLE_INVALID); return; } - fLedgerTx = true; + fLedgerTx = true; fromAccount = ui->ledgerPayFromNameEdit->text(); } CoinControlDialog dlg(this, fLedgerTx, fromAccount); diff --git a/wallet/qt/sendcoinsdialog.h b/wallet/qt/sendcoinsdialog.h index 457d58908..88f981ec0 100644 --- a/wallet/qt/sendcoinsdialog.h +++ b/wallet/qt/sendcoinsdialog.h @@ -1,16 +1,16 @@ #ifndef SENDCOINSDIALOG_H #define SENDCOINSDIALOG_H -#include #include #include #include #include #include +#include #include "coincontroldialog.h" -#include "ntp1/ntp1wallet.h" #include "ntp1/ntp1transaction.h" +#include "ntp1/ntp1wallet.h" #include "walletmodel.h" namespace Ui { @@ -25,7 +25,7 @@ class QUrl; QT_END_NAMESPACE /* Object for signing a transaction on a Ledger device in a separate thread. -*/ + */ class LedgerSignTxWorker : public QObject { Q_OBJECT @@ -33,16 +33,10 @@ class LedgerSignTxWorker : public QObject public slots: // we use the shared pointer argument to ensure that workerPtr will be deleted after doing the // retrieval - void signTx( - WalletModel* model, - QList recipients, - boost::shared_ptr ntp1wallet, - const RawNTP1MetadataBeforeSend& ntp1metadata, - bool fSpendDelegated, - const CCoinControl* coinControl, - const std::string& strFromAccount, - QSharedPointer workerPtr - ); + void signTx(WalletModel* model, QList recipients, + boost::shared_ptr ntp1wallet, const RawNTP1MetadataBeforeSend& ntp1metadata, + bool fSpendDelegated, const CCoinControl* coinControl, const std::string& strFromAccount, + QSharedPointer workerPtr); signals: void resultReady(WalletModel::SendCoinsReturn); diff --git a/wallet/qt/sendcoinsentry.cpp b/wallet/qt/sendcoinsentry.cpp index 2c63948aa..96d7c4e87 100644 --- a/wallet/qt/sendcoinsentry.cpp +++ b/wallet/qt/sendcoinsentry.cpp @@ -10,7 +10,8 @@ #include #include -SendCoinsEntry::SendCoinsEntry(QWidget* parent, bool enableNTP1Tokens) : QFrame(parent), ui(new Ui::SendCoinsEntry), model(0) +SendCoinsEntry::SendCoinsEntry(QWidget* parent, bool enableNTP1Tokens) + : QFrame(parent), ui(new Ui::SendCoinsEntry), model(0) { ui->setupUi(this, enableNTP1Tokens); @@ -127,9 +128,7 @@ SendCoinsRecipient SendCoinsEntry::getValue() return rv; } -bool SendCoinsEntry::isNTP1TokenSelected() const { - return ui->payAmount->isNTP1TokenSelected(); -} +bool SendCoinsEntry::isNTP1TokenSelected() const { return ui->payAmount->isNTP1TokenSelected(); } QWidget* SendCoinsEntry::setupTabChain(QWidget* prev) { diff --git a/wallet/qt/transactiondesc.cpp b/wallet/qt/transactiondesc.cpp index 966c3456b..3231aab17 100644 --- a/wallet/qt/transactiondesc.cpp +++ b/wallet/qt/transactiondesc.cpp @@ -195,7 +195,8 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle } else { bool fAllFromMe = true; for (const CTxIn& txin : wtx.vin) { - fAllFromMe = fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE_AVAILABLE); + fAllFromMe = + fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE_AVAILABLE); } bool fAllToMe = true; @@ -203,7 +204,8 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle if (NTP1Transaction::IsTxOutputOpRet(&txout)) { continue; } - fAllToMe = fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE); + fAllToMe = + fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE); } if (fAllFromMe) { @@ -386,11 +388,11 @@ QString TransactionDesc::toHTML(const ITxDB& txdb, CWallet* wallet, const CWalle } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, vout.nValue); - strHTML = - strHTML + " IsMine=" + - (IsMineCheck(wallet->IsMine(vout), isminetype::ISMINE_SPENDABLE_AVAILABLE) ? tr("true") - : tr("false")) + - ""; + strHTML = strHTML + " IsMine=" + + (IsMineCheck(wallet->IsMine(vout), isminetype::ISMINE_SPENDABLE_AVAILABLE) + ? tr("true") + : tr("false")) + + ""; strHTML = strHTML + " IsWatchOnly=" + (IsMineCheck(wallet->IsMine(vout), isminetype::ISMINE_WATCH_ONLY) ? tr("true") diff --git a/wallet/qt/transactionrecord.cpp b/wallet/qt/transactionrecord.cpp index 6fedaf0c2..84c1f36b3 100644 --- a/wallet/qt/transactionrecord.cpp +++ b/wallet/qt/transactionrecord.cpp @@ -107,7 +107,8 @@ QList TransactionRecord::decomposeTransaction(const CWallet* } else { bool fAllFromMe = true; for (const CTxIn& txin : wtx.vin) { - fAllFromMe = fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE_AVAILABLE); + fAllFromMe = + fAllFromMe && IsMineCheck(wallet->IsMine(txin), isminetype::ISMINE_SPENDABLE_AVAILABLE); } bool fAllToMe = true; @@ -116,7 +117,8 @@ QList TransactionRecord::decomposeTransaction(const CWallet* if (NTP1Transaction::IsTxOutputOpRet(&txout, nullptr)) { continue; } - fAllToMe = fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE); + fAllToMe = + fAllToMe && IsMineCheck(wallet->IsMine(txout), isminetype::ISMINE_SPENDABLE_AVAILABLE); } if (fAllFromMe && fAllToMe) { diff --git a/wallet/qt/transactionview.cpp b/wallet/qt/transactionview.cpp index b70066ab9..25710d079 100644 --- a/wallet/qt/transactionview.cpp +++ b/wallet/qt/transactionview.cpp @@ -198,7 +198,7 @@ void TransactionView::setModel(WalletModel* modelIn) transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Date, 120); transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Type, 120); transactionView->horizontalHeader()->setSectionResizeMode(TransactionTableModel::ToAddress, - QHeaderView::Stretch); + QHeaderView::Stretch); transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Amount, 120); } } @@ -236,7 +236,8 @@ void TransactionView::chooseDate(int idx) case ThisWeek: { // Find last Monday QDate startOfWeek = current.addDays(-(current.dayOfWeek() - 1)); - transactionProxyModel->setDateRange(GetStartOfDay(startOfWeek), TransactionFilterProxy::MAX_DATE); + transactionProxyModel->setDateRange(GetStartOfDay(startOfWeek), + TransactionFilterProxy::MAX_DATE); } break; case ThisMonth: diff --git a/wallet/qt/walletmodel.cpp b/wallet/qt/walletmodel.cpp index 7a79dfc2f..ff6c582e7 100644 --- a/wallet/qt/walletmodel.cpp +++ b/wallet/qt/walletmodel.cpp @@ -198,7 +198,7 @@ void WalletModel::updateAddressBook(const QString& address, const QString& label const QString& purpose, int status) { if (addressTableModel) - addressTableModel->updateEntry(address, label, (isminetype) isMine, purpose, status); + addressTableModel->updateEntry(address, label, (isminetype)isMine, purpose, status); } bool WalletModel::validateAddress(const QString& address) @@ -218,8 +218,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, const CCoinControl* coinControl, - const std::string& strFromAccount, - bool fLedgerTx) + const std::string& strFromAccount, bool fLedgerTx) { qint64 total = 0; QString hex; @@ -315,9 +314,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(QList } } - CWalletTx wtx; + CWalletTx wtx; wtx.strFromAccount = strFromAccount; - wtx.fLedgerTx = fLedgerTx; + wtx.fLedgerTx = fLedgerTx; CReserveKey keyChange(wallet); int64_t nFeeRequired = 0; diff --git a/wallet/qt/walletmodel.h b/wallet/qt/walletmodel.h index b9e2ea34b..a9ecd5301 100644 --- a/wallet/qt/walletmodel.h +++ b/wallet/qt/walletmodel.h @@ -119,7 +119,7 @@ class WalletModel : public QObject SendCoinsReturn sendCoins(QList recipients, boost::shared_ptr ntp1wallet, const RawNTP1MetadataBeforeSend& ntp1metadata, bool fSpendDelegated, - const CCoinControl* coinControl = nullptr, + const CCoinControl* coinControl = nullptr, const std::string& strFromAccount = "", bool fLedgerTx = false); // Wallet encryption diff --git a/wallet/rpcdump.cpp b/wallet/rpcdump.cpp index b5c28919a..0bc0151dd 100644 --- a/wallet/rpcdump.cpp +++ b/wallet/rpcdump.cpp @@ -420,7 +420,8 @@ bool _AddKeyToLocalWallet(const CKey& Key, const std::string& strLabel, int64_t return true; } -bool _AddLedgerKeyToLocalWallet(const CLedgerKey& ledgerKey, const std::string& strLabel, bool addInAddressBook) +bool _AddLedgerKeyToLocalWallet(const CLedgerKey& ledgerKey, const std::string& strLabel, + bool addInAddressBook) { CKeyID keyid = ledgerKey.vchPubKey.GetID(); NLog.write(b_sev::info, "Importing ledger address {}...", CBitcoinAddress(keyid).ToString()); @@ -511,7 +512,7 @@ std::pair ImportBackupWallet(const std::string& Src, std::string& Pa std::set ledgerKeyIDsSet; backupWallet.GetLedgerKeys(ledgerKeyIDsSet); - allKeyIDsSet.insert(ledgerKeyIDsSet.begin(),ledgerKeyIDsSet.end()); + allKeyIDsSet.insert(ledgerKeyIDsSet.begin(), ledgerKeyIDsSet.end()); // deque to simply elements access const std::deque allKeyIDs(allKeyIDsSet.begin(), allKeyIDsSet.end()); @@ -526,24 +527,24 @@ std::pair ImportBackupWallet(const std::string& Src, std::string& Pa for (long i = 0; i < static_cast(allKeyIDs.size()); i++) { AddressBookIt it = addrBook.find(allKeyIDs[i]); bool foundKeyInAddressBook = (it != addrBook.end()); - - CKey key; - CLedgerKey ledgerKey; + + CKey key; + CLedgerKey ledgerKey; if (backupWallet.GetKey(allKeyIDs[i], key)) { // add the key, whether to the address book or simply to reserve if (foundKeyInAddressBook) { // import from address book bool addSucceeded = _AddKeyToLocalWallet( key, it->second.name, - backupWallet.mapKeyMetadata.at(boost::get(it->first)).nCreateTime, earliestTime, - true); + backupWallet.mapKeyMetadata.at(boost::get(it->first)).nCreateTime, + earliestTime, true); if (addSucceeded) succeessfullyAddedOutOfTotal.first++; } else { // import reserve keys - bool addSucceeded = - _AddKeyToLocalWallet(key, "", backupWallet.mapKeyMetadata.at(allKeyIDs[i]).nCreateTime, - earliestTime, importReserveToAddressBook); + bool addSucceeded = _AddKeyToLocalWallet( + key, "", backupWallet.mapKeyMetadata.at(allKeyIDs[i]).nCreateTime, earliestTime, + importReserveToAddressBook); if (addSucceeded) succeessfullyAddedOutOfTotal.first++; } @@ -553,7 +554,8 @@ std::pair ImportBackupWallet(const std::string& Src, std::string& Pa if (addSucceeded) succeessfullyAddedOutOfTotal.first++; } else { - bool addSucceeded = _AddLedgerKeyToLocalWallet(ledgerKey, "", importReserveToAddressBook); + bool addSucceeded = + _AddLedgerKeyToLocalWallet(ledgerKey, "", importReserveToAddressBook); if (addSucceeded) succeessfullyAddedOutOfTotal.first++; } @@ -606,11 +608,11 @@ std::string GetCurrentWalletHash() { std::set allKeyIDsSet; pwalletMain->GetKeys(allKeyIDsSet); - + std::set ledgerKeyIDsSet; pwalletMain->GetLedgerKeys(ledgerKeyIDsSet); - allKeyIDsSet.insert(ledgerKeyIDsSet.begin(),ledgerKeyIDsSet.end()); + allKeyIDsSet.insert(ledgerKeyIDsSet.begin(), ledgerKeyIDsSet.end()); // deque to simply elements access const std::deque allKeyIDs(allKeyIDsSet.begin(), allKeyIDsSet.end()); @@ -619,8 +621,8 @@ std::string GetCurrentWalletHash() std::string finalStringToHash; for (long i = 0; i < static_cast(allKeyIDs.size()); i++) { // retrieve key using key ID - CKey key; - CLedgerKey ledgerKey; + CKey key; + CLedgerKey ledgerKey; if (pwalletMain->GetKey(allKeyIDs[i], key)) { finalStringToHash += key.GetPubKey().GetHash().ToString(); } else if (pwalletMain->GetLedgerKey(allKeyIDs[i], ledgerKey)) { diff --git a/wallet/rpcwallet.cpp b/wallet/rpcwallet.cpp index 8ba8b4e27..589150f45 100644 --- a/wallet/rpcwallet.cpp +++ b/wallet/rpcwallet.cpp @@ -490,9 +490,10 @@ Value getnewaddress(const Array& params, bool fHelp) Value addledgeraddress(const Array& params, bool fHelp) { if (fHelp || params.size() != 3) - throw runtime_error("addledgeraddress