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:
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