Browse Source

move opcodes to bitcoin.py

sqlite_db
SomberNight 6 years ago
parent
commit
e7f38467d7
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 179
      electrum/bitcoin.py
  2. 4
      electrum/gui/qt/paytoedit.py
  3. 4
      electrum/plugins/hw_wallet/plugin.py
  4. 30
      electrum/tests/test_bitcoin.py
  5. 154
      electrum/transaction.py

179
electrum/bitcoin.py

@ -25,6 +25,7 @@
import hashlib import hashlib
from typing import List, Tuple, TYPE_CHECKING, Optional, Union 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 .util import bfh, bh2u, BitcoinException, assert_bytes, to_bytes, inv_dict
from . import version from . import version
@ -49,6 +50,147 @@ TYPE_PUBKEY = 1
TYPE_SCRIPT = 2 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: def rev_hex(s: str) -> str:
return bh2u(bfh(s)[::-1]) return bh2u(bfh(s)[::-1])
@ -112,15 +254,15 @@ def witness_push(item: str) -> str:
return var_int(len(item) // 2) + item return var_int(len(item) // 2) + item
def op_push(i: int) -> str: def _op_push(i: int) -> str:
if i<0x4c: # OP_PUSHDATA1 if i < opcodes.OP_PUSHDATA1:
return int_to_hex(i) return int_to_hex(i)
elif i<=0xff: elif i <= 0xff:
return '4c' + int_to_hex(i) return opcodes.OP_PUSHDATA1.hex() + int_to_hex(i, 1)
elif i<=0xffff: elif i <= 0xffff:
return '4d' + int_to_hex(i,2) return opcodes.OP_PUSHDATA2.hex() + int_to_hex(i, 2)
else: else:
return '4e' + int_to_hex(i,4) return opcodes.OP_PUSHDATA4.hex() + int_to_hex(i, 4)
def push_script(data: str) -> str: 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 ported from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptbuilder.go#L128
""" """
data = bfh(data) data = bfh(data)
from .transaction import opcodes
data_len = len(data) data_len = len(data)
# "small integer" opcodes # "small integer" opcodes
if data_len == 0 or data_len == 1 and data[0] == 0: 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: elif data_len == 1 and data[0] <= 16:
return bh2u(bytes([opcodes.OP_1 - 1 + data[0]])) return bh2u(bytes([opcodes.OP_1 - 1 + data[0]]))
elif data_len == 1 and data[0] == 0x81: 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: 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 witprog is not None:
if not (0 <= witver <= 16): if not (0 <= witver <= 16):
raise BitcoinException(f'impossible witness version: {witver}') raise BitcoinException(f'impossible witness version: {witver}')
OP_n = witver + 0x50 if witver > 0 else 0 script = bh2u(add_number_to_script(witver))
script = bh2u(bytes([OP_n]))
script += push_script(bh2u(bytes(witprog))) script += push_script(bh2u(bytes(witprog)))
return script return script
addrtype, hash_160_ = b58_address_to_hash160(addr) addrtype, hash_160_ = b58_address_to_hash160(addr)
if addrtype == net.ADDRTYPE_P2PKH: 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 += 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: elif addrtype == net.ADDRTYPE_P2SH:
script = 'a9' # op_hash_160 script = opcodes.OP_HASH160.hex()
script += push_script(bh2u(hash_160_)) script += push_script(bh2u(hash_160_))
script += '87' # op_equal script += opcodes.OP_EQUAL.hex()
else: else:
raise BitcoinException(f'unknown address type: {addrtype}') raise BitcoinException(f'unknown address type: {addrtype}')
return script return script
@ -331,9 +470,7 @@ def script_to_scripthash(script: str) -> str:
return bh2u(bytes(reversed(h))) return bh2u(bytes(reversed(h)))
def public_key_to_p2pk_script(pubkey: str) -> str: def public_key_to_p2pk_script(pubkey: str) -> str:
script = push_script(pubkey) return push_script(pubkey) + opcodes.OP_CHECKSIG.hex()
script += 'ac' # op_checksig
return script
__b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' __b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
assert len(__b58chars) == 58 assert len(__b58chars) == 58

4
electrum/gui/qt/paytoedit.py

@ -30,7 +30,8 @@ from PyQt5.QtGui import QFontMetrics
from electrum import bitcoin from electrum import bitcoin
from electrum.util import bfh, PrintError 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 .qrtextedit import ScanQRTextEdit
from .completion_text_edit import CompletionTextEdit from .completion_text_edit import CompletionTextEdit
@ -91,7 +92,6 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, PrintError):
return bitcoin.TYPE_SCRIPT, script return bitcoin.TYPE_SCRIPT, script
def parse_script(self, x): def parse_script(self, x):
from electrum.transaction import opcodes, push_script
script = '' script = ''
for word in x.split(): for word in x.split():
if word[0:3] == 'OP_': if word[0:3] == 'OP_':

4
electrum/plugins/hw_wallet/plugin.py

@ -26,9 +26,9 @@
from electrum.plugin import BasePlugin, hook from electrum.plugin import BasePlugin, hook
from electrum.i18n import _ 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.util import bfh, versiontuple, UserFacingException
from electrum.transaction import opcodes, TxOutput, Transaction from electrum.transaction import TxOutput, Transaction
class HW_PluginBase(BasePlugin): class HW_PluginBase(BasePlugin):

30
electrum/tests/test_bitcoin.py

@ -3,18 +3,18 @@ import sys
from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key, from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
is_address, is_private_key, is_new_seed, is_old_seed, 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, deserialize_privkey, serialize_privkey, is_segwit_address,
is_b58_address, address_to_scripthash, is_minikey, is_b58_address, address_to_scripthash, is_minikey,
is_compressed_privkey, seed_type, EncodeBase58Check, 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, from electrum.bip32 import (bip32_root, bip32_public_derivation, bip32_private_derivation,
xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation, xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
is_xpub, convert_bip32_path_to_list_of_uint32) is_xpub, convert_bip32_path_to_list_of_uint32)
from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS
from electrum import ecc, crypto, constants from electrum import ecc, crypto, constants
from electrum.ecc import number_to_string, string_to_number from electrum.ecc import number_to_string, string_to_number
from electrum.transaction import opcodes
from electrum.util import bfh, bh2u, InvalidPassword from electrum.util import bfh, bh2u, InvalidPassword
from electrum.storage import WalletStorage from electrum.storage import WalletStorage
from electrum.keystore import xtype_from_derivation from electrum.keystore import xtype_from_derivation
@ -291,18 +291,18 @@ class Test_bitcoin(SequentialTestCase):
self.assertEqual(var_int(0x0123456789abcdef), "ffefcdab8967452301") self.assertEqual(var_int(0x0123456789abcdef), "ffefcdab8967452301")
def test_op_push(self): def test_op_push(self):
self.assertEqual(op_push(0x00), '00') self.assertEqual(_op_push(0x00), '00')
self.assertEqual(op_push(0x12), '12') self.assertEqual(_op_push(0x12), '12')
self.assertEqual(op_push(0x4b), '4b') self.assertEqual(_op_push(0x4b), '4b')
self.assertEqual(op_push(0x4c), '4c4c') self.assertEqual(_op_push(0x4c), '4c4c')
self.assertEqual(op_push(0xfe), '4cfe') self.assertEqual(_op_push(0xfe), '4cfe')
self.assertEqual(op_push(0xff), '4cff') self.assertEqual(_op_push(0xff), '4cff')
self.assertEqual(op_push(0x100), '4d0001') self.assertEqual(_op_push(0x100), '4d0001')
self.assertEqual(op_push(0x1234), '4d3412') self.assertEqual(_op_push(0x1234), '4d3412')
self.assertEqual(op_push(0xfffe), '4dfeff') self.assertEqual(_op_push(0xfffe), '4dfeff')
self.assertEqual(op_push(0xffff), '4dffff') self.assertEqual(_op_push(0xffff), '4dffff')
self.assertEqual(op_push(0x10000), '4e00000100') self.assertEqual(_op_push(0x10000), '4e00000100')
self.assertEqual(op_push(0x12345678), '4e78563412') self.assertEqual(_op_push(0x12345678), '4e78563412')
def test_script_num_to_hex(self): def test_script_num_to_hex(self):
# test vectors from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptnum.go#L77 # test vectors from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptnum.go#L77

154
electrum/transaction.py

@ -32,14 +32,14 @@ import traceback
import sys import sys
from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable, from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable,
Callable, List, Dict) Callable, List, Dict)
from enum import IntEnum
from . import ecc, bitcoin, constants, segwit_addr from . import ecc, bitcoin, constants, segwit_addr
from .util import print_error, profiler, to_bytes, bh2u, bfh from .util import print_error, profiler, to_bytes, bh2u, bfh
from .bitcoin import (TYPE_ADDRESS, TYPE_PUBKEY, TYPE_SCRIPT, hash_160, from .bitcoin import (TYPE_ADDRESS, TYPE_PUBKEY, TYPE_SCRIPT, hash_160,
hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr, hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr,
hash_encode, var_int, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC, COIN, 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 .crypto import sha256d
from .keystore import xpubkey_to_address, xpubkey_to_pubkey from .keystore import xpubkey_to_address, xpubkey_to_pubkey
@ -191,148 +191,6 @@ class BCDataStream(object):
self.write(s) 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): def script_GetOp(_bytes : bytes):
i = 0 i = 0
while i < len(_bytes): 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: def multisig_script(public_keys: Sequence[str], m: int) -> str:
n = len(public_keys) n = len(public_keys)
assert 1 <= m <= n <= 15, f'm {m}, n {n}' assert 1 <= m <= n <= 15, f'm {m}, n {n}'
op_m = bh2u(bytes([opcodes.OP_1 - 1 + m])) op_m = bh2u(add_number_to_script(m))
op_n = bh2u(bytes([opcodes.OP_1 - 1 + n])) op_n = bh2u(add_number_to_script(n))
keylist = [push_script(k) for k in public_keys] 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) scriptSig = bitcoin.p2wsh_nested_script(witness_script)
return push_script(scriptSig) return push_script(scriptSig)
elif _type == 'address': 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': elif _type == 'unknown':
return txin['scriptSig'] return txin['scriptSig']
return script return script

Loading…
Cancel
Save