From a4050459d94cb2e7962cd440cc6ae4df5383cd03 Mon Sep 17 00:00:00 2001 From: 4tochka Date: Mon, 28 May 2018 13:59:35 +0400 Subject: [PATCH] version 2.0 draft --- pybtc/address.py | 106 ++++++ pybtc/block.py | 0 pybtc/blockchain.py | 325 ++--------------- pybtc/constants.py | 1 + pybtc/encode.py | 2 - pybtc/hash.py | 1 - pybtc/opcodes.py | 297 +++++++-------- pybtc/tools.py | 401 ++++++++++++++------ pybtc/transaction.py | 225 ++++++++++++ tests/test/__init__.py | 14 +- tests/test/address_class.py | 59 +++ tests/test/address_functions.py | 505 +++++++++++++++++++++++--- tests/test/block.py | 5 + tests/test/create_transaction.py | 6 +- tests/test/ecdsa.py | 8 +- tests/test/hash_functions.py | 4 + tests/test/integer.py | 115 ++++++ tests/test/raw_block.txt | 1 + tests/test/script_deserialize.py | 10 +- tests/test/sighash.py | 6 +- tests/test/transaction_deserialize.py | 62 +++- 21 files changed, 1526 insertions(+), 627 deletions(-) create mode 100644 pybtc/address.py create mode 100644 pybtc/block.py create mode 100644 pybtc/transaction.py create mode 100644 tests/test/address_class.py create mode 100644 tests/test/integer.py create mode 100644 tests/test/raw_block.txt diff --git a/pybtc/address.py b/pybtc/address.py new file mode 100644 index 0000000..0828877 --- /dev/null +++ b/pybtc/address.py @@ -0,0 +1,106 @@ +from .tools import * + +class PrivateKey(): + def __init__(self, key=None, compressed=True, testnet=False): + if key is None: + self.compressed = compressed + self.testnet = testnet + self.raw_key = create_private_key() + else: + if type(key) == str: + try: + key = unhexlify(key) + except: + pass + if type(key) == bytes: + assert len(key) == 32 + self.raw_key = key + self.compressed = compressed + self.testnet = testnet + return + assert type(key) == str + self.raw_key = WIF_to_private_key(key) + if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, + TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): + self.compressed = False + else: + self.compressed = True + if key[0] in (TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, + TESTNET_PRIVATE_KEY_COMPRESSED_PREFIX): + self.testnet = True + else: + self.testnet = False + + def hex(self): + return hexlify(self.raw_key).decode() + + def WIF(self, compressed=None, testnet=None): + if compressed is None: + compressed = self.compressed + if testnet is None: + testnet = self.testnet + return private_key_to_WIF(self.raw_key, compressed, testnet) + + +class PublicKey(): + def __init__(self, key=None): + if type(key) == str: + try: + key = unhexlify(key) + except: + pass + if type(key) == PrivateKey: + key = private_to_public_key(key.raw_key, + compressed=key.compressed) + assert type(key) == bytes + assert len(key) == 33 or len(key) == 65 + if len(key) == 33: + self.compressed = True + else: + self.compressed = False + self.raw_key = key + + + def hex(self): + return hexlify(self.raw_key).decode() + + + +class Address(): + def __init__(self, key = None, + address_type="P2WPKH", testnet=False, compressed = True): + if key is None: + self.private_key = PrivateKey(testnet = testnet, + compressed = compressed) + self.public_key = PublicKey(self.private_key) + elif type(key) == PrivateKey: + self.private_key = key + testnet = key.testnet + compressed = key.compressed + self.public_key = PublicKey(self.private_key) + elif type(key) == PublicKey: + self.public_key = key + testnet = testnet + compressed = key.compressed + assert address_type in ("P2PKH", "PUBKEY", "P2WPKH", "P2SH_P2WPKH") + if not compressed: + assert address_type in ("P2PKH", "PUBKEY") + self.type = address_type + self.testnet = testnet + if address_type in ("P2WPKH"): + self.witness_version = 0 + else: + self.witness_version = None + self.compressed = compressed + + if address_type == "P2SH_P2WPKH": + self.script_hash = True + self.redeem_script = public_key_to_P2SH_P2WPKH_script(self.public_key.raw_key) + self.redeem_script_hex = hexlify(self.redeem_script).decode() + self.hash = hash160(self.redeem_script) + else: + self.script_hash = False + self.hash = hash160(self.public_key.raw_key) + self.address = hash_to_address(self.hash, + script_hash = self.script_hash, + witness_version = self.witness_version) diff --git a/pybtc/block.py b/pybtc/block.py new file mode 100644 index 0000000..e69de29 diff --git a/pybtc/blockchain.py b/pybtc/blockchain.py index 3d59474..7a49756 100644 --- a/pybtc/blockchain.py +++ b/pybtc/blockchain.py @@ -6,259 +6,10 @@ from .tools import * from .consensus import * from binascii import hexlify, unhexlify -def get_stream(stream): - if type(stream) != io.BytesIO: - if type(stream) == str: - stream = unhexlify(stream) - if type(stream) == bytes: - stream = io.BytesIO(stream) - else: - raise TypeError - return stream - -class Opcode(): - """ Class opcode """ - def __init__(self, raw_opcode, data, data_length = b""): - self.raw = raw_opcode - if self.raw in RAW_OPCODE: - if self.raw in (OPCODE["OP_PUSHDATA1"], OPCODE["OP_PUSHDATA2"], OPCODE["OP_PUSHDATA4"]): - self.str = '<%s>' % len(data) - else: - self.str = RAW_OPCODE[self.raw] - elif self.raw < b'L': - self.str = '<%s>' % len(data) - else: - self.str = '[?]' - self.data = data - self.data_length = data_length - - def __str__(self): - return self.str - - @classmethod - def to_raw(cls, name): - if name in OPCODE: - return OPCODE[name] - else: - return b'' - - @classmethod - def pop_from_stream (cls, stream): - b = stream.read(1) - if not b: return None - data = b'' - data_length = b'' - if b <= OPCODE["OP_PUSHDATA4"]: - if b < OPCODE["OP_PUSHDATA1"]: s = int.from_bytes(b,'little') - elif b == OPCODE["OP_PUSHDATA1"]: - data_length = stream.read(1) - s = int.from_bytes( data_length ,'little') - elif b == OPCODE["OP_PUSHDATA2"]: - data_length = stream.read(2) - s = int.from_bytes( data_length ,'little') - elif b == OPCODE["OP_PUSHDATA4"]: - data_length = stream.read(4) - s = int.from_bytes( data_length ,'little') - data = stream.read(s) - if len(data)!=s: - return None - raise Exception('opcode read error') - return cls(b,data,data_length) +k = 0 -class Script(): - """ - Bitcoin script class - """ - def __init__(self, raw_script, coinbase = False, segwit = True): - if type(raw_script) == str: - raw_script = unhexlify(raw_script) - self.raw = raw_script - stream = io.BytesIO(raw_script) - self.script = [] - self.address = list() - self.pattern = bytearray() - self.asm = bytearray() - self.data = b'' - self.type = "NON_STANDARD" - self.ntype = 7 - self.witness_version = None - self.op_sig_count = 0 - if coinbase: - self.pattern = b"" - self.asm = hexlify(raw_script).decode() - return - t = time.time() - while True: - o = Opcode.pop_from_stream(stream) - if o is None: - break - if o.raw == OPCODE["OP_CHECKSIG"] or o.raw == OPCODE["OP_CHECKSIGVERIFY"]: - self.op_sig_count += 1 - if o.raw ==OPCODE["OP_CHECKMULTISIG"]: - self.op_sig_count += 20 - self.script.append(o) - self.pattern += o.str.encode() + b' ' - if o.data: - self.asm += hexlify(o.data) + b' ' - else: - self.asm += o.str.encode() + b' ' - self.asm = self.asm.decode().rstrip() - self.pattern= self.pattern.decode().rstrip() - # check script type - if self.pattern == "OP_DUP OP_HASH160 <20> OP_EQUALVERIFY OP_CHECKSIG": - self.type = "P2PKH" - self.ntype = 0 - self.address.append(self.script[2].data) - elif self.pattern == "OP_HASH160 <20> OP_EQUAL": - self.type = "P2SH" - self.ntype = 1 - self.address.append(self.script[1].data) - elif self.pattern == "<65> OP_CHECKSIG" or self.pattern == "<33> OP_CHECKSIG" : - self.type = "PUBKEY" - self.ntype = 2 - self.address.append(hash160(self.script[0].data)) - elif len(self.script) == 2 and self.script[0].raw == OPCODE["OP_RETURN"]: - # OP_RETURN - if len(self.script[1].data) < NULL_DATA_LIMIT: # <0 to 80 bytes of data> - self.data = self.script[1].data - self.type = "NULL_DATA" - self.ntype = 3 - elif len(self.script)>= 4: - if self.script[-1].raw == OPCODE["OP_CHECKMULTISIG"] \ - and self.script[-2].raw <= OPCODE["OP_15"] \ - and self.script[-2].raw >= OPCODE["OP_1"] : # OP_CHECKMULTISIG "OP_1" "OP_16" - if self.script[0].raw <= OPCODE["OP_15"] \ - and self.script[0].raw >= OPCODE["OP_1"]: - self.op_sig_count = 0 - for o in self.script[1:-2]: - if not o.data: - self.op_sig_count = 20 - break - self.op_sig_count += 1 - self.address.append(hash160(o.data)) - else: - self.bare_multisig_accepted = ord(self.script[0].raw) - 80 - self.bare_multisig_from = ord(self.script[-2].raw) - 80 - self.type = "MULTISIG" - self.ntype = 4 - - elif segwit: - if self.pattern == "OP_0 <20>": - self.type = "P2WPKH" - self.op_sig_count = 1 - self.ntype = 5 - self.witness_version = 0 - self.address.append(self.script[1].data) - elif self.pattern == "OP_0 <32>": - self.type = "P2WSH" - self.ntype = 6 - self.witness_version = 0 - self.address.append(self.script[1].data) - - - -class Input: - """ Transaction Input class """ - # outpoint = (b'00f0f09...',n') - # script = raw bytes - # sequense = int - def __init__(self, outpoint, script, sequence, amount = None, private_key = None): - if type(outpoint[0]) == str: - outpoint = (unhexlify(outpoint[0])[::-1], outpoint[1]) - if type(outpoint[0]) == str: - private_key = WIF2priv(private_key) - self.outpoint = outpoint - self.sequence = sequence - self.pk_script = None - self.amount = amount - self.private_key = private_key - self.p2sh_type = None - self.coinbase = False - if outpoint == (b'\x00'*32 ,0xffffffff): self.coinbase = True - self.sig_script = Script(script, self.coinbase) - self.double_spend = None - self.lock = False - self.addresses = [] - self.redeem_script = None - if len(self.sig_script.script) > 0: - try: - if len(self.sig_script.script[-1].data) <= 520: - self.redeem_script = Script(self.sig_script.script[-1].data) - else: - pass - except Exception as err: - pass - - @classmethod - def deserialize(cls, stream): - stream = get_stream(stream) - outpoint = stream.read(32), int.from_bytes(stream.read(4), 'little') - script_len = from_var_int(read_var_int(stream)) - script = stream.read(script_len) - sequence = int.from_bytes(stream.read(4), 'little') - return cls(outpoint, script, sequence) - - def serialize(self): - return self.outpoint[0] + self.outpoint[1].to_bytes(4, 'little') \ - + to_var_int(len(self.sig_script.raw)) + self.sig_script.raw \ - + self.sequence.to_bytes(4, 'little') - - - -class Output: - """ Transactin output class """ - def __init__(self, value, script): - self.value = value - self.pk_script = Script(script) - - def serialize(self): - return self.value.to_bytes(8,'little') \ - + to_var_int(len(self.pk_script.raw)) + self.pk_script.raw - - - @classmethod - def deserialize(cls, stream): - stream = get_stream(stream) - value = int.from_bytes(stream.read(8), 'little') - script_len = from_var_int(read_var_int(stream)) - pk_script = stream.read(script_len) - return cls(value, pk_script) - -class Witness: - def __init__(self, data, empty = False): - self.empty = empty - self.witness = [b"\x00"] if empty else data - - def __str__(self): - return json.dumps([hexlify(w).decode() for w in self.witness]) - - def hex(self): - return [hexlify(w).decode() for w in self.witness] - - @classmethod - def deserialize(cls, stream): - stream = get_stream(stream) - empty = True - witness_len = from_var_int(read_var_int(stream)) - witness = [] - if witness_len: - for i in range(witness_len): - l = from_var_int(read_var_int(stream)) - w = stream.read(l) - witness.append(w) - empty = False - return cls(witness, empty) - - def serialize(self): - if self.empty: - return b'\x00' - - n = to_var_int(len(self.witness)) - for w in self.witness: - n += to_var_int(len(w)) + w - return n class Transaction(): @@ -306,7 +57,7 @@ class Transaction(): if not self.tx_in: self.witness = list() if witness is None: - self.witness = [Witness.deserialize(b"\x00") for i in range(len(tx_in))] + self.witness = (Witness.deserialize(b"\x00") for i in range(len(tx_in))) if hash is None : self.recalculate_txid() @@ -376,7 +127,7 @@ class Transaction(): if type(p2wpkh_address)==str: assert address_type(p2wpkh_address) == 'P2WPKH' witness_version = get_witness_version(p2wpkh_address) - p2wpkh_address = address2hash(p2wpkh_address) + p2wpkh_address = address_to_hash(p2wpkh_address) assert len(p2wpkh_address) == 20 self.tx_out.append(Output(amount, bytes([witness_version]) + b'\x14' + p2wpkh_address)) @@ -387,7 +138,7 @@ class Transaction(): if type(p2wsh_address)==str: assert address_type(p2wsh_address) == 'P2WSH' witness_version = get_witness_version(p2wsh_address) - p2wsh_address = address2hash(p2wsh_address) + p2wsh_address = address_to_hash(p2wsh_address) assert len(p2wsh_address) == 32 self.tx_out.append(Output(amount, bytes([witness_version]) + b'\x20' + p2wsh_address)) @@ -427,9 +178,9 @@ class Transaction(): def serialize(self, segwit = True, hex = False): version = self.version.to_bytes(4,'little') - ninputs = to_var_int(self.tx_in_count) + ninputs = int_to_var_int(self.tx_in_count) inputs = [i.serialize() for i in self.tx_in] - nouts = to_var_int(len(self.tx_out)) + nouts = int_to_var_int(len(self.tx_out)) outputs = [o.serialize() for o in self.tx_out] marke_flag = b"\x00\x01" if segwit else b"" witness = b"" @@ -445,7 +196,7 @@ class Transaction(): def sign_P2SHP2WPKH_input(self, sighash_type, input_index, private_key = None, amount = None): if type(private_key) == str: - private_key = WIF2priv(private_key) + private_key = WIF_to_private_key(private_key) if amount is not None: self.tx_in[input_index].amount = amount else: @@ -454,7 +205,7 @@ class Transaction(): self.tx_in[input_index].private_key = private_key else: private_key = self.tx_in[input_index].private_key - pubkey = priv2pub(private_key, True) + pubkey = private_to_public_key(private_key, True) pubkey_hash160 = hash160(pubkey) scriptCode = b"\x19" + OPCODE["OP_DUP"] + OPCODE["OP_HASH160"] scriptCode += b'\x14' + pubkey_hash160 + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"] @@ -469,7 +220,7 @@ class Transaction(): self.tx_in[input_index].private_key = private_key else: private_key = self.tx_in[input_index].private_key - pubkey = priv2pub(private_key, compressed) + pubkey = private_to_public_key(private_key, compressed) pubkey_hash160 = hash160(pubkey) scriptCode = OPCODE["OP_DUP"] + OPCODE["OP_HASH160"] + b'\x14' + \ pubkey_hash160 + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"] @@ -489,24 +240,24 @@ class Transaction(): if ((sighash_type&31) == SIGHASH_SINGLE) and (input_index>(len(self.tx_out)-1)): return double_sha256(b'\x01'+b'\x00'*31 + sighash_type.to_bytes(4, 'little')) preimage += self.version.to_bytes(4,'little') - preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else to_var_int(self.tx_in_count) + preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(self.tx_in_count) for number, i in enumerate(self.tx_in): if (sighash_type & SIGHASH_ANYONECANPAY) and (input_index != number): continue input = i.outpoint[0]+i.outpoint[1].to_bytes(4,'little') if sighash_type == 0 or input_index == number: - input += ((to_var_int(len(scriptCode)) + scriptCode) if sighash_type else \ - (to_var_int(len(i.sig_script.raw)) + i.sig_script.raw)) + i.sequence.to_bytes(4,'little') + input += ((int_to_var_int(len(scriptCode)) + scriptCode) if sighash_type else \ + (int_to_var_int(len(i.sig_script.raw)) + i.sig_script.raw)) + i.sequence.to_bytes(4, 'little') else: input += b'\x00' + (i.sequence.to_bytes(4,'little') if \ ((sighash_type&31) == SIGHASH_ALL) else b'\x00\x00\x00\x00') preimage += input - preimage += b'\x00' if (sighash_type&31) == SIGHASH_NONE else ( to_var_int(input_index + 1) if \ - (sighash_type&31) == SIGHASH_SINGLE else to_var_int(self.tx_out_count)) + preimage += b'\x00' if (sighash_type&31) == SIGHASH_NONE else (int_to_var_int(input_index + 1) if \ + (sighash_type&31) == SIGHASH_SINGLE else int_to_var_int(self.tx_out_count)) if (sighash_type&31) != SIGHASH_NONE: for number, i in enumerate(self.tx_out): if number > input_index and (sighash_type&31) == SIGHASH_SINGLE: continue preimage +=(b'\xff'*8+b'\x00' if (sighash_type&31) == SIGHASH_SINGLE and (input_index != number)\ - else i.value.to_bytes(8,'little')+to_var_int(len(i.pk_script.raw))+i.pk_script.raw) + else i.value.to_bytes(8,'little') + int_to_var_int(len(i.pk_script.raw)) + i.pk_script.raw) preimage += self.lock_time.to_bytes(4,'little') preimage += sighash_type.to_bytes(4, 'little') return double_sha256(preimage) if not hex else hexlify(double_sha256(preimage)).decode() @@ -543,10 +294,10 @@ class Transaction(): ho = bytearray() for n, o in enumerate(self.tx_out): if (sighash_type&31) != SIGHASH_SINGLE and (sighash_type&31) != SIGHASH_NONE: - ho += o.value.to_bytes(8,'little')+to_var_int(len(o.pk_script.raw))+o.pk_script.raw + ho += o.value.to_bytes(8,'little') + int_to_var_int(len(o.pk_script.raw)) + o.pk_script.raw elif (sighash_type&31) == SIGHASH_SINGLE and input_index < len(self.tx_out): if input_index == n: - ho += o.value.to_bytes(8, 'little') + to_var_int(len(o.pk_script.raw)) + o.pk_script.raw + ho += o.value.to_bytes(8, 'little') + int_to_var_int(len(o.pk_script.raw)) + o.pk_script.raw hashOutputs = double_sha256(ho) if ho else b'\x00'*32 preimage += hashPrevouts + hashSequence + outpoint + scriptCode + value + nSequence + hashOutputs preimage += self.lock_time.to_bytes(4, 'little') @@ -589,10 +340,10 @@ class Transaction(): if o.pk_script.ntype in (1,6): sh =True for a in o.pk_script.address: - out["address"].append(hash2address(a, - testnet=testnet, - script_hash= sh, - witness_version=o.pk_script.witness_version)) + out["address"].append(hash_to_address(a, + testnet=testnet, + script_hash= sh, + witness_version=o.pk_script.witness_version)) r["vout"].append(out) @@ -662,6 +413,7 @@ class Transaction(): class Block(): def __init__(self, version, prev_block, merkle_root, timestamp, bits, nonce, txs, block_size, hash = None, header = None): + qt = time.time() self.hash = hash self.header = header self.version = version @@ -671,6 +423,7 @@ class Block(): self.timestamp = timestamp self.bits = bits self.nonce = nonce + self.transactions = txs self.tx_hash_list = list() self.size = block_size @@ -679,13 +432,12 @@ class Block(): self.amount = 0 self.fee = 0 self.sigop = 0 - for t in txs: - if t.hash in self.tx_hash_list: - raise Exception("CVE-2012-2459") # merkle tree malleability - self.tx_hash_list.append(t.hash) + + self.target = None self.fee = 0 self.witness_root_hash = None + if txs: if txs[0].coinbase: if self.nversion > 1: @@ -700,6 +452,7 @@ class Block(): self.witness_root_hash = out.pk_script.data[4:36] except: pass + print("t ", time.time() - qt) def calculate_commitment(self, witness = None): wtxid_list = [b"\x00" * 32,] @@ -723,7 +476,7 @@ class Block(): commitment = self.calculate_commitment(tx.witness[0].witness[0]) for o in outputs: if type(o[1]) == str: - tx.tx_out.append(Output(o[0], address2script(o[1]))) + tx.tx_out.append(Output(o[0], address_to_script(o[1]))) else: tx.tx_out.append(Output(o[0], o[1])) tx.tx_out.append(Output(0, b'j$\xaa!\xa9\xed' + commitment)) @@ -762,11 +515,14 @@ class Block(): 'merkle_root': stream.read(32), 'timestamp': int.from_bytes(stream.read(4), 'little'), 'bits': stream.read(4), - 'nonce': stream.read(4), - 'txs': read_var_list(stream, Transaction), - 'block_size': stream.tell(), - 'header': header - } + 'nonce': stream.read(4)} + t = time.time() + kwargs['txs'] = read_var_list(stream, Transaction) + print("tx ",time.time() - t) + kwargs['block_size'] = stream.tell() + kwargs['header'] = header + global k + print(">.>.>.",k) return cls(**kwargs) def serialize(self, hex = False): @@ -776,13 +532,12 @@ class Block(): self.timestamp.to_bytes(4,'little') + \ self.bits + \ self.nonce + \ - to_var_int(len (self.transactions)) + int_to_var_int(len (self.transactions)) for t in self.transactions: if t.hash == t.whash: block += t.serialize(segwit = 0) else: block += t.serialize(segwit = 1) - if hex: return hexlify(block).decode() else: @@ -820,8 +575,8 @@ class BlockTemplate(): self.scan_tx_list() self.coinbase_tx = self.create_coinbase_transaction() self.coinb1, self.coinb2 = self.split_coinbase() - self.target = bits2target(self.bits) - self.difficulty = target2difficulty(self.target) + self.target = bits_to_target(self.bits) + self.difficulty = target_to_difficulty(self.target) self.merkle_branches = [hexlify(i).decode() for i in merkle_branches([self.coinbase_tx.hash,] + self.txid_list)] @@ -906,7 +661,7 @@ class BlockTemplate(): print("branches ", self.merkle_branches) header = version + prev_hash + merkle_root + time + bits + nonce block = hexlify(header).decode() - block += hexlify(to_var_int(len (self.transactions)+1)).decode() + block += hexlify(int_to_var_int(len (self.transactions) + 1)).decode() block += cb for t in self.transactions: block += t["data"] diff --git a/pybtc/constants.py b/pybtc/constants.py index 4eb603a..cdc508f 100644 --- a/pybtc/constants.py +++ b/pybtc/constants.py @@ -74,3 +74,4 @@ SCRIPT_TYPES = { "P2PKH": 0, "NON_STANDART": 7 } + diff --git a/pybtc/encode.py b/pybtc/encode.py index ccbb546..9f7a155 100644 --- a/pybtc/encode.py +++ b/pybtc/encode.py @@ -14,8 +14,6 @@ for n, i in enumerate(base32charset_upcase): int_base32_map[i] = n - - def rebasebits(data, frombits, tobits, pad=True): """General power-of-2 base conversion.""" acc = 0 diff --git a/pybtc/hash.py b/pybtc/hash.py index 51c779e..6f45fc9 100644 --- a/pybtc/hash.py +++ b/pybtc/hash.py @@ -16,7 +16,6 @@ def double_sha256(h, hex = False): return sha256(sha256(h), 1) return sha256(sha256(h)) - def hmac_sha512(key, data, hex = False): if hex: return hmac.new(key, data, hashlib.sha512).hexdigest() diff --git a/pybtc/opcodes.py b/pybtc/opcodes.py index 95d8df6..ad416ee 100644 --- a/pybtc/opcodes.py +++ b/pybtc/opcodes.py @@ -1,148 +1,155 @@ +from binascii import hexlify + +OPCODE = dict() + +# push opcodes + +OPCODE["OP_FALSE"] = 0x00 +OPCODE["OP_0"] = 0x00 +OPCODE["OP_PUSHDATA1"] = 0x4c +OPCODE["OP_PUSHDATA2"] = 0x4d +OPCODE["OP_PUSHDATA4"] = 0x4e +OPCODE["OP_1NEGATE"] = 0x4f +OPCODE["OP_RESERVED"] = 0x50 +OPCODE["OP_1"] = 0x51 +OPCODE["OP_TRUE"] = 0x51 +OPCODE["OP_2"] = 0x52 +OPCODE["OP_3"] = 0x53 +OPCODE["OP_4"] = 0x54 +OPCODE["OP_5"] = 0x55 +OPCODE["OP_6"] = 0x56 +OPCODE["OP_7"] = 0x57 +OPCODE["OP_8"] = 0x58 +OPCODE["OP_9"] = 0x59 +OPCODE["OP_10"] = 0x5a +OPCODE["OP_11"] = 0x5b +OPCODE["OP_12"] = 0x5c +OPCODE["OP_13"] = 0x5d +OPCODE["OP_14"] = 0x5e +OPCODE["OP_15"] = 0x5f +OPCODE["OP_16"] = 0x60 + +# control + +OPCODE["OP_NOP"] = 0x61 +OPCODE["OP_VER"] = 0x62 +OPCODE["OP_IF"] = 0x63 +OPCODE["OP_NOTIF"] = 0x64 +OPCODE["OP_VERIF"] = 0x65 +OPCODE["OP_ELSE"] = 0x67 +OPCODE["OP_ENDIF"] = 0x68 +OPCODE["OP_VERIFY"] = 0x69 +OPCODE["OP_RETURN"] = 0x6a + +# stack + +OPCODE["OP_TOALTSTACK"] = 0x6b +OPCODE["OP_FROMALTSTACK"] = 0x6c +OPCODE["OP_2DROP"] = 0x6d +OPCODE["OP_2DUP"] = 0x6e +OPCODE["OP_3DUP"] = 0x6f +OPCODE["OP_2OVER"] = 0x70 +OPCODE["OP_2ROT"] = 0x71 +OPCODE["OP_2SWAP"] = 0x72 +OPCODE["OP_IFDUP"] = 0x73 +OPCODE["OP_DEPTH"] = 0x74 +OPCODE["OP_DROP"] = 0x75 +OPCODE["OP_DUP"] = 0x76 +OPCODE["OP_NIP"] = 0x77 +OPCODE["OP_OVER"] = 0x78 +OPCODE["OP_PICK"] = 0x79 +OPCODE["OP_ROLL"] = 0x7a +OPCODE["OP_ROT"] = 0x7b +OPCODE["OP_SWAP"] = 0x7c +OPCODE["OP_TUCK"] = 0x7d + +# splice + +OPCODE["OP_CAT"] = 0x7e +OPCODE["OP_SUBSTR"] = 0x7f +OPCODE["OP_LEFT"] = 0x80 +OPCODE["OP_RIGHT"] = 0x81 +OPCODE["OP_SIZE"] = 0x82 + +# bit operations + +OPCODE["OP_INVERT"] = 0x83 +OPCODE["OP_AND"] = 0x84 +OPCODE["OP_OR"] = 0x85 +OPCODE["OP_XOR"] = 0x86 +OPCODE["OP_EQUAL"] = 0x87 +OPCODE["OP_EQUALVERIFY"] = 0x88 +OPCODE["OP_RESERVED1"] = 0x89 +OPCODE["OP_RESERVED2"] = 0x8a + +# math + +OPCODE["OP_1ADD"] = 0x8b +OPCODE["OP_1SUB"] = 0x8c +OPCODE["OP_2MUL"] = 0x8d +OPCODE["OP_2DIV"] = 0x8e +OPCODE["OP_NEGATE"] = 0x8f +OPCODE["OP_ABS"] = 0x90 +OPCODE["OP_NOT"] = 0x91 +OPCODE["OP_0NOTEQUAL"] = 0x92 + +OPCODE["OP_ADD"] = 0x93 +OPCODE["OP_SUB"] = 0x94 +OPCODE["OP_MUL"] = 0x95 +OPCODE["OP_DIV"] = 0x96 +OPCODE["OP_MOD"] = 0x97 +OPCODE["OP_LSHIFT"] = 0x98 +OPCODE["OP_RSHIFT"] = 0x99 + +OPCODE["OP_BOOLAND"] = 0x9a +OPCODE["OP_BOOLOR"] = 0x9b +OPCODE["OP_NUMEQUAL"] = 0x9c +OPCODE["OP_NUMEQUALVERIFY"] = 0x9d +OPCODE["OP_NUMNOTEQUAL"] = 0x9e +OPCODE["OP_LESSTHAN"] = 0x9f +OPCODE["OP_GREATERTHAN"] = 0xa0 +OPCODE["OP_LESSTHANOREQUAL"] = 0xa1 +OPCODE["OP_GREATERTHANOREQUAL"] = 0xa2 +OPCODE["OP_MIN"] = 0xa3 +OPCODE["OP_MAX"] = 0xa4 + +OPCODE["OP_WITHIN"] = 0xa5 + +# crypto + +OPCODE["OP_RIPEMD160"] = 0xa6 +OPCODE["OP_SHA1"] = 0xa7 +OPCODE["OP_SHA256"] = 0xa8 +OPCODE["OP_HASH160"] = 0xa9 +OPCODE["OP_HASH256"] = 0xaa +OPCODE["OP_CODESEPARATOR"] = 0xab +OPCODE["OP_CHECKSIG"] = 0xac +OPCODE["OP_CHECKSIGVERIFY"] = 0xad +OPCODE["OP_CHECKMULTISIG"] = 0xae +OPCODE["OP_CHECKMULTISIGVERIFY"] = 0xaf + +# expansion + +OPCODE["OP_NOP1"] = 0xb0 +OPCODE["OP_CHECKLOCKTIMEVERIFY"] = 0xb1 +OPCODE["OP_CHECKSEQUENCEVERIFY"] = 0xb2 +OPCODE["OP_NOP4"] = 0xb3 +OPCODE["OP_NOP5"] = 0xb4 +OPCODE["OP_NOP6"] = 0xb5 +OPCODE["OP_NOP7"] = 0xb6 +OPCODE["OP_NOP8"] = 0xb7 +OPCODE["OP_NOP9"] = 0xb8 +OPCODE["OP_NOP10"] = 0xb9 + +# template matching params + +OPCODE["OP_SMALLINTEGER"] = 0xfa +OPCODE["OP_PUBKEYS"] = 0xfb +OPCODE["OP_PUBKEYHASH"] = 0xfd +OPCODE["OP_PUBKEY"] = 0xfe +OPCODE["OP_INVALIDOPCODE"] = 0xff -OPCODE = {"OP_0": b'\x00', - "OP_PUSHDATA1": b'L', - "OP_PUSHDATA2": b'M', - "OP_PUSHDATA4": b'N', - "OP_1NEGATE": b'O', - "OP_RESERVED": b'P', - "OP_1": b'Q', - "OP_2": b'R', - "OP_3": b'S', - "OP_4": b'T', - "OP_5": b'U', - "OP_6": b'V', - "OP_7": b'W', - "OP_8": b'X', - "OP_9": b'Y', - "OP_10": b'Z', - "OP_11": b'[', - "OP_12": b'\\', - "OP_13": b']', - "OP_14": b'^', - "OP_15": b'_', - "OP_16": b'`', - "OP_NOP": b'a', - "OP_VER": b'b', - "OP_IF": b'c', - "OP_NOTIF": b'd', - "OP_VERIF": b'e', - "OP_VERNOTIF": b'f', - "OP_ELSE": b'g', - "OP_ENDIF": b'h', - "OP_VERIFY": b'i', - "OP_RETURN": b'j', - "OP_TOALTSTACK": b'k', - "OP_FROMALTSTACK": b'l', - "OP_2DROP": b'm', - "OP_2DUP": b'n', - "OP_3DUP": b'o', - "OP_2OVER": b'p', - "OP_2ROT": b'q', - "OP_2SWAP": b'r', - "OP_IFDUP": b's', - "OP_DEPTH": b't', - "OP_DROP": b'u', - "OP_DUP": b'v', - "OP_NIP": b'w', - "OP_OVER": b'x', - "OP_PICK": b'y', - "OP_ROLL": b'z', - "OP_ROT": b'{', - "OP_SWAP": b'|', - "OP_TUCK": b'}', - "OP_CAT": b'~', - "OP_SUBSTR": b'\x7f', - "OP_LEFT": b'\x80', - "OP_RIGHT": b'\x81', - "OP_SIZE": b'\x82', - "OP_INVERT": b'\x83', - "OP_AND": b'\x84', - "OP_OR": b'\x85', - "OP_XOR": b'\x86', - "OP_EQUAL": b'\x87', - "OP_EQUALVERIFY": b'\x88', - "OP_RESERVED1": b'\x89', - "OP_RESERVED2": b'\x8a', - "OP_1ADD": b'\x8b', - "OP_1SUB": b'\x8c', - "OP_2MUL": b'\x8d', - "OP_2DIV": b'\x8e', - "OP_NEGATE": b'\x8f', - "OP_ABS": b'\x90', - "OP_NOT": b'\x91', - "OP_0NOTEQUAL": b'\x92', - "OP_ADD": b'\x93', - "OP_SUB": b'\x94', - "OP_MUL": b'\x95', - "OP_DIV": b'\x96', - "OP_MOD": b'\x97', - "OP_LSHIFT": b'\x98', - "OP_RSHIFT": b'\x99', - "OP_BOOLAND": b'\x9a', - "OP_BOOLOR": b'\x9b', - "OP_NUMEQUAL": b'\x9c', - "OP_NUMEQUALVERIFY": b'\x9d', - "OP_NUMNOTEQUAL": b'\x9e', - "OP_LESSTHAN": b'\x9f', - "OP_GREATERTHAN": b'\xa0', - "OP_LESSTHANOREQUAL": b'\xa1', - "OP_GREATERTHANOREQUAL": b'\xa2', - "OP_MIN": b'\xa3', - "OP_MAX": b'\xa4', - "OP_WITHIN": b'\xa5', - "OP_RIPEMD160": b'\xa6', - "OP_SHA1": b'\xa7', - "OP_SHA256": b'\xa8', - "OP_HASH160": b'\xa9', - "OP_HASH256": b'\xaa', - "OP_CODESEPARATOR": b'\xab', - "OP_CHECKSIG": b'\xac', - "OP_CHECKSIGVERIFY": b'\xad', - "OP_CHECKMULTISIG": b'\xae', - "OP_CHECKMULTISIGVERIFY": b'\xaf', - "OP_NOP1": b'\xb0', - "OP_NOP2": b'\xb1', - "OP_NOP3": b'\xb2', - "OP_NOP4": b'\xb3', - "OP_NOP5": b'\xb4', - "OP_NOP6": b'\xb5', - "OP_NOP7": b'\xb6', - "OP_NOP8": b'\xb7', - "OP_NOP9": b'\xb8', - "OP_NOP10": b'\xb9', - "OP_NULLDATA": b'\xfc', - "OP_PUBKEYHASH": b'\xfd', - "OP_PUBKEY": b'\xfe', - "OP_INVALIDOPCODE": b'\xff'} RAW_OPCODE = dict ( (OPCODE[i], i) for i in OPCODE ) - -DISABLED_OPCODE = set (( - # OPCODE["OP_RETURN"], - OPCODE["OP_CAT"], - OPCODE["OP_SUBSTR"], - OPCODE["OP_LEFT"], - OPCODE["OP_RIGHT"], - OPCODE["OP_LEFT"], - OPCODE["OP_LEFT"], - OPCODE["OP_AND"], - OPCODE["OP_OR"], - OPCODE["OP_XOR"], - OPCODE["OP_2MUL"], - OPCODE["OP_2DIV"], - OPCODE["OP_MUL"], - OPCODE["OP_DIV"], - OPCODE["OP_MOD"], - OPCODE["OP_LSHIFT"], - OPCODE["OP_RSHIFT"], - OPCODE["OP_RESERVED"], - # OPCODE["OP_VER"], - OPCODE["OP_VERIF"], - OPCODE["OP_VERNOTIF"], - OPCODE["OP_RESERVED1"], - OPCODE["OP_RESERVED2"], - OPCODE["OP_PUBKEYHASH"], - OPCODE["OP_PUBKEY"], - OPCODE["OP_INVALIDOPCODE"] - )) - +BYTE_OPCODE = dict ((i,bytes([OPCODE[i]])) for i in OPCODE ) +HEX_OPCODE = dict ((i,hexlify(bytes([OPCODE[i]])).decode()) for i in OPCODE ) diff --git a/pybtc/tools.py b/pybtc/tools.py index 74213f1..dbed6bd 100644 --- a/pybtc/tools.py +++ b/pybtc/tools.py @@ -1,23 +1,20 @@ -import hashlib -from binascii import hexlify, unhexlify import time import struct -import hmac from .constants import * from .opcodes import * from .hash import * from .encode import * +import math +import io -# Bitcoin keys/ addresses +# Bitcoin keys # -def create_priv(hex=False): +def create_private_key(hex=False): """ :return: 32 bytes private key """ - q = time.time() - rnd = random.SystemRandom() - a = rnd.randint(0,MAX_INT_PRIVATE_KEY) + a = random.SystemRandom().randint(0,MAX_INT_PRIVATE_KEY) i = int((time.time()%0.01)*100000) h = a.to_bytes(32,byteorder="big") while True: @@ -30,15 +27,12 @@ def create_priv(hex=False): return hexlify(h).decode() return h -def priv_from_int(k): - return int.to_bytes(k,byteorder="big",length=32) - - -def priv2WIF(h, compressed = True, testnet = False): +def private_key_to_WIF(h, compressed = True, testnet = False): #uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded #compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded if type(h) == str: h = unhexlify(h) + assert len(h) == 32 if testnet: h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h else: @@ -47,15 +41,15 @@ def priv2WIF(h, compressed = True, testnet = False): h += double_sha256(h)[:4] return encode_base58(h) -def WIF2priv(h, hex = False, verify = 1): - if verify: - assert is_WIF_valid(h) +def WIF_to_private_key(h, hex = False): + assert is_WIF_valid(h) h = decode_base58(h) if hex: return hexlify(h[1:33]).decode() return h[1:33] def is_WIF_valid(wif): + assert type(wif) == str if wif[0] not in PRIVATE_KEY_PREFIX_LIST: return False try: @@ -73,7 +67,7 @@ def is_WIF_valid(wif): return False return True -def priv2pub(private_key, compressed = True, hex = False): +def private_to_public_key(private_key, compressed = True, hex = False): if type(private_key)!= bytes: if type(private_key) == bytearray: private_key = bytes(private_key) @@ -84,7 +78,7 @@ def priv2pub(private_key, compressed = True, hex = False): if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): compressed = False - private_key = WIF2priv(private_key, verify=0) + private_key = WIF_to_private_key(private_key) else: raise TypeError("private key must be a bytes or WIF or hex encoded string") pubkey_ptr = ffi.new('secp256k1_pubkey *') @@ -99,7 +93,7 @@ def priv2pub(private_key, compressed = True, hex = False): pub = bytes(ffi.buffer(pubkey, len_key)) return hexlify(pub).decode() if hex else pub -def is_valid_pub(key): +def is_valid_public_key(key): if len(key) < 33: return False if key[0] == 0x04 and len(key) != 65: @@ -115,8 +109,8 @@ def is_valid_pub(key): # -def hash2address(address_hash, testnet = False, - script_hash = False, witness_version = 0): +def hash_to_address(address_hash, testnet = False, + script_hash = False, witness_version = 0): if type(address_hash) == str: address_hash = unhexlify(address_hash) if not script_hash: @@ -151,7 +145,7 @@ def hash2address(address_hash, testnet = False, return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode()) -def address2hash(address, hex = False): +def address_to_hash(address, hex = False): if address[0] in ADDRESS_PREFIX_LIST: h = decode_base58(address)[1:-4] elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX, @@ -190,7 +184,7 @@ def address_type(address, num = False): return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN' return SCRIPT_TYPES[t] if num else t -def script2hash(s, witness = False, hex = False): +def script_to_hash(s, witness = False, hex = False): if type(s) == str: s = unhexlify(s) if witness: @@ -198,31 +192,41 @@ def script2hash(s, witness = False, hex = False): else: return hash160(s, hex) -def address2script(address): +def address_to_script(address, hex = False): if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, MAINNET_SCRIPT_ADDRESS_PREFIX): - return OPCODE["OP_HASH160"] + b'\x14' + address2hash(address) + OPCODE["OP_EQUAL"] - if address[0] in (MAINNET_ADDRESS_PREFIX, + s = [BYTE_OPCODE["OP_HASH160"], + b'\x14', + address_to_hash(address), + BYTE_OPCODE["OP_EQUAL"]] + elif address[0] in (MAINNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX_2): - return OPCODE["OP_DUP"] + OPCODE["OP_HASH160"] + b'\x14' + \ - address2hash(address) + OPCODE["OP_EQUALVERIFY"] + OPCODE["OP_CHECKSIG"] - if address[0] in (TESTNET_SEGWIT_ADDRESS_PREFIX, + s = [BYTE_OPCODE["OP_DUP"], + BYTE_OPCODE["OP_HASH160"], + b'\x14', + address_to_hash(address), + BYTE_OPCODE["OP_EQUALVERIFY"], + BYTE_OPCODE["OP_CHECKSIG"]] + elif address[:2] in (TESTNET_SEGWIT_ADDRESS_PREFIX, MAINNET_SEGWIT_ADDRESS_PREFIX): - h = address2hash(address) - return OPCODE["OP_0"] + bytes([len(h)]) + h - raise Exception("Unknown address") + h = address_to_hash(address) + s = [BYTE_OPCODE["OP_0"], + bytes([len(h)]), + h] + else: + assert False + s = b''.join(s) + return hexlify(s).decode() if hex else s -def script_P2SH_P2WPKH(pubkey, hash = False): +def public_key_to_P2SH_P2WPKH_script(pubkey): assert len(pubkey) == 33 - if hash: - return hash160(b'\x00\x14' + hash160(pubkey)) return b'\x00\x14' + hash160(pubkey) -def pub2address(pubkey, testnet = False, - p2sh_p2wpkh = False, - witness_version = 0): +def public_key_to_address(pubkey, testnet = False, + p2sh_p2wpkh = False, + witness_version = 0): if type(pubkey) == str: pubkey = unhexlify(pubkey) if p2sh_p2wpkh: @@ -233,18 +237,122 @@ def pub2address(pubkey, testnet = False, if witness_version is not None: assert len(pubkey) == 33 h = hash160(pubkey) - return hash2address(h, testnet = testnet, + return hash_to_address(h, testnet = testnet, script_hash = p2sh_p2wpkh, witness_version = witness_version) -# def pub2P2SH_P2WPKH_hash(pubkey): -# return hash160(b'\x00\x14' + hash160(pubkey)) -# -# def pub2P2SH_P2WPKH_address(pubkey, testnet = False): -# return hash2address(pub2P2SH_P2WPKH_hash(pubkey), -# testnet=testnet, -# script_hash=True, -# witness_version=None) +def parse_script(script, segwit=True): + if type(script) == str: + try: + script = unhexlify(script) + except: + pass + assert type(script) == bytes + l = len(script) + if segwit: + if l == 22 and script[0] == 0: + return {"nType": 5, "type": "P2WPKH", "reqSigs": 1, "addressHash": script[2:]} + if l == 34 and script[0] == 0: + return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]} + if l == 25 and \ + script[:2] == b"\x76\xa9" and \ + script[-2:] == b"\x88\xac": + return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]} + if l == 23 and \ + script[0] == 169 and \ + script[-1] == 135: + return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]} + if l == 67 and script[-1] == 172: + return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} + if l == 35 and script[-1] == 172: + return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} + if script[0] == 106 and l > 1 and l <= 82: + if script[1] == l - 2: + return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": script[2:]} + if script[0] >= 81 and script[0] <= 96: + if script[-1] == 174: + if script[-2] >= 81 and script[-2] <= 96: + if script[-2] >= script[0]: + c, s = 0, 1 + while l - 2 - s > 0: + if script[s] < 0x4c: + s += script[s] + c += 1 + else: + c = 0 + break + s += 1 + if c == script[-2] - 80: + return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "script": script} + + s, m, n, last, req_sigs = 0, 0, 0, 0, 0 + while l - s > 0: + if script[s] >= 81 and script[s] <= 96: + if not n: + n = script[s] - 80 + else: + if m == 0: + n, m = script[s] - 80, 0 + elif n > m: + n, m = script[s] - 80, 0 + elif m == script[s] - 80: + last = 0 if last else 2 + elif script[s] < 0x4c: + s += script[s] + m += 1 + if m > 16: + n, m = 0, 0 + elif script[s] == OPCODE["OP_PUSHDATA1"]: + s += 1 + script[s + 1] + elif script[s] == OPCODE["OP_PUSHDATA2"]: + s += 2 + struct.unpack(' 0: + if script[s] < 0x4c and script[s]: + if asm: + result.append(hexlify(script[s+1:s+1 +script[s]]).decode()) + else: + result.append('[%s]' % script[s]) + s += script[s] + 1 + continue + elif script[s] == OPCODE["OP_PUSHDATA1"]: + s += 1 + script[s + 1] + elif script[s] == OPCODE["OP_PUSHDATA2"]: + s += 2 + struct.unpack('> 24 - target = (bits & 0xffffff) * (1 << (8 * (shift - 3))) - return target - -def target2difficulty(target): - return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target - -def bits2difficulty(bits): - return target2difficulty(bits2target(bits)) - -def difficulty2target(difficulty): - return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty) - def rh2s(tthash): return hexlify(tthash[::-1]).decode() @@ -479,7 +568,9 @@ def s2rh_step4(hash_string): def reverse_hash(h): return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', h)[::-1])[::-1] - +# +# +# def merkleroot(tx_hash_list): tx_hash_list = list(tx_hash_list) @@ -529,73 +620,143 @@ def merkleroot_from_branches(merkle_branches, coinbase_hash_bin): h = unhexlify(h) merkle_root = double_sha256(merkle_root + h) return merkle_root + +def bits_to_target(bits): + if type(bits) == str: + bits = unhexlify(bits) + if type(bits) == bytes: + return int.from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3))) + else: + shift = bits >> 24 + target = (bits & 0xffffff) * (1 << (8 * (shift - 3))) + return target + +def target_to_difficulty(target): + return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target + +def bits_to_difficulty(bits): + return target_to_difficulty(bits_to_target(bits)) + +def difficulty_to_target(difficulty): + return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty) + + # # # +def bytes_needed(n): + if n == 0: + return 1 + return math.ceil(n.bit_length()/8) -def var_int(data): - e, s = 1, 0 - if data[:1] == b'\xfd': - s, e = 1, 3 - elif data[:1] == b'\xfe': - s = 1 - e = 5 - elif data[:1] == b'\xff': - s = 1 - e = 9 - i = int.from_bytes(data[s:e], byteorder='little', signed=False) - return (i, e) +def int_to_bytes(i, byteorder='big'): + return i.to_bytes(bytes_needed(i), byteorder=byteorder, signed=False) + +def bytes_to_int(i, byteorder='big'): + return int.from_bytes(i, byteorder=byteorder, signed=False) -def from_var_int(data): - # retrun - e = 1 - s = 0 - if data[:1] == b'\xfd': - s = 1 - e = 3 - elif data[:1] == b'\xfe': - s = 1 - e = 5 - elif data[:1] == b'\xff': - s = 1 - e = 9 - i = int.from_bytes(data[s:e], byteorder='little', signed=False) - return i +# variable integer - -def var_int_len(byte): - e = 1 - if byte == 253: - e = 3 - elif byte == 254: - e = 5 - elif byte == 255: - e = 9 - return e - - -def to_var_int(i): - if i < 253: - return i.to_bytes(1, byteorder='little') +def int_to_var_int(i): + if i < 0xfd: + return struct.pack(' OP_EQUALVERIFY OP_CHECKSIG") self.assertEqual(s.op_sig_count, 1) @@ -24,7 +28,7 @@ class ScriptDeserializeTests(unittest.TestCase): self.assertEqual(s.type, "P2SH") self.assertEqual(s.ntype, 1) self.assertEqual(s.asm, "OP_HASH160 69f37572ab1b69f304f987b119e2450e0b71bf5c OP_EQUAL") - self.assertEqual(s.address[0], address2hash("3BMEXVsYyfKB5h3m53XRSFHkqi1zPwsvcK")) + self.assertEqual(s.address[0], address_to_hash("3BMEXVsYyfKB5h3m53XRSFHkqi1zPwsvcK")) self.assertEqual(s.pattern, "OP_HASH160 <20> OP_EQUAL") self.assertEqual(s.op_sig_count, 0) diff --git a/tests/test/sighash.py b/tests/test/sighash.py index 947cfd4..5e5ac86 100644 --- a/tests/test/sighash.py +++ b/tests/test/sighash.py @@ -1,7 +1,11 @@ import unittest +import os, sys +parentPath = os.path.abspath("..") +if parentPath not in sys.path: + sys.path.insert(0, parentPath) from pybtc import * from binascii import unhexlify -from pybtc import address2hash as address2hash160 +from pybtc import address_to_hash as address2hash160 class SighashTests(unittest.TestCase): @classmethod diff --git a/tests/test/transaction_deserialize.py b/tests/test/transaction_deserialize.py index 572a5b4..971fed1 100644 --- a/tests/test/transaction_deserialize.py +++ b/tests/test/transaction_deserialize.py @@ -1,31 +1,65 @@ import unittest -from pybtc import blockchain +import os, sys +import time +parentPath = os.path.abspath("..") +if parentPath not in sys.path: + sys.path.insert(0, parentPath) +from pybtc.tools import * +from pybtc.hash import * +from pybtc.transaction import * from binascii import unhexlify -from pybtc import address2hash as address2hash160 +from pybtc import address_to_hash as address2hash160 + +def decode_block_tx(block): + stream = get_stream(block) + tx = dict() + stream.seek(80) + count = var_int_to_int(read_var_int(stream)) + for i in range(count): + tx[i] = Transaction(stream) + return tx class TransactionDeserializeTests(unittest.TestCase): @classmethod def setUpClass(cls): print("\nTesting Transaction class deserialization:\n") + + def test_serialaize_and_perfomance(self): + f = open('./test/raw_block.txt') + fc = f.readline() + qt = time.time() + bt = decode_block_tx(fc[:-1]) + self.assertEqual(time.time() - qt < 1, 1) + print("decode block tx count: %s time: %s" % (len(bt), time.time() - qt)) + for t in bt: + raw_tx_legacy = bt[t].serialize(segwit=False) + raw_tx_segwit = bt[t].serialize() + bt[t] = bt[t].decode() + # print(bt[t]["txId"], bt[t]["hash"], "segwit:", + # True if "segwit" in bt[t] else False, end = " ") + self.assertEqual(bt[t].serialize(segwit=False), raw_tx_legacy) + self.assertEqual(bt[t].serialize(), raw_tx_segwit) + self.assertEqual(rh2s(double_sha256(bt[t].serialize())), bt[t]["hash"]) + self.assertEqual(rh2s(double_sha256(bt[t].serialize(segwit=False))), bt[t]["txId"]) + # print("OK") + def test_segwit_deserialize(self): non_segwit_view = "020000000140d43a99926d43eb0e619bf0b3d83b4a31f60c176beecfb9d35bf45e54d0f7420100000017160014a4b4ca48de0b3fffc15404a1acdc8dbaae226955ffffffff0100e1f5050000000017a9144a1154d50b03292b3024370901711946cb7cccc38700000000" segwit_view = "0200000000010140d43a99926d43eb0e619bf0b3d83b4a31f60c176beecfb9d35bf45e54d0f7420100000017160014a4b4ca48de0b3fffc15404a1acdc8dbaae226955ffffffff0100e1f5050000000017a9144a1154d50b03292b3024370901711946cb7cccc387024830450221008604ef8f6d8afa892dee0f31259b6ce02dd70c545cfcfed8148179971876c54a022076d771d6e91bed212783c9b06e0de600fab2d518fad6f15a2b191d7fbd262a3e0121039d25ab79f41f75ceaf882411fd41fa670a4c672c23ffaf0e361a969cde0692e800000000" print("Deserialize Segwit transaction") - ns = blockchain.Transaction.deserialize(non_segwit_view) - s = blockchain.Transaction.deserialize(segwit_view) - s.serialize(True,True) - self.assertEqual(s.serialize(False,True), non_segwit_view) - self.assertEqual(s.serialize(True,True), segwit_view) - self.assertEqual(ns.serialize(False,True), non_segwit_view) + ns = Transaction(non_segwit_view) + s = Transaction(segwit_view) + self.assertEqual(s.serialize(segwit=False, hex = True), non_segwit_view) + self.assertEqual(s.serialize(segwit=True, hex = True), segwit_view) + self.assertEqual(ns.serialize(segwit=False, hex = True), non_segwit_view) non_segwit_view = "01000000060c02c24bbfefd94cdc4f58a3f83f93e05b14ad968ec6aba54190c3dcba6eef1b00000000da00483045022100f4dbf2ca7b5da97bd78818635d48004e6bf1a6f7e5e24bcecb7d93f554e49eaf02200a05025d93475b6372d14bd8fe8366fe10570ade772b19d124d3b0175b9f6eda0147304402202290feb53fc4cb077c5d3eed0ed5367fef4011ac708c1acaaa5003e4ed680ddf022012c52160ae6b85fc59ceed80c7cacd5358b80712371733de7c76fef552114ee60147522103ee30ea502d692ecfc79dfcb6fb2d6914fc70ba80db971beebacccdbaed40d7c52102f52db885d04dc43ca8562471759dd621b899faf508caba70963d9228bfd2415e52ae00000000dab020ee0a80a818e4d20a52aa7ba367a0a2d430d22c26ccb4572527e259e14a01000000d900473044022064745ac8cae859bb19305a550981b8d39b69efec55b9a423dca645cd3b5c845502205cf375839d7f056235feb550a8138a03e75fa140503a2ce61fe239a3bfe42145014730440220728b0393d5427d8abb56a608c6b1a0b14c5455f3abeb56ce8a5c7f70035af71d022052a99e4e389790b364f6180caf1523d6da2b3daabe85952705023be2b5409b360147522103e296937dbdafdae4d0a45b33d9a691b0c0e71d97bd2ffc82da895a251f74bd7e2103ead7ad0c398f928cbe851a6b387a5e74d139487caf4d4ac3dc3d478254dbbb4452ae0000000067a6c2e2f14fc412b3f5627fafac2fe43009bc403ec680839579444df2ce042b00000000da00483045022100d3bdc055fa5dcce335a1d572b1c3ccb8cc9ba91960c6361c77e29ded716e233102200e7ebb43fd39fb98c714098d4fda32d94cdbefdd96c0c918b788aacc6164953c0147304402202f4338d2710edb1960dcf7136411f065a16bee4e44b86604d64c660315bc55040220238c1c3216feb31051f7798297317819da1dfa830d24a6a9e38a37367a68ebd101475221037b2987df626510ce25e6ce5fdb716705e23fecb7398b2cbb0a1c0af7ca5da717210345e358653b4580b5bd68d263089a0a2bf9fcc1e145fcf2a3d4b9ab5cd7e1a76752ae000000004f28d63103dfb86a5d92d2daf328bbb35d72239766a5853b7076a90d1745813200000000da00483045022100e89ac8215ee87186de284c419b2522ebfb2ecb8063d0f91942f2ad63f383d3d4022036485902bb1f2e0b2cc645aab8781def27f25e91d8256d48dd48d5cfca1a21c20147304402201449379f1d57f2b7ad1dc0882f59627287a6c32180ffa7637941b0eaa666dd4b022028eb0eed77e1b92de046098c855834a5feeadea55d17160bc6d11d47184e8b51014752210283db605dc305201ab9be509a22d2c83b388002fb54ecd82d86efe83c0a1d35822103146f745eff0ae31fe899aafd27d51d2c0f5b0c03f2f47b3c65bb26ec7581ad8652ae0000000021cb3b00d1f22455e76e86872e00ef556578bcc112071e6a5b4ac02ab682fdb301000000232200206ea344e9a4a8f8a8983479af2ae3ed29fab153955af14457780a304a6832b9c50000000016dcc4b40a514c43ed61d6c01a9006d7f21a6d30b99b3e580d21578e35002502000000002322002049ea1f7c280b32fee0dce2e1801df2218df59d64614c4fe76c043ee2c80116700000000002005ed0b20000000017a91495c5c19257aa52bd4b702ba1a5e29b8d72a75a3a876d6b4e010000000017a91487b6255a5df746188f0bd22ed0194a40ec98f2de87be810700" segwit_view = "010000000001060c02c24bbfefd94cdc4f58a3f83f93e05b14ad968ec6aba54190c3dcba6eef1b00000000da00483045022100f4dbf2ca7b5da97bd78818635d48004e6bf1a6f7e5e24bcecb7d93f554e49eaf02200a05025d93475b6372d14bd8fe8366fe10570ade772b19d124d3b0175b9f6eda0147304402202290feb53fc4cb077c5d3eed0ed5367fef4011ac708c1acaaa5003e4ed680ddf022012c52160ae6b85fc59ceed80c7cacd5358b80712371733de7c76fef552114ee60147522103ee30ea502d692ecfc79dfcb6fb2d6914fc70ba80db971beebacccdbaed40d7c52102f52db885d04dc43ca8562471759dd621b899faf508caba70963d9228bfd2415e52ae00000000dab020ee0a80a818e4d20a52aa7ba367a0a2d430d22c26ccb4572527e259e14a01000000d900473044022064745ac8cae859bb19305a550981b8d39b69efec55b9a423dca645cd3b5c845502205cf375839d7f056235feb550a8138a03e75fa140503a2ce61fe239a3bfe42145014730440220728b0393d5427d8abb56a608c6b1a0b14c5455f3abeb56ce8a5c7f70035af71d022052a99e4e389790b364f6180caf1523d6da2b3daabe85952705023be2b5409b360147522103e296937dbdafdae4d0a45b33d9a691b0c0e71d97bd2ffc82da895a251f74bd7e2103ead7ad0c398f928cbe851a6b387a5e74d139487caf4d4ac3dc3d478254dbbb4452ae0000000067a6c2e2f14fc412b3f5627fafac2fe43009bc403ec680839579444df2ce042b00000000da00483045022100d3bdc055fa5dcce335a1d572b1c3ccb8cc9ba91960c6361c77e29ded716e233102200e7ebb43fd39fb98c714098d4fda32d94cdbefdd96c0c918b788aacc6164953c0147304402202f4338d2710edb1960dcf7136411f065a16bee4e44b86604d64c660315bc55040220238c1c3216feb31051f7798297317819da1dfa830d24a6a9e38a37367a68ebd101475221037b2987df626510ce25e6ce5fdb716705e23fecb7398b2cbb0a1c0af7ca5da717210345e358653b4580b5bd68d263089a0a2bf9fcc1e145fcf2a3d4b9ab5cd7e1a76752ae000000004f28d63103dfb86a5d92d2daf328bbb35d72239766a5853b7076a90d1745813200000000da00483045022100e89ac8215ee87186de284c419b2522ebfb2ecb8063d0f91942f2ad63f383d3d4022036485902bb1f2e0b2cc645aab8781def27f25e91d8256d48dd48d5cfca1a21c20147304402201449379f1d57f2b7ad1dc0882f59627287a6c32180ffa7637941b0eaa666dd4b022028eb0eed77e1b92de046098c855834a5feeadea55d17160bc6d11d47184e8b51014752210283db605dc305201ab9be509a22d2c83b388002fb54ecd82d86efe83c0a1d35822103146f745eff0ae31fe899aafd27d51d2c0f5b0c03f2f47b3c65bb26ec7581ad8652ae0000000021cb3b00d1f22455e76e86872e00ef556578bcc112071e6a5b4ac02ab682fdb301000000232200206ea344e9a4a8f8a8983479af2ae3ed29fab153955af14457780a304a6832b9c50000000016dcc4b40a514c43ed61d6c01a9006d7f21a6d30b99b3e580d21578e35002502000000002322002049ea1f7c280b32fee0dce2e1801df2218df59d64614c4fe76c043ee2c80116700000000002005ed0b20000000017a91495c5c19257aa52bd4b702ba1a5e29b8d72a75a3a876d6b4e010000000017a91487b6255a5df746188f0bd22ed0194a40ec98f2de87000000000400473044022100d0d2ded141c9369bcc99de23d3d41d1d99d6cff47126df1b0c4d4797f947eacf021f790f1c112b3425ebc3251d719aae6ef0f9830b688585275591a5353f1f973801483045022100bf06c762e6ab64258d2f2777a66fe32ddd8f36e232b80bf5afa6ff9b9aa73ee0022049caf991fce808e60a9b17499f5e0dc11f6163e3ef7bca8109b72b5695d674210147522103edd556806048b319d71f43466c4415001bb32d8afe3aac06532d3ac210fd0e86210215e16727cf1389b4ee377487385f3ec595841a6bb747eb9c3a5cd559e9b1c8dc52ae040047304402204e9cc87526e148d236d692fa70104d26b8df632f30f4e3be38a2e99cec76d0f80220354ae575c3537c0ad2399a6037a9164b0cb147b12f262efc906649ca7950e2eb0147304402203553bcd1565804ec71c997c87006bd91c639b74a004b19a239c7f551aab5635a0220753f74e065c0b7cdf67d16b00f6a20dda7159a49f20aa493a7889b2851b2fea30147522103b09ac1fa65a55fa4feadea57c4cf417d7490065d8b844ada60c242a441e0e3a42103c0625169b46dbbde3492db7c62f1be8f582131467620cef17335306bad7ef88a52aebe810700" print("Deserialize Segwit transaction") - ns = blockchain.Transaction.deserialize(non_segwit_view) - s = blockchain.Transaction.deserialize(segwit_view) - s.serialize(True, True) - self.assertEqual(s.serialize(False, True), non_segwit_view) - self.assertEqual(s.serialize(True, True), segwit_view) - self.assertEqual(ns.serialize(False, True), non_segwit_view) + ns = Transaction(non_segwit_view) + s = Transaction(segwit_view) + self.assertEqual(s.serialize(segwit=False,hex = True), non_segwit_view) + self.assertEqual(s.serialize(segwit=True, hex = True), segwit_view) + self.assertEqual(ns.serialize(segwit=False, hex = True), non_segwit_view)