electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit e7f38467d7a592ab5288e29200cbace65e87834e
parent c03c17f1c7714a9a1db08ef91bf7bece9ef8dfe7
Author: SomberNight <somber.night@protonmail.com>
Date:   Fri, 22 Feb 2019 17:34:43 +0100

move opcodes to bitcoin.py

Diffstat:
Melectrum/bitcoin.py | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Melectrum/gui/qt/paytoedit.py | 4++--
Melectrum/plugins/hw_wallet/plugin.py | 4++--
Melectrum/tests/test_bitcoin.py | 30+++++++++++++++---------------
Melectrum/transaction.py | 154++++---------------------------------------------------------------------------
5 files changed, 183 insertions(+), 188 deletions(-)

diff --git a/electrum/bitcoin.py b/electrum/bitcoin.py @@ -25,6 +25,7 @@ import hashlib from typing import List, Tuple, TYPE_CHECKING, Optional, Union +from enum import IntEnum from .util import bfh, bh2u, BitcoinException, assert_bytes, to_bytes, inv_dict from . import version @@ -49,6 +50,147 @@ TYPE_PUBKEY = 1 TYPE_SCRIPT = 2 +class opcodes(IntEnum): + # push value + OP_0 = 0x00 + OP_FALSE = OP_0 + OP_PUSHDATA1 = 0x4c + OP_PUSHDATA2 = 0x4d + OP_PUSHDATA4 = 0x4e + OP_1NEGATE = 0x4f + OP_RESERVED = 0x50 + OP_1 = 0x51 + OP_TRUE = OP_1 + OP_2 = 0x52 + OP_3 = 0x53 + OP_4 = 0x54 + OP_5 = 0x55 + OP_6 = 0x56 + OP_7 = 0x57 + OP_8 = 0x58 + OP_9 = 0x59 + OP_10 = 0x5a + OP_11 = 0x5b + OP_12 = 0x5c + OP_13 = 0x5d + OP_14 = 0x5e + OP_15 = 0x5f + OP_16 = 0x60 + + # control + OP_NOP = 0x61 + OP_VER = 0x62 + OP_IF = 0x63 + OP_NOTIF = 0x64 + OP_VERIF = 0x65 + OP_VERNOTIF = 0x66 + OP_ELSE = 0x67 + OP_ENDIF = 0x68 + OP_VERIFY = 0x69 + OP_RETURN = 0x6a + + # stack ops + OP_TOALTSTACK = 0x6b + OP_FROMALTSTACK = 0x6c + OP_2DROP = 0x6d + OP_2DUP = 0x6e + OP_3DUP = 0x6f + OP_2OVER = 0x70 + OP_2ROT = 0x71 + OP_2SWAP = 0x72 + OP_IFDUP = 0x73 + OP_DEPTH = 0x74 + OP_DROP = 0x75 + OP_DUP = 0x76 + OP_NIP = 0x77 + OP_OVER = 0x78 + OP_PICK = 0x79 + OP_ROLL = 0x7a + OP_ROT = 0x7b + OP_SWAP = 0x7c + OP_TUCK = 0x7d + + # splice ops + OP_CAT = 0x7e + OP_SUBSTR = 0x7f + OP_LEFT = 0x80 + OP_RIGHT = 0x81 + OP_SIZE = 0x82 + + # bit logic + OP_INVERT = 0x83 + OP_AND = 0x84 + OP_OR = 0x85 + OP_XOR = 0x86 + OP_EQUAL = 0x87 + OP_EQUALVERIFY = 0x88 + OP_RESERVED1 = 0x89 + OP_RESERVED2 = 0x8a + + # numeric + OP_1ADD = 0x8b + OP_1SUB = 0x8c + OP_2MUL = 0x8d + OP_2DIV = 0x8e + OP_NEGATE = 0x8f + OP_ABS = 0x90 + OP_NOT = 0x91 + OP_0NOTEQUAL = 0x92 + + OP_ADD = 0x93 + OP_SUB = 0x94 + OP_MUL = 0x95 + OP_DIV = 0x96 + OP_MOD = 0x97 + OP_LSHIFT = 0x98 + OP_RSHIFT = 0x99 + + OP_BOOLAND = 0x9a + OP_BOOLOR = 0x9b + OP_NUMEQUAL = 0x9c + OP_NUMEQUALVERIFY = 0x9d + OP_NUMNOTEQUAL = 0x9e + OP_LESSTHAN = 0x9f + OP_GREATERTHAN = 0xa0 + OP_LESSTHANOREQUAL = 0xa1 + OP_GREATERTHANOREQUAL = 0xa2 + OP_MIN = 0xa3 + OP_MAX = 0xa4 + + OP_WITHIN = 0xa5 + + # crypto + OP_RIPEMD160 = 0xa6 + OP_SHA1 = 0xa7 + OP_SHA256 = 0xa8 + OP_HASH160 = 0xa9 + OP_HASH256 = 0xaa + OP_CODESEPARATOR = 0xab + OP_CHECKSIG = 0xac + OP_CHECKSIGVERIFY = 0xad + OP_CHECKMULTISIG = 0xae + OP_CHECKMULTISIGVERIFY = 0xaf + + # expansion + OP_NOP1 = 0xb0 + OP_CHECKLOCKTIMEVERIFY = 0xb1 + OP_NOP2 = OP_CHECKLOCKTIMEVERIFY + OP_CHECKSEQUENCEVERIFY = 0xb2 + OP_NOP3 = OP_CHECKSEQUENCEVERIFY + OP_NOP4 = 0xb3 + OP_NOP5 = 0xb4 + OP_NOP6 = 0xb5 + OP_NOP7 = 0xb6 + OP_NOP8 = 0xb7 + OP_NOP9 = 0xb8 + OP_NOP10 = 0xb9 + + OP_INVALIDOPCODE = 0xff + + def hex(self) -> str: + return bytes([self]).hex() + + def rev_hex(s: str) -> str: return bh2u(bfh(s)[::-1]) @@ -112,15 +254,15 @@ def witness_push(item: str) -> str: return var_int(len(item) // 2) + item -def op_push(i: int) -> str: - if i<0x4c: # OP_PUSHDATA1 +def _op_push(i: int) -> str: + if i < opcodes.OP_PUSHDATA1: return int_to_hex(i) - elif i<=0xff: - return '4c' + int_to_hex(i) - elif i<=0xffff: - return '4d' + int_to_hex(i,2) + elif i <= 0xff: + return opcodes.OP_PUSHDATA1.hex() + int_to_hex(i, 1) + elif i <= 0xffff: + return opcodes.OP_PUSHDATA2.hex() + int_to_hex(i, 2) else: - return '4e' + int_to_hex(i,4) + return opcodes.OP_PUSHDATA4.hex() + int_to_hex(i, 4) def push_script(data: str) -> str: @@ -131,19 +273,17 @@ def push_script(data: str) -> str: ported from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptbuilder.go#L128 """ data = bfh(data) - from .transaction import opcodes - data_len = len(data) # "small integer" opcodes if data_len == 0 or data_len == 1 and data[0] == 0: - return bh2u(bytes([opcodes.OP_0])) + return opcodes.OP_0.hex() elif data_len == 1 and data[0] <= 16: return bh2u(bytes([opcodes.OP_1 - 1 + data[0]])) elif data_len == 1 and data[0] == 0x81: - return bh2u(bytes([opcodes.OP_1NEGATE])) + return opcodes.OP_1NEGATE.hex() - return op_push(data_len) + bh2u(data) + return _op_push(data_len) + bh2u(data) def add_number_to_script(i: int) -> bytes: @@ -305,19 +445,18 @@ def address_to_script(addr: str, *, net=None) -> str: if witprog is not None: if not (0 <= witver <= 16): raise BitcoinException(f'impossible witness version: {witver}') - OP_n = witver + 0x50 if witver > 0 else 0 - script = bh2u(bytes([OP_n])) + script = bh2u(add_number_to_script(witver)) script += push_script(bh2u(bytes(witprog))) return script addrtype, hash_160_ = b58_address_to_hash160(addr) if addrtype == net.ADDRTYPE_P2PKH: - script = '76a9' # op_dup, op_hash_160 + script = bytes([opcodes.OP_DUP, opcodes.OP_HASH160]).hex() script += push_script(bh2u(hash_160_)) - script += '88ac' # op_equalverify, op_checksig + script += bytes([opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG]).hex() elif addrtype == net.ADDRTYPE_P2SH: - script = 'a9' # op_hash_160 + script = opcodes.OP_HASH160.hex() script += push_script(bh2u(hash_160_)) - script += '87' # op_equal + script += opcodes.OP_EQUAL.hex() else: raise BitcoinException(f'unknown address type: {addrtype}') return script @@ -331,9 +470,7 @@ def script_to_scripthash(script: str) -> str: return bh2u(bytes(reversed(h))) def public_key_to_p2pk_script(pubkey: str) -> str: - script = push_script(pubkey) - script += 'ac' # op_checksig - return script + return push_script(pubkey) + opcodes.OP_CHECKSIG.hex() __b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' assert len(__b58chars) == 58 diff --git a/electrum/gui/qt/paytoedit.py b/electrum/gui/qt/paytoedit.py @@ -30,7 +30,8 @@ from PyQt5.QtGui import QFontMetrics from electrum import bitcoin from electrum.util import bfh, PrintError -from electrum.transaction import TxOutput +from electrum.transaction import TxOutput, push_script +from electrum.bitcoin import opcodes from .qrtextedit import ScanQRTextEdit from .completion_text_edit import CompletionTextEdit @@ -91,7 +92,6 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, PrintError): return bitcoin.TYPE_SCRIPT, script def parse_script(self, x): - from electrum.transaction import opcodes, push_script script = '' for word in x.split(): if word[0:3] == 'OP_': diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py @@ -26,9 +26,9 @@ from electrum.plugin import BasePlugin, hook from electrum.i18n import _ -from electrum.bitcoin import is_address, TYPE_SCRIPT +from electrum.bitcoin import is_address, TYPE_SCRIPT, opcodes from electrum.util import bfh, versiontuple, UserFacingException -from electrum.transaction import opcodes, TxOutput, Transaction +from electrum.transaction import TxOutput, Transaction class HW_PluginBase(BasePlugin): diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py @@ -3,18 +3,18 @@ import sys from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key, is_address, is_private_key, is_new_seed, is_old_seed, - var_int, op_push, address_to_script, + var_int, _op_push, address_to_script, deserialize_privkey, serialize_privkey, is_segwit_address, is_b58_address, address_to_scripthash, is_minikey, is_compressed_privkey, seed_type, EncodeBase58Check, - script_num_to_hex, push_script, add_number_to_script, int_to_hex) + script_num_to_hex, push_script, add_number_to_script, int_to_hex, + opcodes) from electrum.bip32 import (bip32_root, bip32_public_derivation, bip32_private_derivation, xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation, is_xpub, convert_bip32_path_to_list_of_uint32) from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS from electrum import ecc, crypto, constants from electrum.ecc import number_to_string, string_to_number -from electrum.transaction import opcodes from electrum.util import bfh, bh2u, InvalidPassword from electrum.storage import WalletStorage from electrum.keystore import xtype_from_derivation @@ -291,18 +291,18 @@ class Test_bitcoin(SequentialTestCase): self.assertEqual(var_int(0x0123456789abcdef), "ffefcdab8967452301") def test_op_push(self): - self.assertEqual(op_push(0x00), '00') - self.assertEqual(op_push(0x12), '12') - self.assertEqual(op_push(0x4b), '4b') - self.assertEqual(op_push(0x4c), '4c4c') - self.assertEqual(op_push(0xfe), '4cfe') - self.assertEqual(op_push(0xff), '4cff') - self.assertEqual(op_push(0x100), '4d0001') - self.assertEqual(op_push(0x1234), '4d3412') - self.assertEqual(op_push(0xfffe), '4dfeff') - self.assertEqual(op_push(0xffff), '4dffff') - self.assertEqual(op_push(0x10000), '4e00000100') - self.assertEqual(op_push(0x12345678), '4e78563412') + self.assertEqual(_op_push(0x00), '00') + self.assertEqual(_op_push(0x12), '12') + self.assertEqual(_op_push(0x4b), '4b') + self.assertEqual(_op_push(0x4c), '4c4c') + self.assertEqual(_op_push(0xfe), '4cfe') + self.assertEqual(_op_push(0xff), '4cff') + self.assertEqual(_op_push(0x100), '4d0001') + self.assertEqual(_op_push(0x1234), '4d3412') + self.assertEqual(_op_push(0xfffe), '4dfeff') + self.assertEqual(_op_push(0xffff), '4dffff') + self.assertEqual(_op_push(0x10000), '4e00000100') + self.assertEqual(_op_push(0x12345678), '4e78563412') def test_script_num_to_hex(self): # test vectors from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptnum.go#L77 diff --git a/electrum/transaction.py b/electrum/transaction.py @@ -32,14 +32,14 @@ import traceback import sys from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable, Callable, List, Dict) -from enum import IntEnum from . import ecc, bitcoin, constants, segwit_addr from .util import print_error, profiler, to_bytes, bh2u, bfh from .bitcoin import (TYPE_ADDRESS, TYPE_PUBKEY, TYPE_SCRIPT, hash_160, hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr, hash_encode, var_int, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC, COIN, - push_script, int_to_hex, push_script, b58_address_to_hash160) + push_script, int_to_hex, push_script, b58_address_to_hash160, + opcodes, add_number_to_script) from .crypto import sha256d from .keystore import xpubkey_to_address, xpubkey_to_pubkey @@ -191,148 +191,6 @@ class BCDataStream(object): self.write(s) -class opcodes(IntEnum): - # push value - OP_0 = 0x00 - OP_FALSE = OP_0 - OP_PUSHDATA1 = 0x4c - OP_PUSHDATA2 = 0x4d - OP_PUSHDATA4 = 0x4e - OP_1NEGATE = 0x4f - OP_RESERVED = 0x50 - OP_1 = 0x51 - OP_TRUE = OP_1 - OP_2 = 0x52 - OP_3 = 0x53 - OP_4 = 0x54 - OP_5 = 0x55 - OP_6 = 0x56 - OP_7 = 0x57 - OP_8 = 0x58 - OP_9 = 0x59 - OP_10 = 0x5a - OP_11 = 0x5b - OP_12 = 0x5c - OP_13 = 0x5d - OP_14 = 0x5e - OP_15 = 0x5f - OP_16 = 0x60 - - # control - OP_NOP = 0x61 - OP_VER = 0x62 - OP_IF = 0x63 - OP_NOTIF = 0x64 - OP_VERIF = 0x65 - OP_VERNOTIF = 0x66 - OP_ELSE = 0x67 - OP_ENDIF = 0x68 - OP_VERIFY = 0x69 - OP_RETURN = 0x6a - - # stack ops - OP_TOALTSTACK = 0x6b - OP_FROMALTSTACK = 0x6c - OP_2DROP = 0x6d - OP_2DUP = 0x6e - OP_3DUP = 0x6f - OP_2OVER = 0x70 - OP_2ROT = 0x71 - OP_2SWAP = 0x72 - OP_IFDUP = 0x73 - OP_DEPTH = 0x74 - OP_DROP = 0x75 - OP_DUP = 0x76 - OP_NIP = 0x77 - OP_OVER = 0x78 - OP_PICK = 0x79 - OP_ROLL = 0x7a - OP_ROT = 0x7b - OP_SWAP = 0x7c - OP_TUCK = 0x7d - - # splice ops - OP_CAT = 0x7e - OP_SUBSTR = 0x7f - OP_LEFT = 0x80 - OP_RIGHT = 0x81 - OP_SIZE = 0x82 - - # bit logic - OP_INVERT = 0x83 - OP_AND = 0x84 - OP_OR = 0x85 - OP_XOR = 0x86 - OP_EQUAL = 0x87 - OP_EQUALVERIFY = 0x88 - OP_RESERVED1 = 0x89 - OP_RESERVED2 = 0x8a - - # numeric - OP_1ADD = 0x8b - OP_1SUB = 0x8c - OP_2MUL = 0x8d - OP_2DIV = 0x8e - OP_NEGATE = 0x8f - OP_ABS = 0x90 - OP_NOT = 0x91 - OP_0NOTEQUAL = 0x92 - - OP_ADD = 0x93 - OP_SUB = 0x94 - OP_MUL = 0x95 - OP_DIV = 0x96 - OP_MOD = 0x97 - OP_LSHIFT = 0x98 - OP_RSHIFT = 0x99 - - OP_BOOLAND = 0x9a - OP_BOOLOR = 0x9b - OP_NUMEQUAL = 0x9c - OP_NUMEQUALVERIFY = 0x9d - OP_NUMNOTEQUAL = 0x9e - OP_LESSTHAN = 0x9f - OP_GREATERTHAN = 0xa0 - OP_LESSTHANOREQUAL = 0xa1 - OP_GREATERTHANOREQUAL = 0xa2 - OP_MIN = 0xa3 - OP_MAX = 0xa4 - - OP_WITHIN = 0xa5 - - # crypto - OP_RIPEMD160 = 0xa6 - OP_SHA1 = 0xa7 - OP_SHA256 = 0xa8 - OP_HASH160 = 0xa9 - OP_HASH256 = 0xaa - OP_CODESEPARATOR = 0xab - OP_CHECKSIG = 0xac - OP_CHECKSIGVERIFY = 0xad - OP_CHECKMULTISIG = 0xae - OP_CHECKMULTISIGVERIFY = 0xaf - - # expansion - OP_NOP1 = 0xb0 - OP_CHECKLOCKTIMEVERIFY = 0xb1 - OP_NOP2 = OP_CHECKLOCKTIMEVERIFY - OP_CHECKSEQUENCEVERIFY = 0xb2 - OP_NOP3 = OP_CHECKSEQUENCEVERIFY - OP_NOP4 = 0xb3 - OP_NOP5 = 0xb4 - OP_NOP6 = 0xb5 - OP_NOP7 = 0xb6 - OP_NOP8 = 0xb7 - OP_NOP9 = 0xb8 - OP_NOP10 = 0xb9 - - OP_INVALIDOPCODE = 0xff - - def hex(self) -> str: - return bytes([self]).hex() - - - def script_GetOp(_bytes : bytes): i = 0 while i < len(_bytes): @@ -714,10 +572,10 @@ def deserialize(raw: str, force_full_parse=False) -> dict: def multisig_script(public_keys: Sequence[str], m: int) -> str: n = len(public_keys) assert 1 <= m <= n <= 15, f'm {m}, n {n}' - op_m = bh2u(bytes([opcodes.OP_1 - 1 + m])) - op_n = bh2u(bytes([opcodes.OP_1 - 1 + n])) + op_m = bh2u(add_number_to_script(m)) + op_n = bh2u(add_number_to_script(n)) keylist = [push_script(k) for k in public_keys] - return op_m + ''.join(keylist) + op_n + 'ae' + return op_m + ''.join(keylist) + op_n + opcodes.OP_CHECKMULTISIG.hex() @@ -1024,7 +882,7 @@ class Transaction: scriptSig = bitcoin.p2wsh_nested_script(witness_script) return push_script(scriptSig) elif _type == 'address': - return 'ff00' + push_script(pubkeys[0]) # fd extended pubkey + return bytes([opcodes.OP_INVALIDOPCODE, opcodes.OP_0]).hex() + push_script(pubkeys[0]) elif _type == 'unknown': return txin['scriptSig'] return script