From 7d1f956b57fd3c22c1e781a770ba666f30ed7b60 Mon Sep 17 00:00:00 2001 From: BTChip Date: Sun, 17 Sep 2017 18:34:38 +0200 Subject: [PATCH 1/3] Check firmware version for Segwit related transactions, add native segwit support --- plugins/ledger/ledger.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py index 00708a4fc..e41cff0cf 100644 --- a/plugins/ledger/ledger.py +++ b/plugins/ledger/ledger.py @@ -2,6 +2,7 @@ from struct import pack, unpack import hashlib import time import sys +import os import traceback import electrum @@ -59,7 +60,14 @@ class Ledger_Client(): #self.get_client() # prompt for the PIN before displaying the dialog if necessary #self.handler.show_message("Computing master public key") try: - xtype = 'segwit_p2sh' if bip32_path.startswith("m/49'/") else 'standard' + if (os.getenv("LEDGER_NATIVE_SEGWIT") is not None) and self.supports_native_segwit(): + xtype = 'segwit' + elif bip32_path.startswith("m/49'/"): + if not self.supports_segwit(): + raise Exception("Firmware version too old for Segwit support. Please update at https://www.ledgerwallet.com") + xtype = 'segwit_p2sh' + else: + xtype = 'standard' splitPath = bip32_path.split('/') if splitPath[0] == 'm': splitPath = splitPath[1:] @@ -107,10 +115,20 @@ class Ledger_Client(): def supports_multi_output(self): return self.multiOutputSupported + def supports_segwit(self): + return self.segwitSupported + + def supports_native_segwit(self): + return self.nativeSegwitSupported + def perform_hw1_preflight(self): try: - firmware = self.dongleObject.getFirmwareVersion()['version'].split(".") + firmwareInfo = self.dongleObject.getFirmwareVersion() + firmware = firmwareInfo['version'].split(".") self.multiOutputSupported = int(firmware[0]) >= 1 and int(firmware[1]) >= 1 and int(firmware[2]) >= 4 + self.segwitSupported = (int(firmware[0]) >= 1 and int(firmware[1]) >= 1 and int(firmware[2]) >= 10) or (firmwareInfo['specialVersion'] == 0x20 and int(firmware[0]) == 1 and int(firmware[1]) == 0 and int(firmware[2]) >= 4) + self.nativeSegwitSupported = int(firmware[0]) >= 1 and int(firmware[1]) >= 1 and int(firmware[2]) >= 10 + if not checkFirmware(firmware): self.dongleObject.dongle.close() raise Exception("HW1 firmware version too old. Please update at https://www.ledgerwallet.com") @@ -280,7 +298,14 @@ class Ledger_KeyStore(Hardware_KeyStore): if txin['type'] in ['p2sh']: p2shTransaction = True - if txin['type'] in ['p2wpkh-p2sh']: + if txin['type'] in ['p2wpkh-p2sh', 'p2wsh-p2sh']: + if not self.get_client_electrum().supports_segwit(): + self.give_error("Firmware version too old to support segwit. Please update at https://www.ledgerwallet.com") + segwitTransaction = True + + if txin['type'] in ['p2wpkh', 'p2wsh']: + if not self.get_client_electrum().supports_native_segwit(): + self.give_error("Firmware version too old to support native segwit. Please update at https://www.ledgerwallet.com") segwitTransaction = True pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin) From e90f14492e36c7063a0cf46005893ff1a524543b Mon Sep 17 00:00:00 2001 From: Joe Ellis Date: Mon, 18 Sep 2017 23:14:12 +0100 Subject: [PATCH 2/3] Added scroll pane in install wizard confirmation dialog. Prevent cropping of text that occurs when you have just a label. In response to issue 2888. --- gui/qt/installwizard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py index 4e1b3f3a1..8b02e3d66 100644 --- a/gui/qt/installwizard.py +++ b/gui/qt/installwizard.py @@ -423,8 +423,12 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): self.confirm(message, title) def confirm(self, message, title): + area = QScrollArea() + label = WWLabel(message) + area.setWidget(label) + vbox = QVBoxLayout() - vbox.addWidget(WWLabel(message)) + vbox.addWidget(area) self.exec_layout(vbox, title) @wizard_dialog From 2fbc70d860da2921fbb7f1819aaaa21d72e8b2c6 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 21 Sep 2017 05:51:34 +0200 Subject: [PATCH 3/3] Moving to virtual tx size instead of total tx size. --- lib/tests/test_transaction.py | 19 +++++++++++++++++++ lib/transaction.py | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lib/tests/test_transaction.py b/lib/tests/test_transaction.py index 8a4d7e19e..84d72c149 100644 --- a/lib/tests/test_transaction.py +++ b/lib/tests/test_transaction.py @@ -18,6 +18,7 @@ from lib.util import bh2u unsigned_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000005701ff4c53ff0488b21e03ef2afea18000000089689bff23e1e7fb2f161daa37270a97a3d8c2e537584b2d304ecb47b86d21fc021b010d3bd425f8cf2e04824bfdf1f1f5ff1d51fadd9a41f9e3fb8dd3403b1bfe00000000ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000' signed_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000006c493046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d985012102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae35cdb84d2f6ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000' v2_blob = "0200000001191601a44a81e061502b7bfbc6eaa1cef6d1e6af5308ef96c9342f71dbf4b9b5000000006b483045022100a6d44d0a651790a477e75334adfb8aae94d6612d01187b2c02526e340a7fd6c8022028bdf7a64a54906b13b145cd5dab21a26bd4b85d6044e9b97bceab5be44c2a9201210253e8e0254b0c95776786e40984c1aa32a7d03efa6bdacdea5f421b774917d346feffffff026b20fa04000000001976a914024db2e87dd7cfd0e5f266c5f212e21a31d805a588aca0860100000000001976a91421919b94ae5cefcdf0271191459157cdb41c4cbf88aca6240700" +signed_segwit_blob = "01000000000101b66d722484f2db63e827ebf41d02684fed0c6550e85015a6c9d41ef216a8a6f00000000000fdffffff0280c3c90100000000160014b65ce60857f7e7892b983851c2a8e3526d09e4ab64bac30400000000160014c478ebbc0ab2097706a98e10db7cf101839931c4024730440220789c7d47f876638c58d98733c30ae9821c8fa82b470285dcdf6db5994210bf9f02204163418bbc44af701212ad42d884cc613f3d3d831d2d0cc886f767cca6e0235e012103083a6dc250816d771faa60737bfe78b23ad619f6b458e0a1f1688e3a0605e79c00000000" class TestBCDataStream(unittest.TestCase): @@ -137,6 +138,24 @@ class TestTransaction(unittest.TestCase): tx.update_signatures(signed_blob) + self.assertEqual(tx.estimated_total_size(), 193) + self.assertEqual(tx.estimated_base_size(), 193) + self.assertEqual(tx.estimated_witness_size(), 0) + self.assertEqual(tx.estimated_weight(), 772) + self.assertEqual(tx.estimated_virtual_size(), 193) + self.assertEqual(tx.estimated_size(), 193) + + # TODO other tests for segwit tx + def test_tx_signed_segwit(self): + tx = transaction.Transaction(signed_segwit_blob) + + self.assertEqual(tx.estimated_total_size(), 222) + self.assertEqual(tx.estimated_base_size(), 113) + self.assertEqual(tx.estimated_witness_size(), 109) + self.assertEqual(tx.estimated_weight(), 561) + self.assertEqual(tx.estimated_virtual_size(), 141) + self.assertEqual(tx.estimated_size(), 141) + def test_errors(self): with self.assertRaises(TypeError): transaction.Transaction.pay_script(output_type=None, addr='') diff --git a/lib/transaction.py b/lib/transaction.py index 6b0726c7f..baf9de4dd 100644 --- a/lib/transaction.py +++ b/lib/transaction.py @@ -825,8 +825,14 @@ class Transaction: @profiler def estimated_size(self): - '''Return an estimated tx size in bytes.''' - return len(self.serialize(True)) // 2 if not self.is_complete() or self.raw is None else len(self.raw) / 2 # ASCII hex string + """Return an estimated virtual tx size in vbytes. + BIP-0141 defines 'Virtual transaction size' to be weight/4 rounded up. + This definition is only for humans, and has little meaning otherwise. + If we wanted sub-byte precision, fee calculation should use transaction + weights, but for simplicity we approximate that with (virtual_size)x4 + """ + weight = self.estimated_weight() + return weight // 4 + (weight % 4 > 0) @classmethod def estimated_input_size(self, txin): @@ -834,6 +840,29 @@ class Transaction: script = self.input_script(txin, True) return len(self.serialize_input(txin, script)) // 2 + def estimated_total_size(self): + """Return an estimated total transaction size in bytes.""" + return len(self.serialize(True)) // 2 if not self.is_complete() or self.raw is None else len(self.raw) // 2 # ASCII hex string + + def estimated_witness_size(self): + """Return an estimate of witness size in bytes.""" + if not self.is_segwit(): + return 0 + inputs = self.inputs() + witness = ''.join(self.serialize_witness(x) for x in inputs) + witness_size = len(witness) // 2 + 2 # include marker and flag + return witness_size + + def estimated_base_size(self): + """Return an estimated base transaction size in bytes.""" + return self.estimated_total_size() - self.estimated_witness_size() + + def estimated_weight(self): + """Return an estimate of transaction weight.""" + total_tx_size = self.estimated_total_size() + base_tx_size = self.estimated_base_size() + return 3 * base_tx_size + total_tx_size + def signature_count(self): r = 0 s = 0