diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle index cedb736..aeae7e0 100644 Binary files a/docs/build/doctrees/environment.pickle and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree index c34f012..39bafcb 100644 Binary files a/docs/build/doctrees/index.doctree and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/doctrees/transaction.doctree b/docs/build/doctrees/transaction.doctree index eff737f..1da20cd 100644 Binary files a/docs/build/doctrees/transaction.doctree and b/docs/build/doctrees/transaction.doctree differ diff --git a/docs/build/html/_modules/pybtc/address.html b/docs/build/html/_modules/pybtc/address.html index cf20458..3b60f1c 100644 --- a/docs/build/html/_modules/pybtc/address.html +++ b/docs/build/html/_modules/pybtc/address.html @@ -78,10 +78,11 @@ self.hex = hexlify(self.key).decode() self.wif = private_key_to_wif(self.key, compressed, testnet) return - assert isinstance(key, str) + if not isinstance(key, str) or not is_wif_valid(key): + raise TypeError("private key invalid") + self.key = wif_to_private_key(key, hex=False) self.hex = hexlify(self.key).decode() - self.wif = private_key_to_wif(self.key, compressed, testnet) if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): self.compressed = False @@ -92,6 +93,7 @@ self.testnet = True else: self.testnet = False + self.wif = private_key_to_wif(self.key, self.compressed, self.testnet) def __str__(self): return self.wif @@ -252,6 +254,51 @@ witness_version=self.witness_version, testnet=self.testnet) + @classmethod + def multisig(cls, n, m, public_key_list, testnet = False): + """ + The class method for creating a multisig address. + + :param n: count of required signatures (max 15). + :param m: count of total addresses of participants (max 15). + :param list address_list: addresses list, allowed types: + + - bytes or HEX encoded private key + - private key in WIF format + - PrivateKey instance, + - bytes or HEX encoded public key + - PublicKey instance + + + """ + if n > 15 or m > 15 or n > m or n < 1 or m < 1: + raise TypeError("invalid n of m maximum 15 of 15 multisig allowed") + if len(public_key_list) != m: + raise TypeError("invalid address list count") + script = bytes([0x50 + n]) + for a in list(public_key_list): + if isinstance(a, str): + try: + a = unhexlify(a) + except: + if is_wif_valid(a): + a = private_to_public_key(a, hex=False) + pass + if isinstance(a, Address): + a = a.public_key.key + elif isinstance(a, PublicKey): + a = a.key + elif isinstance(a, PrivateKey): + a = private_to_public_key(a.key) + if not isinstance(a, bytes): + raise TypeError("invalid public key list element") + if len(a) == 32: + a = private_to_public_key(a) + if len(a) != 33: + raise TypeError("invalid public key list element size") + script += int_to_var_int(len(a)) + a + script += bytes([0x50 + m]) + OP_CHECKMULTISIG + return cls(script, testnet=testnet) diff --git a/docs/build/html/_modules/pybtc/transaction.html b/docs/build/html/_modules/pybtc/transaction.html index 2f5a626..818089f 100644 --- a/docs/build/html/_modules/pybtc/transaction.html +++ b/docs/build/html/_modules/pybtc/transaction.html @@ -42,639 +42,687 @@
[docs]class Transaction(dict): - def __init__(self, raw_tx=None, tx_format="decoded", version=1, lockTime=0, testnet=False): - assert tx_format in ("decoded", "raw") - self["format"] = tx_format - self["testnet"] = testnet - self["segwit"] = False - self["txId"] = None - self["hash"] = None - self["version"] = version - self["size"] = 0 - self["vSize"] = 0 - self["bSize"] = 0 - self["lockTime"] = lockTime - self["vIn"] = dict() - self["vOut"] = dict() - self["rawTx"] = None - self["blockHash"] = None - self["confirmations"] = None - self["time"] = None - self["blockTime"] = None - self["blockIndex"] = None - self["coinbase"] = False - self["fee"] = None - self["data"] = None - self["amount"] = None - if raw_tx is None: - return - self["amount"] = 0 - stream = self.get_stream(raw_tx) - start = stream.tell() - (self["version"],) = unpack('<L', stream.read(4)) - n = read_var_int(stream) - sw = 0 - sw_len = 0 - if n == b'\x00': - sw = 1 - self["flag"] = stream.read(1) - n = read_var_int(stream) - ic = var_int_to_int(n) - for k in range(ic): - self["vIn"][k] = dict() - self["vIn"][k]["txId"] = stream.read(32) - self["vIn"][k]["vOut"] = unpack('<L', stream.read(4))[0] - n = var_int_to_int(read_var_int(stream)) - self["vIn"][k]["scriptSig"] = stream.read(n) - (self["vIn"][k]["sequence"],) = unpack('<L', stream.read(4)) - for k in range(var_int_to_int(read_var_int(stream))): - self["vOut"][k] = dict() - self["vOut"][k]["value"] = unpack('<Q', stream.read(8))[0] - self["amount"] += self["vOut"][k]["value"] - self["vOut"][k]["scriptPubKey"] = stream.read(var_int_to_int(read_var_int(stream))) - s = parse_script(self["vOut"][k]["scriptPubKey"], sw) - self["vOut"][k]["nType"] = s["nType"] - self["vOut"][k]["type"] = s["type"] - if self["data"] is None: - if s["nType"] == 3: - self["data"] = s["data"] - if s["nType"] not in (3, 4, 7): - self["vOut"][k]["addressHash"] = s["addressHash"] - self["vOut"][k]["reqSigs"] = s["reqSigs"] - if sw: - sw = stream.tell() - start - for k in range(ic): - self["vIn"][k]["txInWitness"] = [stream.read(var_int_to_int(read_var_int(stream))) \ - for c in range(var_int_to_int(read_var_int(stream)))] - sw_len = stream.tell() - sw + 2 - self["lockTime"] = unpack('<L', stream.read(4))[0] - end = stream.tell() - stream.seek(start) - b = stream.read(end - start) - self["rawTx"] = b - self["size"] = end - start - self["bSize"] = end - start - sw_len - self["weight"] = self["bSize"] * 3 + self["size"] - self["vSize"] = math.ceil(self["weight"] / 4) - if ic == 1 and \ - self["vIn"][0]["txId"] == b'\x00' * 32 and \ - self["vIn"][0]["vOut"] == 0xffffffff: - self["coinbase"] = True - else: - self["coinbase"] = False - if sw: - self["segwit"] = True - self["hash"] = double_sha256(b) - self["txId"] = double_sha256(b[:4] + b[6:sw] + b[-4:]) - else: - self["segwit"] = False - self["txId"] = double_sha256(b) - self["hash"] = self["txId"] + """ + The class for Transaction object - def decode(self, testnet=None): - if self["format"] == "decoded": - self.encode() - self["format"] = "decoded" - if testnet is not None: - self["testnet"] = testnet - if type(self["txId"]) == bytes: - self["txId"] = rh2s(self["txId"]) - if "flag" in self: - if type(self["flag"]) == bytes: - self["flag"] = rh2s(self["flag"]) - if type(self["hash"]) == bytes: - self["hash"] = rh2s(self["hash"]) - if type(self["rawTx"]) == bytes: - self["rawTx"] = hexlify(self["rawTx"]).decode() - for i in self["vIn"]: - if type(self["vIn"][i]["txId"]) == bytes: - self["vIn"][i]["txId"] = rh2s(self["vIn"][i]["txId"]) - if type(self["vIn"][i]["scriptSig"]) == bytes: - self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode() - try: - t = list() - for w in self["vIn"][i]["txInWitness"]: - if type(w) == bytes: - w = hexlify(w).decode() - t.append(w) - self["vIn"][i]["txInWitness"] = t - self["vIn"][i]["txInWitnessAsm"] = [decode_script(ws, 1) for ws in - self["vIn"][i]["txInWitness"]] - self["vIn"][i]["txInWitnessOpcodes"] = [decode_script(ws) for ws in - self["vIn"][i]["txInWitness"]] - except: - pass - try: - if type(self["vIn"][i]["addressHash"]) == bytes: - self["vIn"][i]["addressHash"] = hexlify(self["vIn"][i]["addressHash"]).decode() - sh = True if self["vIn"][i]["nType"] in (1, 5) else False - witness_version = None if self["vIn"][i]["nType"] < 5 else 0 - self["vIn"][i]["address"] = hash_to_address(self["vIn"][i]["addressHash"], - self["testnet"], - sh, - witness_version) - except: - pass - if "scriptPubKey" in self["vIn"][i]: - if type(self["vIn"][i]["scriptPubKey"]) == bytes: - self["vIn"][i]["scriptPubKey"] = hexlify(self["vIn"][i]["scriptPubKey"]).decode() - self["vIn"][i]["scriptPubKeyOpcodes"] = decode_script(self["vIn"][i]["scriptPubKey"]) - self["vIn"][i]["scriptPubKeyAsm"] = decode_script(self["vIn"][i]["scriptPubKey"], 1) - if "redeemScript" in self["vIn"][i]: - if type(self["vIn"][i]["redeemScript"]) == bytes: - self["vIn"][i]["redeemScript"] = hexlify(self["vIn"][i]["redeemScript"]).decode() - self["vIn"][i]["redeemScriptOpcodes"] = decode_script(self["vIn"][i]["redeemScript"]) - self["vIn"][i]["redeemScriptAsm"] = decode_script(self["vIn"][i]["redeemScript"], 1) - if not self["coinbase"]: - if type(self["vIn"][i]["scriptSig"]) == bytes: - self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode() - self["vIn"][i]["scriptSigOpcodes"] = decode_script(self["vIn"][i]["scriptSig"]) - self["vIn"][i]["scriptSigAsm"] = decode_script(self["vIn"][i]["scriptSig"], 1) + :param raw_tx: (optional) raw transaction in bytes or HEX encoded string, if no raw transaction provided + well be created new empty transaction template. + :param tx_format: "raw" or "decoded" format. Raw format is mean that all transaction represented in bytes + for best performance. + Decoded transaction is represented in human readable format using base68, hex, bech32, + asm and opcodes. By default "decoded" format using. + :param int version: transaction version for new template, by default 1. + :param int lock_time: transaction lock time for new template, by default 0. + :param boolean testnet: address type for "decoded" transaction representation. - for i in self["vOut"]: - if type(self["vOut"][i]["scriptPubKey"]) == bytes: - self["vOut"][i]["scriptPubKey"] = hexlify(self["vOut"][i]["scriptPubKey"]).decode() - try: - if type(self["vOut"][i]["addressHash"]) == bytes: - self["vOut"][i]["addressHash"] = hexlify(self["vOut"][i]["addressHash"]).decode() - sh = True if self["vOut"][i]["nType"] in (1, 5) else False - witness_version = None if self["vOut"][i]["nType"] < 5 else 0 - self["vOut"][i]["address"] = hash_to_address(self["vOut"][i]["addressHash"], - self["testnet"], - sh, - witness_version) - except: - pass - self["vOut"][i]["scriptPubKeyOpcodes"] = decode_script(self["vOut"][i]["scriptPubKey"]) - self["vOut"][i]["scriptPubKeyAsm"] = decode_script(self["vOut"][i]["scriptPubKey"], 1) - if "data" in self: - if type(self["data"]) == bytes: - self["data"] = hexlify(self["data"]).decode() + """ + def __init__(self, raw_tx=None, tx_format="decoded", version=1, lock_time=0, testnet=False): + if tx_format not in ("decoded", "raw"): + raise TypeError("tx_format error, raw or decoded allowed") + self["format"] = tx_format + self["testnet"] = testnet + self["segwit"] = False + self["txId"] = None + self["hash"] = None + self["version"] = version + self["size"] = 0 + self["vSize"] = 0 + self["bSize"] = 0 + self["lockTime"] = lock_time + self["vIn"] = dict() + self["vOut"] = dict() + self["rawTx"] = None + self["blockHash"] = None + self["confirmations"] = None + self["time"] = None + self["blockTime"] = None + self["blockIndex"] = None + self["coinbase"] = False + self["fee"] = None + self["data"] = None + self["amount"] = None + if raw_tx is None: + return + + self["amount"] = 0 + sw = sw_len = 0 + stream = self.get_stream(raw_tx) + start = stream.tell() + + # start deserialization + self["version"] = unpack('<L', stream.read(4))[0] + n = read_var_int(stream) + if n == b'\x00': + # segwit format + sw = 1 + self["flag"] = stream.read(1) + n = read_var_int(stream) + + # inputs + ic = var_int_to_int(n) + for k in range(ic): + self["vIn"][k] = dict() + self["vIn"][k]["txId"] = stream.read(32) + self["vIn"][k]["vOut"] = unpack('<L', stream.read(4))[0] + self["vIn"][k]["scriptSig"] = stream.read(var_int_to_int(read_var_int(stream))) + (self["vIn"][k]["sequence"],) = unpack('<L', stream.read(4)) + + # outputs + for k in range(var_int_to_int(read_var_int(stream))): + self["vOut"][k] = dict() + self["vOut"][k]["value"] = unpack('<Q', stream.read(8))[0] + self["amount"] += self["vOut"][k]["value"] + self["vOut"][k]["scriptPubKey"] = stream.read(var_int_to_int(read_var_int(stream))) + s = parse_script(self["vOut"][k]["scriptPubKey"], sw) + self["vOut"][k]["nType"] = s["nType"] + self["vOut"][k]["type"] = s["type"] + if self["data"] is None: + if s["nType"] == 3: + self["data"] = s["data"] + if s["nType"] not in (3, 4, 7): + self["vOut"][k]["addressHash"] = s["addressHash"] + self["vOut"][k]["reqSigs"] = s["reqSigs"] + + # witness + if sw: + sw = stream.tell() - start + for k in range(ic): + self["vIn"][k]["txInWitness"] = [stream.read(var_int_to_int(read_var_int(stream))) \ + for c in range(var_int_to_int(read_var_int(stream)))] + sw_len = stream.tell() - sw + 2 + + self["lockTime"] = unpack('<L', stream.read(4))[0] + + end = stream.tell() + stream.seek(start) + b = stream.read(end - start) + self["rawTx"] = b + self["size"] = end - start + self["bSize"] = end - start - sw_len + self["weight"] = self["bSize"] * 3 + self["size"] + self["vSize"] = math.ceil(self["weight"] / 4) + if ic == 1 and \ + self["vIn"][0]["txId"] == b'\x00' * 32 and \ + self["vIn"][0]["vOut"] == 0xffffffff: + self["coinbase"] = True + else: + self["coinbase"] = False + if sw: + self["segwit"] = True + self["hash"] = double_sha256(b) + self["txId"] = double_sha256(b[:4] + b[6:sw] + b[-4:]) + else: + self["segwit"] = False + self["txId"] = double_sha256(b) + self["hash"] = self["txId"] + +
[docs] def decode(self, testnet=None): + """ + change Transacion object representation to "decoded" human readable format + + :param bool testnet: (optional) address type for "decoded" transaction representation, by default None. + if None used type from transaction property "format". + """ + if self["format"] == "decoded": + self.encode() + self["format"] = "decoded" + if testnet is not None: + self["testnet"] = testnet + if type(self["txId"]) == bytes: + self["txId"] = rh2s(self["txId"]) + if "flag" in self: + if type(self["flag"]) == bytes: + self["flag"] = rh2s(self["flag"]) + if type(self["hash"]) == bytes: + self["hash"] = rh2s(self["hash"]) + if type(self["rawTx"]) == bytes: + self["rawTx"] = hexlify(self["rawTx"]).decode() + for i in self["vIn"]: + if type(self["vIn"][i]["txId"]) == bytes: + self["vIn"][i]["txId"] = rh2s(self["vIn"][i]["txId"]) + if type(self["vIn"][i]["scriptSig"]) == bytes: + self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode() + try: + t = list() + for w in self["vIn"][i]["txInWitness"]: + if type(w) == bytes: + w = hexlify(w).decode() + t.append(w) + self["vIn"][i]["txInWitness"] = t + self["vIn"][i]["txInWitnessAsm"] = [decode_script(ws, 1) for ws in + self["vIn"][i]["txInWitness"]] + self["vIn"][i]["txInWitnessOpcodes"] = [decode_script(ws) for ws in + self["vIn"][i]["txInWitness"]] + except: + pass + try: + if type(self["vIn"][i]["addressHash"]) == bytes: + self["vIn"][i]["addressHash"] = hexlify(self["vIn"][i]["addressHash"]).decode() + sh = True if self["vIn"][i]["nType"] in (1, 5) else False + witness_version = None if self["vIn"][i]["nType"] < 5 else 0 + self["vIn"][i]["address"] = hash_to_address(self["vIn"][i]["addressHash"], + self["testnet"], + sh, + witness_version) + except: + pass + if "scriptPubKey" in self["vIn"][i]: + if type(self["vIn"][i]["scriptPubKey"]) == bytes: + self["vIn"][i]["scriptPubKey"] = hexlify(self["vIn"][i]["scriptPubKey"]).decode() + self["vIn"][i]["scriptPubKeyOpcodes"] = decode_script(self["vIn"][i]["scriptPubKey"]) + self["vIn"][i]["scriptPubKeyAsm"] = decode_script(self["vIn"][i]["scriptPubKey"], 1) + if "redeemScript" in self["vIn"][i]: + if type(self["vIn"][i]["redeemScript"]) == bytes: + self["vIn"][i]["redeemScript"] = hexlify(self["vIn"][i]["redeemScript"]).decode() + self["vIn"][i]["redeemScriptOpcodes"] = decode_script(self["vIn"][i]["redeemScript"]) + self["vIn"][i]["redeemScriptAsm"] = decode_script(self["vIn"][i]["redeemScript"], 1) + if not self["coinbase"]: + if type(self["vIn"][i]["scriptSig"]) == bytes: + self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode() + self["vIn"][i]["scriptSigOpcodes"] = decode_script(self["vIn"][i]["scriptSig"]) + self["vIn"][i]["scriptSigAsm"] = decode_script(self["vIn"][i]["scriptSig"], 1) + + for i in self["vOut"]: + if type(self["vOut"][i]["scriptPubKey"]) == bytes: + self["vOut"][i]["scriptPubKey"] = hexlify(self["vOut"][i]["scriptPubKey"]).decode() + try: + if type(self["vOut"][i]["addressHash"]) == bytes: + self["vOut"][i]["addressHash"] = hexlify(self["vOut"][i]["addressHash"]).decode() + sh = True if self["vOut"][i]["nType"] in (1, 5) else False + witness_version = None if self["vOut"][i]["nType"] < 5 else 0 + self["vOut"][i]["address"] = hash_to_address(self["vOut"][i]["addressHash"], + self["testnet"], + sh, + witness_version) + except: + pass + self["vOut"][i]["scriptPubKeyOpcodes"] = decode_script(self["vOut"][i]["scriptPubKey"]) + self["vOut"][i]["scriptPubKeyAsm"] = decode_script(self["vOut"][i]["scriptPubKey"], 1) + if "data" in self: + if type(self["data"]) == bytes: + self["data"] = hexlify(self["data"]).decode() + return self
+ +
[docs] def encode(self): + """ + change Transaction object representation to "raw" bytes format, + all human readable part will be stripped. + + """ + if type(self["txId"]) == str: + self["txId"] = s2rh(self["txId"]) + if "flag" in self: + if type(self["flag"]) == str: + self["flag"] = s2rh(self["flag"]) + if type(self["hash"]) == str: + self["hash"] = s2rh(self["hash"]) + if type(self["rawTx"]) == str: + self["rawTx"] = unhexlify(self["rawTx"]) + + for i in self["vIn"]: + if type(self["vIn"][i]["txId"]) == str: + self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"]) + if type(self["vIn"][i]["scriptSig"]) == str: + self["vIn"][i]["scriptSig"] = unhexlify(self["vIn"][i]["scriptSig"]) + try: + t = list() + for w in self["vIn"][i]["txInWitness"]: + if type(w) == str: + w = unhexlify(w) + t.append(w) + self["vIn"][i]["txInWitness"] = t + if "txInWitnessAsm" in self["vIn"][i]: + del self["vIn"][i]["txInWitnessAsm"] + if "txInWitnessOpcodes" in self["vIn"][i]: + del self["vIn"][i]["txInWitnessOpcodes"] + except: + pass + try: + if type(self["vIn"][i]["addressHash"]) == str: + self["vIn"][i]["addressHash"] = unhexlify(self["vIn"][i]["addressHash"]) + if "address" in self["vIn"][i]: + del self["vIn"][i]["address"] + except: + pass + if "scriptSigAsm" in self["vIn"][i]: + del self["vIn"][i]["scriptSigAsm"] + if "scriptSigOpcodes" in self["vIn"][i]: + del self["vIn"][i]["scriptSigOpcodes"] + + for i in self["vOut"]: + if type(self["vOut"][i]["scriptPubKey"]) == str: + self["vOut"][i]["scriptPubKey"] = unhexlify(self["vOut"][i]["scriptPubKey"]) + try: + if type(self["vOut"][i]["addressHash"]) == str: + self["vOut"][i]["addressHash"] = unhexlify(self["vOut"][i]["addressHash"]) + if "address" in self["vOut"][i]: + del self["vOut"][i]["address"] + except: + pass + if "scriptPubKeyOpcodes" in self["vOut"][i]: + del self["vOut"][i]["scriptPubKeyOpcodes"] + if "scriptPubKeyAsm" in self["vOut"][i]: + del self["vOut"][i]["scriptPubKeyAsm"] + + if "data" in self: + if type(self["data"]) == str: + self["data"] = unhexlify(self["data"]) + self["format"] = "raw" + return self
+ + def get_stream(self, 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 + +
[docs] def serialize(self, segwit=True, hex=True): + """ + Get serialized transaction + + :param bool segwit: (optional) flag for segwit representation of serialized transaction, by + default True. + :param bool hex: (optional) if set to True return HEX encoded string, by default True. + :return str,bytes: serialized transaction in HEX or bytes. + """ + chunks = [] + chunks.append(struct.pack('<L', self["version"])) + if segwit and self["segwit"]: + chunks.append(b"\x00\x01") + chunks.append(int_to_var_int(len(self["vIn"]))) + for i in self["vIn"]: + if isinstance(self["vIn"][i]['txId'], bytes): + chunks.append(self["vIn"][i]['txId']) + else: + chunks.append(s2rh(self["vIn"][i]['txId'])) + chunks.append(struct.pack('<L', self["vIn"][i]['vOut'])) + if isinstance(self["vIn"][i]['scriptSig'], bytes): + chunks.append(int_to_var_int(len(self["vIn"][i]['scriptSig']))) + chunks.append(self["vIn"][i]['scriptSig']) + else: + chunks.append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2))) + chunks.append(unhexlify(self["vIn"][i]['scriptSig'])) + chunks.append(struct.pack('<L', self["vIn"][i]['sequence'])) + chunks.append(int_to_var_int(len(self["vOut"]))) + for i in self["vOut"]: + chunks.append(struct.pack('<Q', self["vOut"][i]['value'])) + if isinstance(self["vOut"][i]['scriptPubKey'], bytes): + chunks.append(int_to_var_int(len(self["vOut"][i]['scriptPubKey']))) + chunks.append(self["vOut"][i]['scriptPubKey']) + else: + chunks.append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2))) + chunks.append(unhexlify(self["vOut"][i]['scriptPubKey'])) + if segwit and self["segwit"]: + for i in self["vIn"]: + chunks.append(int_to_var_int(len(self["vIn"][i]['txInWitness']))) + for w in self["vIn"][i]['txInWitness']: + if isinstance(w, bytes): + chunks.append(int_to_var_int(len(w))) + chunks.append(w) + else: + chunks.append(int_to_var_int(int(len(w) / 2))) + chunks.append(unhexlify(w)) + chunks.append(struct.pack('<L', self['lockTime'])) + tx = b''.join(chunks) + return tx if not hex else hexlify(tx).decode()
+ +
[docs] def json(self): + """ + Get json transaction representation + + """ + try: + return json.dumps(self) + except: + pass + return json.dumps(self.decode())
+ + def add_input(self, tx_id=None, v_out=0, sequence=0xffffffff, + script_sig=b"", tx_in_witness=None, amount=None, + script_pub_key=None, address=None, private_key=None): + if tx_id is None: + tx_id = b"\x00" * 32 + v_out = 0xffffffff + assert v_out == 0xffffffff and sequence == 0xffffffff + assert not self["vIn"] + if type(tx_id) == str: + tx_id = s2rh(tx_id) + if type(script_sig) == str: + script_sig = unhexlify(script_sig) + assert type(tx_id) == bytes + assert len(tx_id) == 32 + assert type(v_out) == int + assert v_out <= 0xffffffff and v_out >= 0 + assert type(sequence) == int + assert sequence <= 0xffffffff and sequence >= 0 + assert type(script_sig) == bytes + assert len(script_sig) <= 520 + if private_key: + if type(private_key) != PrivateKey: + private_key = PrivateKey(private_key) + if amount: + assert type(amount) == int + assert amount >= 0 and amount <= MAX_AMOUNT + if tx_in_witness: + assert type(tx_in_witness) == list + l = 0 + witness = [] + for w in tx_in_witness: + if type(w) == str: + witness.append(unhexlify(w) if self["format"] == "raw" else w) + else: + witness.append(w if self["format"] == "raw" else unhexlify(w)) + l += 1 + len(w) + if len(w) >= 0x4c: + l += 1 + if len(w) > 0xff: + l += 1 + # witness script limit + assert l <= 10000 + if tx_id == b"\x00" * 32: + assert v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100 + self["coinbase"] = True + + # script_pub_key + if script_pub_key: + if type(script_pub_key) == str: + script_pub_key = unhexlify(script_pub_key) + type(script_pub_key) == bytes + if address is not None: + if type(address) == str: + net = True if address_net_type(address) == 'mainnet' else False + assert not net == self["testnet"] + script = address_to_script(address) + elif type(address) in (Address, ScriptAddress): + assert type(address) == Address + script = address_to_script(address.address) + if script_pub_key: + assert script_pub_key == script + else: + script_pub_key = script + + k = len(self["vIn"]) + self["vIn"][k] = dict() + self["vIn"][k]["vOut"] = v_out + self["vIn"][k]["sequence"] = sequence + if self["format"] == "raw": + self["vIn"][k]["txId"] = tx_id + self["vIn"][k]["scriptSig"] = script_sig + if script_pub_key: + self["vIn"][k]["scriptPubKey"] = script_pub_key + else: + self["vIn"][k]["txId"] = rh2s(tx_id) + self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode() + self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig) + self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1) + if script_pub_key: + self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() + if tx_in_witness: + self["segwit"] = True + self["vIn"][k]["txInWitness"] = witness + if amount: + self["vIn"][k]["value"] = amount + if private_key: + self["vIn"][k].private_key = private_key + self.__refresh__() + return self + + def add_output(self, amount, address=None, script_pub_key=None): + assert address is not None or script_pub_key is not None + assert not (address is None and script_pub_key is None) + assert type(amount) == int + assert amount >= 0 and amount <= MAX_AMOUNT + if script_pub_key: + if type(script_pub_key) == str: + script_pub_key = unhexlify(script_pub_key) + assert type(script_pub_key) == bytes + else: + if type(address) == Address: + address = address.address + script_pub_key = address_to_script(address) + + k = len(self["vOut"]) + self["vOut"][k] = dict() + self["vOut"][k]["value"] = amount + segwit = True if "segwit" in self else False + s = parse_script(script_pub_key, segwit) + self["vOut"][k]["nType"] = s["nType"] + self["vOut"][k]["type"] = s["type"] + + if self["format"] == "raw": + self["vOut"][k]["scriptPubKey"] = script_pub_key + if self["data"] is None: + if s["nType"] == 3: + self["data"] = s["data"] + if s["nType"] not in (3, 4, 7): + self["vOut"][k]["addressHash"] = s["addressHash"] + self["vOut"][k]["reqSigs"] = s["reqSigs"] + else: + self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() + if self["data"] is None: + if s["nType"] == 3: + self["data"] = hexlify(s["data"]).decode() + if s["nType"] not in (3, 4, 7): + self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode() + self["vOut"][k]["reqSigs"] = s["reqSigs"] + self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) + self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) + sh = True if self["vOut"][k]["nType"] in (1, 5) else False + witness_version = None if self["vOut"][k]["nType"] < 5 else 0 + if "addressHash" in self["vOut"][k]: + self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"], + self["testnet"], + sh, + witness_version) + self.__refresh__() + return self + + def del_output(self, n=None): + if not self["vOut"]: + return self + if n is None: + n = len(self["vOut"]) - 1 + new_out = dict() + c = 0 + for i in range(len(self["vOut"])): + if i != n: + new_out[c] = self["vOut"][i] + c += 1 + self["vOut"] = new_out + self.__refresh__() + return self + + def del_input(self, n): + if not self["vIn"]: + return self + if n is None: + n = len(self["vIn"]) - 1 + new_in = dict() + c = 0 + for i in range(len(self["vIn"])): + if i != n: + new_in[c] = self["vIn"][i] + c += 1 + self["vIn"] = new_in + self.__refresh__() + return self + + def sign_input(self, n, private_key=None, script_pub_key=None, redeem_script=None, sighash_type=SIGHASH_ALL): + if private_key is not None: + if private_key: + if type(private_key) != PrivateKey: + private_key_obj = PrivateKey(private_key) + public_key = PublicKey(private_key_obj).key + private_key = private_key_obj.key + else: + if "privateKey" not in self["vIn"][n]: return self + private_key = self["vIn"][n].private_key.key + public_key = PublicKey(self["vIn"][n].private_key).key - def encode(self): - if type(self["txId"]) == str: - self["txId"] = s2rh(self["txId"]) - if "flag" in self: - if type(self["flag"]) == str: - self["flag"] = s2rh(self["flag"]) - if type(self["hash"]) == str: - self["hash"] = s2rh(self["hash"]) - if type(self["rawTx"]) == str: - self["rawTx"] = unhexlify(self["rawTx"]) + if redeem_script: + if type(redeem_script) == str: + redeem_script = unhexlify(redeem_script).decode() + assert type(redeem_script) == bytes + script = redeem_script + else: + script = script_pub_key - for i in self["vIn"]: - if type(self["vIn"][i]["txId"]) == str: - self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"]) - if type(self["vIn"][i]["scriptSig"]) == str: - self["vIn"][i]["scriptSig"] = unhexlify(self["vIn"][i]["scriptSig"]) - try: - t = list() - for w in self["vIn"][i]["txInWitness"]: - if type(w) == str: - w = unhexlify(w) - t.append(w) - self["vIn"][i]["txInWitness"] = t - if "txInWitnessAsm" in self["vIn"][i]: - del self["vIn"][i]["txInWitnessAsm"] - if "txInWitnessOpcodes" in self["vIn"][i]: - del self["vIn"][i]["txInWitnessOpcodes"] - except: - pass - try: - if type(self["vIn"][i]["addressHash"]) == str: - self["vIn"][i]["addressHash"] = unhexlify(self["vIn"][i]["addressHash"]) - if "address" in self["vIn"][i]: - del self["vIn"][i]["address"] - except: - pass - if "scriptSigAsm" in self["vIn"][i]: - del self["vIn"][i]["scriptSigAsm"] - if "scriptSigOpcodes" in self["vIn"][i]: - del self["vIn"][i]["scriptSigOpcodes"] - - for i in self["vOut"]: - if type(self["vOut"][i]["scriptPubKey"]) == str: - self["vOut"][i]["scriptPubKey"] = unhexlify(self["vOut"][i]["scriptPubKey"]) - try: - if type(self["vOut"][i]["addressHash"]) == str: - self["vOut"][i]["addressHash"] = unhexlify(self["vOut"][i]["addressHash"]) - if "address" in self["vOut"][i]: - del self["vOut"][i]["address"] - except: - pass - if "scriptPubKeyOpcodes" in self["vOut"][i]: - del self["vOut"][i]["scriptPubKeyOpcodes"] - if "scriptPubKeyAsm" in self["vOut"][i]: - del self["vOut"][i]["scriptPubKeyAsm"] - - if "data" in self: - if type(self["data"]) == str: - self["data"] = unhexlify(self["data"]) - self["format"] = "raw" - return self - - def get_stream(self, 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 - - def serialize(self, segwit=True, hex=True): - chunks = [] - chunks.append(struct.pack('<L', self["version"])) - if segwit and self["segwit"]: - chunks.append(b"\x00\x01") - chunks.append(int_to_var_int(len(self["vIn"]))) - for i in self["vIn"]: - if type(self["vIn"][i]['txId']) == bytes: - chunks.append(self["vIn"][i]['txId']) - else: - chunks.append(s2rh(self["vIn"][i]['txId'])) - chunks.append(struct.pack('<L', self["vIn"][i]['vOut'])) - if type(self["vIn"][i]['scriptSig']) == bytes: - chunks.append(int_to_var_int(len(self["vIn"][i]['scriptSig']))) - chunks.append(self["vIn"][i]['scriptSig']) - else: - chunks.append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2))) - chunks.append(unhexlify(self["vIn"][i]['scriptSig'])) - chunks.append(struct.pack('<L', self["vIn"][i]['sequence'])) - chunks.append(int_to_var_int(len(self["vOut"]))) - for i in self["vOut"]: - chunks.append(struct.pack('<Q', self["vOut"][i]['value'])) - if type(self["vOut"][i]['scriptPubKey']) == bytes: - chunks.append(int_to_var_int(len(self["vOut"][i]['scriptPubKey']))) - chunks.append(self["vOut"][i]['scriptPubKey']) - else: - chunks.append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2))) - chunks.append(unhexlify(self["vOut"][i]['scriptPubKey'])) - if segwit and self["segwit"]: - for i in self["vIn"]: - chunks.append(int_to_var_int(len(self["vIn"][i]['txInWitness']))) - for w in self["vIn"][i]['txInWitness']: - if type(w) == bytes: - chunks.append(int_to_var_int(len(w))) - chunks.append(w) - else: - chunks.append(int_to_var_int(int(len(w) / 2))) - chunks.append(unhexlify(w)) - chunks.append(struct.pack('<L', self['lockTime'])) - tx = b''.join(chunks) - return tx if not hex else hexlify(tx).decode() - - def json(self): - try: - return json.dumps(self) - except: - pass - return json.dumps(self.decode()) - - def add_input(self, tx_id=None, v_out=0, sequence=0xffffffff, - script_sig=b"", tx_in_witness=None, amount=None, - script_pub_key=None, address=None, private_key=None): - if tx_id is None: - tx_id = b"\x00" * 32 - v_out = 0xffffffff - assert v_out == 0xffffffff and sequence == 0xffffffff - assert not self["vIn"] - if type(tx_id) == str: - tx_id = s2rh(tx_id) - if type(script_sig) == str: - script_sig = unhexlify(script_sig) - assert type(tx_id) == bytes - assert len(tx_id) == 32 - assert type(v_out) == int - assert v_out <= 0xffffffff and v_out >= 0 - assert type(sequence) == int - assert sequence <= 0xffffffff and sequence >= 0 - assert type(script_sig) == bytes - assert len(script_sig) <= 520 - if private_key: - if type(private_key) != PrivateKey: - private_key = PrivateKey(private_key) - if amount: - assert type(amount) == int - assert amount >= 0 and amount <= MAX_AMOUNT - if tx_in_witness: - assert type(tx_in_witness) == list - l = 0 - witness = [] - for w in tx_in_witness: - if type(w) == str: - witness.append(unhexlify(w) if self["format"] == "raw" else w) - else: - witness.append(w if self["format"] == "raw" else unhexlify(w)) - l += 1 + len(w) - if len(w) >= 0x4c: - l += 1 - if len(w) > 0xff: - l += 1 - # witness script limit - assert l <= 10000 - if tx_id == b"\x00" * 32: - assert v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100 - self["coinbase"] = True - - # script_pub_key - if script_pub_key: - if type(script_pub_key) == str: - script_pub_key = unhexlify(script_pub_key) - type(script_pub_key) == bytes - if address is not None: - if type(address) == str: - net = True if address_net_type(address) == 'mainnet' else False - assert not net == self["testnet"] - script = address_to_script(address) - elif type(address) in (Address, ScriptAddress): - assert type(address) == Address - script = address_to_script(address.address) - if script_pub_key: - assert script_pub_key == script - else: - script_pub_key = script - - k = len(self["vIn"]) - self["vIn"][k] = dict() - self["vIn"][k]["vOut"] = v_out - self["vIn"][k]["sequence"] = sequence - if self["format"] == "raw": - self["vIn"][k]["txId"] = tx_id - self["vIn"][k]["scriptSig"] = script_sig - if script_pub_key: - self["vIn"][k]["scriptPubKey"] = script_pub_key + sighash = self.sig_hash_input(n, script_pub_key=script, sighash_type=sighash_type) + if type(sighash) == str: + sighash = s2rh(sighash) + signature = sign_message(sighash, private_key, 0) + bytes([sighash_type]) + if redeem_script: + if self["vIn"][n]["scriptSig"]: + sig_script = self["vIn"][n]["scriptSig"] + if type(sig_script) == str: + sig_script = unhexlify(sig_script).decode() + sig_script = bytes([len(public_key)]) + public_key + sig_script + sig_script = bytes([len(signature)]) + signature + sig_script + else: + sig_script = bytes([len(signature)]) + signature + sig_script += bytes([len(public_key)]) + public_key + if len(redeem_script) <= 0x4b: + sig_script += bytes([len(redeem_script)]) + redeem_script + elif len(redeem_script) <= 0xff: + sig_script = BYTE_OPCODE["OP_PUSHDATA1"] + bytes([len(redeem_script)]) + redeem_script + elif len(redeem_script) <= 0xffff: + sig_script = BYTE_OPCODE["OP_PUSHDATA2"] + bytes([len(redeem_script)]) + redeem_script else: - self["vIn"][k]["txId"] = rh2s(tx_id) - self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode() - self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig) - self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1) - if script_pub_key: - self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() - if tx_in_witness: - self["segwit"] = True - self["vIn"][k]["txInWitness"] = witness - if amount: - self["vIn"][k]["value"] = amount - if private_key: - self["vIn"][k].private_key = private_key - self.__refresh__() - return self + sig_script = BYTE_OPCODE["OP_PUSHDATA4"] + bytes([len(redeem_script)]) + redeem_script + else: + sig_script = bytes([len(signature)]) + signature + sig_script += bytes([len(public_key)]) + public_key + if self["format"] == "raw": + self["vIn"][n]["scriptSig"] = sig_script + else: + self["vIn"][n]["scriptSig"] = hexlify(sig_script).decode() + self["vIn"][n]["scriptSigOpcodes"] = decode_script(sig_script) + self["vIn"][n]["scriptSigAsm"] = decode_script(sig_script, 1) + self.__refresh__() + return self - def add_output(self, amount, address=None, script_pub_key=None): - assert address is not None or script_pub_key is not None - assert not (address is None and script_pub_key is None) - assert type(amount) == int - assert amount >= 0 and amount <= MAX_AMOUNT - if script_pub_key: - if type(script_pub_key) == str: - script_pub_key = unhexlify(script_pub_key) - assert type(script_pub_key) == bytes + def sig_hash_input(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL): + # check n + assert n >= 0 + tx_in_count = len(self["vIn"]) + + if n >= tx_in_count: + if self["format"] == "raw": + return b'\x01' + b'\x00' * 31 + else: + return rh2s(b'\x01' + b'\x00' * 31) + + # check script_pub_key for input + if script_pub_key is not None: + script_code = script_pub_key + else: + assert "scriptPubKey" in self["vIn"][n] + script_code = self["vIn"][n]["scriptPubKey"] + if type(script_code) == str: + script_code = unhexlify(script_code) + assert type(script_code) == bytes + + # remove opcode separators + script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) + preimage = bytearray() + + if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))): + if self["format"] == "raw": + return b'\x01' + b'\x00' * 31 + else: + return rh2s(b'\x01' + b'\x00' * 31) + + preimage += struct.pack('<L', self["version"]) + preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(tx_in_count) + + for i in self["vIn"]: + # skip all other inputs for SIGHASH_ANYONECANPAY case + if (sighash_type & SIGHASH_ANYONECANPAY) and (n != i): + continue + sequence = self["vIn"][i]["sequence"] + if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): + sequence = 0 + if (sighash_type & 31) == SIGHASH_NONE and (n != i): + sequence = 0 + tx_id = self["vIn"][i]["txId"] + if type(tx_id) == str: + tx_id = s2rh(tx_id) + input = tx_id + struct.pack('<L', self["vIn"][i]["vOut"]) + if n == i: + input += int_to_var_int(len(script_code)) + script_code + input += struct.pack('<L', sequence) + else: + input += b'\x00' + struct.pack('<L', sequence) + preimage += input + + if (sighash_type & 31) == SIGHASH_NONE: + preimage += b'\x00' + else: + if (sighash_type & 31) == SIGHASH_SINGLE: + preimage += int_to_var_int(n + 1) + else: + preimage += int_to_var_int(len(self["vOut"])) + + if (sighash_type & 31) != SIGHASH_NONE: + for i in self["vOut"]: + script_pub_key = self["vOut"][i]["scriptPubKey"] + if type(self["vOut"][i]["scriptPubKey"]) == str: + script_pub_key = unhexlify(script_pub_key) + if i > n and (sighash_type & 31) == SIGHASH_SINGLE: + continue + if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): + preimage += b'\xff' * 8 + b'\x00' else: - if type(address) == Address: - address = address.address - script_pub_key = address_to_script(address) + preimage += self["vOut"][i]["value"].to_bytes(8, 'little') + preimage += int_to_var_int(len(script_pub_key)) + script_pub_key - k = len(self["vOut"]) - self["vOut"][k] = dict() - self["vOut"][k]["value"] = amount - segwit = True if "segwit" in self else False - s = parse_script(script_pub_key, segwit) - self["vOut"][k]["nType"] = s["nType"] - self["vOut"][k]["type"] = s["type"] + preimage += self["lockTime"].to_bytes(4, 'little') + preimage += struct.pack(b"<i", sighash_type) + return double_sha256(preimage) if self["format"] == "raw" else rh2s(double_sha256(preimage)) - if self["format"] == "raw": - self["vOut"][k]["scriptPubKey"] = script_pub_key - if self["data"] is None: - if s["nType"] == 3: - self["data"] = s["data"] - if s["nType"] not in (3, 4, 7): - self["vOut"][k]["addressHash"] = s["addressHash"] - self["vOut"][k]["reqSigs"] = s["reqSigs"] - else: - self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() - if self["data"] is None: - if s["nType"] == 3: - self["data"] = hexlify(s["data"]).decode() - if s["nType"] not in (3, 4, 7): - self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode() - self["vOut"][k]["reqSigs"] = s["reqSigs"] - self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) - self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) - sh = True if self["vOut"][k]["nType"] in (1, 5) else False - witness_version = None if self["vOut"][k]["nType"] < 5 else 0 - if "addressHash" in self["vOut"][k]: - self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"], - self["testnet"], - sh, - witness_version) - self.__refresh__() - return self + def __refresh__(self): + if not self["vOut"] or not self["vIn"]: + return + no_segwit_view = self.serialize(segwit=False, hex=False) + self["txId"] = double_sha256(no_segwit_view) + self["rawTx"] = self.serialize(segwit=True, hex=False) + self["hash"] = double_sha256(self["rawTx"]) - def del_output(self, n=None): - if not self["vOut"]: - return self - if n is None: - n = len(self["vOut"]) - 1 - new_out = dict() - c = 0 - for i in range(len(self["vOut"])): - if i != n: - new_out[c] = self["vOut"][i] - c += 1 - self["vOut"] = new_out - self.__refresh__() - return self + self["size"] = len(self["rawTx"]) + self["bSize"] = len(no_segwit_view) + self["weight"] = self["bSize"] * 3 + self["size"] + self["vSize"] = math.ceil(self["weight"] / 4) - def del_input(self, n): - if not self["vIn"]: - return self - if n is None: - n = len(self["vIn"]) - 1 - new_in = dict() - c = 0 - for i in range(len(self["vIn"])): - if i != n: - new_in[c] = self["vIn"][i] - c += 1 - self["vIn"] = new_in - self.__refresh__() - return self + if self["format"] != "raw": + self["txId"] = rh2s(self["txId"]) + self["hash"] = rh2s(self["hash"]) + self["rawTx"] = hexlify(self["rawTx"]).decode() - def sign_input(self, n, private_key=None, script_pub_key=None, redeem_script=None, sighash_type=SIGHASH_ALL): - if private_key is not None: - if private_key: - if type(private_key) != PrivateKey: - private_key_obj = PrivateKey(private_key) - public_key = PublicKey(private_key_obj).key - private_key = private_key_obj.key - else: - if "privateKey" not in self["vIn"][n]: - return self - private_key = self["vIn"][n].private_key.key - public_key = PublicKey(self["vIn"][n].private_key).key + input_sum = 0 + for i in self["vIn"]: + if "value" in self["vIn"][i]: + input_sum += self["vIn"][i]["value"] + else: + input_sum = None + break - if redeem_script: - if type(redeem_script) == str: - redeem_script = unhexlify(redeem_script).decode() - assert type(redeem_script) == bytes - script = redeem_script - else: - script = script_pub_key - - sighash = self.sig_hash_input(n, script_pub_key=script, sighash_type=sighash_type) - if type(sighash) == str: - sighash = s2rh(sighash) - signature = sign_message(sighash, private_key, 0) + bytes([sighash_type]) - if redeem_script: - if self["vIn"][n]["scriptSig"]: - sig_script = self["vIn"][n]["scriptSig"] - if type(sig_script) == str: - sig_script = unhexlify(sig_script).decode() - sig_script = bytes([len(public_key)]) + public_key + sig_script - sig_script = bytes([len(signature)]) + signature + sig_script - else: - sig_script = bytes([len(signature)]) + signature - sig_script += bytes([len(public_key)]) + public_key - if len(redeem_script) <= 0x4b: - sig_script += bytes([len(redeem_script)]) + redeem_script - elif len(redeem_script) <= 0xff: - sig_script = BYTE_OPCODE["OP_PUSHDATA1"] + bytes([len(redeem_script)]) + redeem_script - elif len(redeem_script) <= 0xffff: - sig_script = BYTE_OPCODE["OP_PUSHDATA2"] + bytes([len(redeem_script)]) + redeem_script - else: - sig_script = BYTE_OPCODE["OP_PUSHDATA4"] + bytes([len(redeem_script)]) + redeem_script - else: - sig_script = bytes([len(signature)]) + signature - sig_script += bytes([len(public_key)]) + public_key - if self["format"] == "raw": - self["vIn"][n]["scriptSig"] = sig_script - else: - self["vIn"][n]["scriptSig"] = hexlify(sig_script).decode() - self["vIn"][n]["scriptSigOpcodes"] = decode_script(sig_script) - self["vIn"][n]["scriptSigAsm"] = decode_script(sig_script, 1) - self.__refresh__() - return self - - def sig_hash_input(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL): - # check n - assert n >= 0 - tx_in_count = len(self["vIn"]) - - if n >= tx_in_count: - if self["format"] == "raw": - return b'\x01' + b'\x00' * 31 - else: - return rh2s(b'\x01' + b'\x00' * 31) - - # check script_pub_key for input - if script_pub_key is not None: - script_code = script_pub_key - else: - assert "scriptPubKey" in self["vIn"][n] - script_code = self["vIn"][n]["scriptPubKey"] - if type(script_code) == str: - script_code = unhexlify(script_code) - assert type(script_code) == bytes - - # remove opcode separators - script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) - preimage = bytearray() - - if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))): - if self["format"] == "raw": - return b'\x01' + b'\x00' * 31 - else: - return rh2s(b'\x01' + b'\x00' * 31) - - preimage += struct.pack('<L', self["version"]) - preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(tx_in_count) - - for i in self["vIn"]: - # skip all other inputs for SIGHASH_ANYONECANPAY case - if (sighash_type & SIGHASH_ANYONECANPAY) and (n != i): - continue - sequence = self["vIn"][i]["sequence"] - if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): - sequence = 0 - if (sighash_type & 31) == SIGHASH_NONE and (n != i): - sequence = 0 - tx_id = self["vIn"][i]["txId"] - if type(tx_id) == str: - tx_id = s2rh(tx_id) - input = tx_id + struct.pack('<L', self["vIn"][i]["vOut"]) - if n == i: - input += int_to_var_int(len(script_code)) + script_code - input += struct.pack('<L', sequence) - else: - input += b'\x00' + struct.pack('<L', sequence) - preimage += input - - if (sighash_type & 31) == SIGHASH_NONE: - preimage += b'\x00' - else: - if (sighash_type & 31) == SIGHASH_SINGLE: - preimage += int_to_var_int(n + 1) - else: - preimage += int_to_var_int(len(self["vOut"])) - - if (sighash_type & 31) != SIGHASH_NONE: - for i in self["vOut"]: - script_pub_key = self["vOut"][i]["scriptPubKey"] - if type(self["vOut"][i]["scriptPubKey"]) == str: - script_pub_key = unhexlify(script_pub_key) - if i > n and (sighash_type & 31) == SIGHASH_SINGLE: - continue - if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): - preimage += b'\xff' * 8 + b'\x00' - else: - preimage += self["vOut"][i]["value"].to_bytes(8, 'little') - preimage += int_to_var_int(len(script_pub_key)) + script_pub_key - - preimage += self["lockTime"].to_bytes(4, 'little') - preimage += struct.pack(b"<i", sighash_type) - return double_sha256(preimage) if self["format"] == "raw" else rh2s(double_sha256(preimage)) - - def __refresh__(self): - if not self["vOut"] or not self["vIn"]: - return - no_segwit_view = self.serialize(segwit=False, hex=False) - self["txId"] = double_sha256(no_segwit_view) - self["rawTx"] = self.serialize(segwit=True, hex=False) - self["hash"] = double_sha256(self["rawTx"]) - - self["size"] = len(self["rawTx"]) - self["bSize"] = len(no_segwit_view) - self["weight"] = self["bSize"] * 3 + self["size"] - self["vSize"] = math.ceil(self["weight"] / 4) - - if self["format"] != "raw": - self["txId"] = rh2s(self["txId"]) - self["hash"] = rh2s(self["hash"]) - self["rawTx"] = hexlify(self["rawTx"]).decode() - - input_sum = 0 - for i in self["vIn"]: - if "value" in self["vIn"][i]: - input_sum += self["vIn"][i]["value"] - else: - input_sum = None - break - - output_sum = 0 - for i in self["vOut"]: - if "value" in self["vOut"][i]: - output_sum += self["vOut"][i]["value"] - else: - output_sum = None - break - self["amount"] = output_sum - if output_sum and input_sum: - self["fee"] = input_sum - output_sum - else: - self["fee"] = None
+ output_sum = 0 + for i in self["vOut"]: + if "value" in self["vOut"][i]: + output_sum += self["vOut"][i]["value"] + else: + output_sum = None + break + self["amount"] = output_sum + if output_sum and input_sum: + self["fee"] = input_sum - output_sum + else: + self["fee"] = None diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt index 8ac9e21..7dd0afd 100644 --- a/docs/build/html/_sources/index.rst.txt +++ b/docs/build/html/_sources/index.rst.txt @@ -44,7 +44,7 @@ Usage example:: import pybtc a = pybtc.Address() print(a.address) - print(a.private_key.wif()) + print(a.private_key.wif) diff --git a/docs/build/html/_sources/transaction.rst.txt b/docs/build/html/_sources/transaction.rst.txt index ac2360c..1da13cb 100644 --- a/docs/build/html/_sources/transaction.rst.txt +++ b/docs/build/html/_sources/transaction.rst.txt @@ -6,6 +6,7 @@ The class for creating transaction. .. autoclass:: pybtc.Transaction - :members: + :members: + diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html index cf68b77..4061c25 100644 --- a/docs/build/html/genindex.html +++ b/docs/build/html/genindex.html @@ -41,9 +41,11 @@ | B | C | D + | E | G | H | I + | J | K | M | P @@ -113,6 +115,8 @@

D

@@ -124,6 +128,14 @@
+

E

+ + +
+

G

    @@ -174,6 +186,14 @@
+

J

+ + +
+

K

    @@ -254,6 +274,8 @@
  • script_to_hash() (in module pybtc) +
  • +
  • serialize() (pybtc.Transaction method)
  • sign_message() (in module pybtc)
  • diff --git a/docs/build/html/index.html b/docs/build/html/index.html index d1ff280..8570cf8 100644 --- a/docs/build/html/index.html +++ b/docs/build/html/index.html @@ -59,7 +59,7 @@
    import pybtc
     a = pybtc.Address()
     print(a.address)
    -print(a.private_key.wif())
    +print(a.private_key.wif)
     
    diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv index 7ecf781..b86be7d 100644 Binary files a/docs/build/html/objects.inv and b/docs/build/html/objects.inv differ diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js index dc383c8..c10b989 100644 --- a/docs/build/html/searchindex.js +++ b/docs/build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["address","block","classes","contributing","examples","functional","index","installation","transaction"],envversion:53,filenames:["address.rst","block.rst","classes.rst","contributing.rst","examples.rst","functional.rst","index.rst","installation.rst","transaction.rst"],objects:{"pybtc.Address":{address:[0,1,1,""],hash:[0,1,1,""],hash_hex:[0,1,1,""],private_key:[0,1,1,""],public_key:[0,1,1,""],redeem_script:[0,1,1,""],redeem_script_hex:[0,1,1,""],script_hash:[0,1,1,""],testnet:[0,1,1,""],type:[0,1,1,""],witness_version:[0,1,1,""]},"pybtc.PrivateKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""],wif:[0,1,1,""]},"pybtc.PublicKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""]},pybtc:{Address:[0,0,1,""],Block:[1,0,1,""],PrivateKey:[0,0,1,""],PublicKey:[0,0,1,""],Transaction:[8,0,1,""],address_to_hash:[5,2,1,""],address_to_script:[5,2,1,""],address_type:[5,2,1,""],bits_to_difficulty:[5,2,1,""],bits_to_target:[5,2,1,""],bytes_needed:[5,2,1,""],bytes_to_int:[5,2,1,""],c_int_len:[5,2,1,""],c_int_to_int:[5,2,1,""],create_private_key:[5,2,1,""],decode_script:[5,2,1,""],delete_from_script:[5,2,1,""],difficulty_to_target:[5,2,1,""],get_var_int_len:[5,2,1,""],hash_to_address:[5,2,1,""],int_to_bytes:[5,2,1,""],int_to_c_int:[5,2,1,""],int_to_var_int:[5,2,1,""],is_address_valid:[5,2,1,""],is_public_key_valid:[5,2,1,""],is_valid_signature_encoding:[5,2,1,""],is_wif_valid:[5,2,1,""],merkle_branches:[5,2,1,""],merkle_root:[5,2,1,""],merkleroot_from_branches:[5,2,1,""],parse_script:[5,2,1,""],private_key_to_wif:[5,2,1,""],private_to_public_key:[5,2,1,""],public_key_to_address:[5,2,1,""],read_var_int:[5,2,1,""],read_var_list:[5,2,1,""],reverse_hash:[5,2,1,""],rh2s:[5,2,1,""],s2rh:[5,2,1,""],script_to_hash:[5,2,1,""],sign_message:[5,2,1,""],target_to_difficulty:[5,2,1,""],var_int_len:[5,2,1,""],var_int_to_int:[5,2,1,""],verify_signature:[5,2,1,""],wif_to_private_key:[5,2,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","attribute","Python attribute"],"2":["py","function","Python function"]},objtypes:{"0":"py:class","1":"py:attribute","2":"py:function"},terms:{"03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af":4,"0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f":4,"17mxwxxzrmj1njjzdszzbw9ursaradeuat":4,"1chpkurzfhdculkanhcc3ra9kfxm2lrguw":4,"3bqeq3xql6azmk3bxnyr8vxgxutog63j4t":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381hwuhvpgirjto9ztnr":4,"boolean":[0,5],"byte":[0,5],"case":[0,4,5],"class":[0,1,4,6,8],"default":[0,4,5],"float":5,"function":6,"import":[4,6],"new":[0,5],"public":[0,4,6,7],"return":5,"true":[0,4,5],"try":3,For:0,NOT:5,The:[0,1,4,6,7,8],Use:5,Using:5,abil:4,accord:[0,5],acord:6,add:3,address:[2,6],address_hash:5,address_net_typ:4,address_to_hash:5,address_to_script:5,address_typ:[0,4,5],addresshash:5,adopt:4,against:3,aiohttp:3,aleksei:6,all:3,allow:0,alreadi:[0,4],ani:7,asm:5,avail:6,avoid:5,backward:5,base58:[0,5],base:[0,5],base_byt:5,basic:6,bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5:4,bech32:[0,5],big:5,bip141:6,bip32:6,bip39:6,bip44:6,bit:5,bitap:7,bitcoin:[0,6],bits_to_difficulti:5,bits_to_target:5,block:[2,4,6],branch:[3,5],bug:6,bui:4,button:3,byteord:5,bytes_need:5,bytes_to_int:5,bytesio:5,c_int_len:5,c_int_to_int:5,calcul:5,can:[4,7],capac:4,chang:[3,5],check:5,choic:7,clone:[3,7],coinbas:5,coinbase_hash:5,collect:0,com:7,command:7,commit:3,compat:5,compatibilitylegaci:5,compres:5,compress:[0,4,5],comress:0,constructor:6,contain:0,continu:6,contribut:6,contributor:6,control:4,convert:5,copi:7,correspond:[0,5],cost:4,count:5,cover:7,coverag:6,creat:[0,1,5,6,8],create_private_kei:[4,5],current:6,data:5,data_typ:5,decod:[5,8],decode_script:5,delete_from_script:5,depend:5,deprec:5,der:5,deseri:6,determin:0,determinist:6,develop:6,dictionari:5,difficulti:6,difficulty_to_target:5,document:7,don:[3,7],easili:7,ecdsa:5,emb:7,encod:[0,6],exampl:6,except:4,expand:4,fals:[0,4,5,8],fee:4,feel:6,file:[3,6],first:7,flag:[0,5],folder:3,fork:3,format:[0,4,5],found:6,free:6,freeli:6,from:[0,3,5,6],fromat:5,fund:5,futur:5,gener:6,get:5,get_var_int_len:5,git:7,github:[3,6,7],given:5,good:3,gpl:6,guid:7,hard:3,has:0,hash160:5,hash:[0,6],hash_hex:0,hash_str:5,hash_to_address:5,have:[0,3,4,6,7],hex:[0,4,5],hierarch:6,host:6,human:5,ignor:0,implement:[0,4,5,6],imporv:6,improv:6,initi:[0,6],insid:5,instanc:0,instans:0,instruct:6,int_to_byt:5,int_to_c_int:5,int_to_var_int:5,integ:5,intp:3,is_address_valid:[4,5],is_public_key_valid:5,is_valid_signature_encod:5,is_wif_valid:[4,5],issu:6,jto9ztnr:4,karpov:6,karybkin:6,kei:0,kyvzyvdzwd4jspft4wxwjg53as227zt2qiwbmticzeusjiwvbeqi:4,l5xkga2xehcinwepmyiabs1bqqux8av5dgvqcprtvjc3zcr5sxu:4,legaci:[4,5],len:5,length:[0,5],librari:7,link:3,list:5,littl:5,locat:3,locktim:8,log:3,loss:5,mainnet:4,make:3,master:3,mean:0,menu:3,merkl:6,merkle_branch:5,merkle_root:5,merkleroot_from_branch:5,messag:5,mine:6,miner:4,mnemon:6,mpr4hdfu269yxgztpvysd21gtpvdxptmh6:4,msg:5,must:5,nativ:4,necessari:5,need:[4,5],network:[0,5],non:4,none:[0,4,5,8],ntype:5,num:5,numer:5,object:[0,4],onc:7,one:0,onli:[0,5],opcod:5,open:3,option:[0,5],order:[3,5,6],own:[3,7],p2pkh:[0,4,5,6],p2sh:[0,5,6],p2sh_p2wpkh:[0,4,5],p2wpkh:[0,4,5,6],p2wsh:[0,5,6],packag:6,page:3,paradigm:5,paramet:[0,5],pars:5,parse_script:5,part:7,pass:3,perfom:6,pip:6,pleas:[3,6],pool:6,potenti:5,press:3,pretti:3,primit:[5,6],print:6,privat:[0,4,6],private_kei:[0,4,5,6],private_key_to_wif:5,private_to_public_kei:[4,5],privatekei:0,process:7,program:[0,5],project:6,properli:7,provid:0,pub:4,pub_kei:5,pubkei:[0,4,5,6],public_kei:[0,4],public_key_to_address:[4,5],publickei:[0,4],pull:[3,6],pure:6,pwpkh:[0,6],pybtc:[0,1,4,5,7,8],python3:7,python:[6,7],random:0,raw:5,raw_hash:5,raw_tx:8,read:5,read_var_int:5,read_var_list:5,readabl:5,recent:6,recogn:5,recommend:[0,4],record:3,redeeem:0,redeem_script:0,redeem_script_hex:0,reduc:4,refer:6,remov:5,repo:3,repositori:7,represent:5,reqsig:5,request:[3,6],requir:5,result:5,revers:5,reverse_hash:5,rh2:5,right:3,ripemd160:5,root:6,run:7,s2rh:5,script:[0,6],script_hash:[0,5],script_to_hash:5,secp256k1:[5,6],segreg:6,segwit:[0,4,5],send:6,set:[0,4,5],setup:7,sha256:5,sig:5,sign:5,sign_messag:5,signatur:6,simpl:7,simpli:7,site:7,softwar:7,some:6,sourc:[0,1,5,8],specifi:[0,5],sript:5,start:5,step:7,straightforward:3,stream:5,string:[0,5],sub_script:5,subscript:5,suggest:6,support:[0,4,6],sure:3,target:5,target_to_difficulti:5,termin:7,test:6,testnet:[0,4,5,8],thi:[0,4,5,6,7],through:7,tool:[4,6],tracker:6,traget:5,transact:[1,2,5,6],transacton:5,tx_format:8,tx_hash_list:5,type:[0,4,5,6],uncompress:[4,5],upper:3,usag:[4,6],use:[0,4,5],user:5,using:[5,7],valid:5,valu:5,var_int_len:5,var_int_to_int:5,variabl:5,verifi:5,verify_signatur:5,version:[0,5,6,8],vriabl:5,wallet:6,web:3,which:[4,5],wif:[0,4,5,6],wif_to_private_kei:5,wit:[0,4,5,6],witness_vers:[0,4,5],work:[0,4],workflow:3,wors:3,written:6,you:[0,4,6,7],your:[3,4,7]},titles:["Addresses","Blocks","Reference","Contributing","Examples","Pure functions reference","Welcome to PYBTC","Installation","Transactions"],titleterms:{"function":[4,5],"new":6,"public":5,address:[0,4,5],author:6,block:1,code:[6,7],content:6,contribut:3,contributor:3,coverag:3,creat:4,depend:6,difficulti:5,encod:5,exampl:4,featur:6,from:[4,7],get:[4,6,7],hash:5,instal:[6,7],instruct:3,kei:[4,5,6],librari:6,licens:6,merkl:5,packag:7,pip:7,privat:5,pure:[4,5],pybtc:6,quick:6,refer:[2,5],root:5,script:[4,5],signatur:5,sourc:[6,7],start:6,tabl:6,test:3,tool:5,transact:8,welcom:6,what:6}}) \ No newline at end of file +Search.setIndex({docnames:["address","block","classes","contributing","examples","functional","index","installation","transaction"],envversion:53,filenames:["address.rst","block.rst","classes.rst","contributing.rst","examples.rst","functional.rst","index.rst","installation.rst","transaction.rst"],objects:{"pybtc.Address":{address:[0,1,1,""],hash:[0,1,1,""],hash_hex:[0,1,1,""],private_key:[0,1,1,""],public_key:[0,1,1,""],redeem_script:[0,1,1,""],redeem_script_hex:[0,1,1,""],script_hash:[0,1,1,""],testnet:[0,1,1,""],type:[0,1,1,""],witness_version:[0,1,1,""]},"pybtc.PrivateKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""],wif:[0,1,1,""]},"pybtc.PublicKey":{compressed:[0,1,1,""],hex:[0,1,1,""],key:[0,1,1,""],testnet:[0,1,1,""]},"pybtc.Transaction":{decode:[8,2,1,""],encode:[8,2,1,""],json:[8,2,1,""],serialize:[8,2,1,""]},pybtc:{Address:[0,0,1,""],Block:[1,0,1,""],PrivateKey:[0,0,1,""],PublicKey:[0,0,1,""],Transaction:[8,0,1,""],address_to_hash:[5,3,1,""],address_to_script:[5,3,1,""],address_type:[5,3,1,""],bits_to_difficulty:[5,3,1,""],bits_to_target:[5,3,1,""],bytes_needed:[5,3,1,""],bytes_to_int:[5,3,1,""],c_int_len:[5,3,1,""],c_int_to_int:[5,3,1,""],create_private_key:[5,3,1,""],decode_script:[5,3,1,""],delete_from_script:[5,3,1,""],difficulty_to_target:[5,3,1,""],get_var_int_len:[5,3,1,""],hash_to_address:[5,3,1,""],int_to_bytes:[5,3,1,""],int_to_c_int:[5,3,1,""],int_to_var_int:[5,3,1,""],is_address_valid:[5,3,1,""],is_public_key_valid:[5,3,1,""],is_valid_signature_encoding:[5,3,1,""],is_wif_valid:[5,3,1,""],merkle_branches:[5,3,1,""],merkle_root:[5,3,1,""],merkleroot_from_branches:[5,3,1,""],parse_script:[5,3,1,""],private_key_to_wif:[5,3,1,""],private_to_public_key:[5,3,1,""],public_key_to_address:[5,3,1,""],read_var_int:[5,3,1,""],read_var_list:[5,3,1,""],reverse_hash:[5,3,1,""],rh2s:[5,3,1,""],s2rh:[5,3,1,""],script_to_hash:[5,3,1,""],sign_message:[5,3,1,""],target_to_difficulty:[5,3,1,""],var_int_len:[5,3,1,""],var_int_to_int:[5,3,1,""],verify_signature:[5,3,1,""],wif_to_private_key:[5,3,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","attribute","Python attribute"],"2":["py","method","Python method"],"3":["py","function","Python function"]},objtypes:{"0":"py:class","1":"py:attribute","2":"py:method","3":"py:function"},terms:{"03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af":4,"0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f":4,"17mxwxxzrmj1njjzdszzbw9ursaradeuat":4,"1chpkurzfhdculkanhcc3ra9kfxm2lrguw":4,"3bqeq3xql6azmk3bxnyr8vxgxutog63j4t":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381":4,"5jw8dy1ubrd35xup6ed6klefa4ajfbx381hwuhvpgirjto9ztnr":4,"boolean":[0,5,8],"byte":[0,5,8],"case":[0,4,5],"class":[0,1,4,6,8],"default":[0,4,5,8],"float":5,"function":6,"import":[4,6],"int":8,"new":[0,5,8],"public":[0,4,6,7],"return":[5,8],"true":[0,4,5,8],"try":3,For:0,NOT:5,The:[0,1,4,6,7,8],Use:5,Using:5,abil:4,accord:[0,5],acord:6,add:3,address:[2,6,8],address_hash:5,address_net_typ:4,address_to_hash:5,address_to_script:5,address_typ:[0,4,5],addresshash:5,adopt:4,against:3,aiohttp:3,aleksei:6,all:[3,8],allow:0,alreadi:[0,4],also:[],ani:7,asm:[5,8],avail:6,avoid:5,backward:5,base58:[0,5],base68:8,base:[0,5],base_byt:5,basic:6,bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5:4,bech32:[0,5,8],best:8,big:5,bip141:6,bip32:6,bip39:6,bip44:6,bit:5,bitap:7,bitcoin:[0,6],bits_to_difficulti:5,bits_to_target:5,block:[2,4,6],bool:8,branch:[3,5],bug:6,bui:4,button:3,byteord:5,bytes_need:5,bytes_to_int:5,bytesio:5,c_int_len:5,c_int_to_int:5,calcul:5,can:[4,7],capac:4,chang:[3,5,8],check:5,choic:7,clear:[],clone:[3,7],coinbas:5,coinbase_hash:5,collect:0,com:7,command:7,commit:3,compat:5,compatibilitylegaci:5,compres:5,compress:[0,4,5],comress:0,constructor:6,contain:0,continu:6,contribut:6,contributor:6,control:4,convert:5,copi:7,correspond:[0,5],cost:4,count:5,cover:7,coverag:6,creat:[0,1,5,6,8],create_private_kei:[4,5],current:6,data:5,data_typ:5,decod:[5,8],decode_script:5,delete_from_script:5,depend:5,deprec:5,der:5,deseri:6,determin:0,determinist:6,develop:6,dict:[],dictionari:5,difficulti:6,difficulty_to_target:5,document:7,doe:[],don:[3,7],easili:7,ecdsa:5,either:[],els:[],emb:7,empti:8,encod:[0,6,8],equal:[],exampl:6,except:4,expand:4,fals:[0,4,5,8],fee:4,feel:6,file:[3,6],first:7,flag:[0,5,8],folder:3,follow:[],fork:3,format:[0,4,5,8],found:6,free:6,freeli:6,from:[0,3,5,6,8],fromat:5,fromkei:[],fund:5,futur:5,gener:6,get:[5,8],get_var_int_len:5,git:7,github:[3,6,7],given:5,good:3,gpl:6,guid:7,hard:3,has:0,hash160:5,hash:[0,6],hash_hex:0,hash_str:5,hash_to_address:5,have:[0,3,4,6,7],hex:[0,4,5,8],hierarch:6,host:6,human:[5,8],ignor:0,implement:[0,4,5,6],imporv:6,improv:6,initi:[0,6],insid:5,instanc:0,instans:0,instruct:6,int_to_byt:5,int_to_c_int:5,int_to_var_int:5,integ:5,intp:3,is_address_valid:[4,5],is_public_key_valid:5,is_valid_signature_encod:5,is_wif_valid:[4,5],issu:6,item:[],iter:[],json:8,jto9ztnr:4,karpov:6,karybkin:6,kei:0,keyerror:[],kyvzyvdzwd4jspft4wxwjg53as227zt2qiwbmticzeusjiwvbeqi:4,l5xkga2xehcinwepmyiabs1bqqux8av5dgvqcprtvjc3zcr5sxu:4,lack:[],legaci:[4,5],len:5,length:[0,5],librari:7,like:[],link:3,list:5,littl:5,locat:3,lock:8,lock_tim:8,locktim:[],log:3,loss:5,mainnet:4,make:3,master:3,mean:[0,8],menu:3,merkl:6,merkle_branch:5,merkle_root:5,merkleroot_from_branch:5,messag:5,method:[],mine:6,miner:4,mnemon:6,mpr4hdfu269yxgztpvysd21gtpvdxptmh6:4,msg:5,must:5,nativ:4,necessari:5,need:[4,5],network:[0,5],non:4,none:[0,4,5,8],ntype:5,num:5,numer:5,object:[0,4,8],onc:7,one:0,onli:[0,5],opcod:[5,8],open:3,option:[0,5,8],order:[3,5,6],otherwis:[],own:[3,7],p2pkh:[0,4,5,6],p2sh:[0,5,6],p2sh_p2wpkh:[0,4,5],p2wpkh:[0,4,5,6],p2wsh:[0,5,6],packag:6,page:3,pair:[],paradigm:5,paramet:[0,5,8],pars:5,parse_script:5,part:[7,8],pass:3,perfom:6,perform:8,pip:6,pleas:[3,6],pool:6,pop:[],popitem:[],potenti:5,present:[],press:3,pretti:3,primit:[5,6],print:6,privat:[0,4,6],private_kei:[0,4,5,6],private_key_to_wif:5,private_to_public_kei:[4,5],privatekei:0,process:7,program:[0,5],project:6,properli:7,properti:8,provid:[0,8],pub:4,pub_kei:5,pubkei:[0,4,5,6],public_kei:[0,4],public_key_to_address:[4,5],publickei:[0,4],pull:[3,6],pure:6,pwpkh:[0,6],pybtc:[0,1,4,5,7,8],python3:7,python:[6,7],rais:[],random:0,raw:[5,8],raw_hash:5,raw_tx:8,read:5,read_var_int:5,read_var_list:5,readabl:[5,8],recent:6,recogn:5,recommend:[0,4],record:3,redeeem:0,redeem_script:0,redeem_script_hex:0,reduc:4,refer:6,remov:5,repo:3,repositori:7,repres:8,represent:[5,8],reqsig:5,request:[3,6],requir:5,result:5,revers:5,reverse_hash:5,rh2:5,right:3,ripemd160:5,root:6,run:7,s2rh:5,script:[0,6],script_hash:[0,5],script_to_hash:5,secp256k1:[5,6],segreg:6,segwit:[0,4,5,8],send:6,serial:8,set:[0,4,5,8],setdefault:[],setup:7,sha256:5,shallow:[],sig:5,sign:5,sign_messag:5,signatur:6,simpl:7,simpli:7,site:7,softwar:7,some:6,sourc:[0,1,5,8],specifi:[0,5],sript:5,start:5,step:7,str:8,straightforward:3,stream:5,string:[0,5,8],strip:8,sub_script:5,subscript:5,suggest:6,support:[0,4,6],sure:3,target:5,target_to_difficulti:5,templat:8,termin:7,test:6,testnet:[0,4,5,8],thi:[0,4,5,6,7],through:7,time:8,tool:[4,6],tracker:6,traget:5,transacion:8,transact:[1,2,5,6],transacton:5,tupl:[],tx_format:8,tx_hash_list:5,type:[0,4,5,6,8],uncompress:[4,5],updat:[],upper:3,usag:[4,6],use:[0,4,5],used:8,user:5,using:[5,7,8],valid:5,valu:5,var_int_len:5,var_int_to_int:5,variabl:5,verifi:5,verify_signatur:5,version:[0,5,6,8],view:[],vriabl:5,wallet:6,web:3,well:8,which:[4,5],wif:[0,4,5,6],wif_to_private_kei:5,wit:[0,4,5,6],witness_vers:[0,4,5],work:[0,4],workflow:3,wors:3,written:6,you:[0,4,6,7],your:[3,4,7]},titles:["Addresses","Blocks","Reference","Contributing","Examples","Pure functions reference","Welcome to PYBTC","Installation","Transactions"],titleterms:{"function":[4,5],"new":6,"public":5,address:[0,4,5],author:6,block:1,code:[6,7],content:6,contribut:3,contributor:3,coverag:3,creat:4,depend:6,difficulti:5,encod:5,exampl:4,featur:6,from:[4,7],get:[4,6,7],hash:5,instal:[6,7],instruct:3,kei:[4,5,6],librari:6,licens:6,merkl:5,packag:7,pip:7,privat:5,pure:[4,5],pybtc:6,quick:6,refer:[2,5],root:5,script:[4,5],signatur:5,sourc:[6,7],start:6,tabl:6,test:3,tool:5,transact:8,welcom:6,what:6}}) \ No newline at end of file diff --git a/docs/build/html/transaction.html b/docs/build/html/transaction.html index e450a7b..e7f3102 100644 --- a/docs/build/html/transaction.html +++ b/docs/build/html/transaction.html @@ -39,8 +39,79 @@

    The class for creating transaction.

    -class pybtc.Transaction(raw_tx=None, tx_format='decoded', version=1, lockTime=0, testnet=False)[source]
    -
    +class pybtc.Transaction(raw_tx=None, tx_format='decoded', version=1, lock_time=0, testnet=False)[source] +

    The class for Transaction object

    + +++ + + + +
    Parameters:
      +
    • raw_tx – (optional) raw transaction in bytes or HEX encoded string, if no raw transaction provided +well be created new empty transaction template.
    • +
    • tx_format – “raw” or “decoded” format. Raw format is mean that all transaction represented in bytes +for best performance. +Decoded transaction is represented in human readable format using base68, hex, bech32, +asm and opcodes. By default “decoded” format using.
    • +
    • version (int) – transaction version for new template, by default 1.
    • +
    • lock_time (int) – transaction lock time for new template, by default 0.
    • +
    • testnet (boolean) – address type for “decoded” transaction representation.
    • +
    +
    +
    +
    +decode(testnet=None)[source]
    +

    change Transacion object representation to “decoded” human readable format

    + +++ + + + +
    Parameters:testnet (bool) – (optional) address type for “decoded” transaction representation, by default None. +if None used type from transaction property “format”.
    +
    + +
    +
    +encode()[source]
    +

    change Transaction object representation to “raw” bytes format, +all human readable part will be stripped.

    +
    + +
    +
    +json()[source]
    +

    Get json transaction representation

    +
    + +
    +
    +serialize(segwit=True, hex=True)[source]
    +

    Get serialized transaction

    + +++ + + + + + + +
    Parameters:
      +
    • segwit (bool) – (optional) flag for segwit representation of serialized transaction, by +default True.
    • +
    • hex (bool) – (optional) if set to True return HEX encoded string, by default True.
    • +
    +
    Return str,bytes:
     

    serialized transaction in HEX or bytes.

    +
    +
    + +
    diff --git a/docs/source/transaction.rst b/docs/source/transaction.rst index ac2360c..1da13cb 100644 --- a/docs/source/transaction.rst +++ b/docs/source/transaction.rst @@ -6,6 +6,7 @@ The class for creating transaction. .. autoclass:: pybtc.Transaction - :members: + :members: + diff --git a/pybtc/address.py b/pybtc/address.py index b67b2c1..2eccdfe 100644 --- a/pybtc/address.py +++ b/pybtc/address.py @@ -43,10 +43,11 @@ class PrivateKey(): self.hex = hexlify(self.key).decode() self.wif = private_key_to_wif(self.key, compressed, testnet) return - assert isinstance(key, str) + if not isinstance(key, str) or not is_wif_valid(key): + raise TypeError("private key invalid") + self.key = wif_to_private_key(key, hex=False) self.hex = hexlify(self.key).decode() - self.wif = private_key_to_wif(self.key, compressed, testnet) if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): self.compressed = False @@ -57,6 +58,7 @@ class PrivateKey(): self.testnet = True else: self.testnet = False + self.wif = private_key_to_wif(self.key, self.compressed, self.testnet) def __str__(self): return self.wif @@ -201,15 +203,18 @@ class Address(): class ScriptAddress(): - def __init__(self, script, address_type="P2SH", - testnet=False, witness_version=None): + def __init__(self, script, + testnet=False, witness_version=0): self.witness_version = witness_version self.testnet = testnet if isinstance(script, str): script = unhexlify(script) self.script = script self.script_hex = hexlify(self.script).decode() - self.hash = hash160(self.script) + if witness_version is None: + self.hash = hash160(self.script) + else: + self.hash = sha256(self.script) self.script_opcodes = decode_script(self.script) self.script_opcodes_asm = decode_script(self.script, 1) self.address = hash_to_address(self.hash, @@ -217,3 +222,49 @@ class ScriptAddress(): witness_version=self.witness_version, testnet=self.testnet) + @classmethod + def multisig(cls, n, m, public_key_list, + testnet=False, witness_version=0): + """ + The class method for creating a multisig address. + + :param n: count of required signatures (max 15). + :param m: count of total addresses of participants (max 15). + :param list address_list: addresses list, allowed types: + + - bytes or HEX encoded private key + - private key in WIF format + - PrivateKey instance, + - bytes or HEX encoded public key + - PublicKey instance + + + """ + if n > 15 or m > 15 or n > m or n < 1 or m < 1: + raise TypeError("invalid n of m maximum 15 of 15 multisig allowed") + if len(public_key_list) != m: + raise TypeError("invalid address list count") + script = bytes([0x50 + n]) + for a in list(public_key_list): + if isinstance(a, str): + try: + a = unhexlify(a) + except: + if is_wif_valid(a): + a = private_to_public_key(a, hex=False) + pass + if isinstance(a, Address): + a = a.public_key.key + elif isinstance(a, PublicKey): + a = a.key + elif isinstance(a, PrivateKey): + a = private_to_public_key(a.key) + if not isinstance(a, bytes): + raise TypeError("invalid public key list element") + if len(a) == 32: + a = private_to_public_key(a) + if len(a) != 33: + raise TypeError("invalid public key list element size") + script += int_to_var_int(len(a)) + a + script += bytes([0x50 + m]) + OP_CHECKMULTISIG + return cls(script, testnet=testnet, witness_version=witness_version) diff --git a/pybtc/opcodes.py b/pybtc/opcodes.py index 93d9162..b10c147 100644 --- a/pybtc/opcodes.py +++ b/pybtc/opcodes.py @@ -11,8 +11,8 @@ 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_1"] = 0x51 OPCODE["OP_2"] = 0x52 OPCODE["OP_3"] = 0x53 OPCODE["OP_4"] = 0x54 diff --git a/pybtc/tools.py b/pybtc/tools.py index a3808a3..907cfb3 100644 --- a/pybtc/tools.py +++ b/pybtc/tools.py @@ -438,6 +438,13 @@ def get_witness_version(address): # Script +def public_key_to_pubkey_script(key, hex=True): + if isinstance(key, str): + key = unhexlify(key) + s = bytes([len(key)]) + key + OP_CHECKSIG + return hexlify(s).decode() if hex else s + + def parse_script(script, segwit=True): """ Parse script and return script type, script address and required signatures count. @@ -552,9 +559,10 @@ def decode_script(script, asm=False): Decode script to ASM format or to human readable OPCODES string. :param script: script in bytes string or HEX encoded string format. - :param asm: (optional) If set to True decode to ASM fromat, by default set to False. + :param asm: (optional) If set to True decode to ASM format, by default set to False. :return: script in ASM format string or OPCODES string. """ + if isinstance(script, str): try: script = unhexlify(script) @@ -573,14 +581,35 @@ def decode_script(script, asm=False): result.append('[%s]' % script[s]) s += script[s] + 1 continue - elif script[s] == OPCODE["OP_PUSHDATA1"]: - s += 1 + script[s + 1] + + if script[s] == OPCODE["OP_PUSHDATA1"]: + ld = script[s + 1] + if asm: + result.append(hexlify(script[s + 1:s + 1 + ld]).decode()) + else: + result.append(RAW_OPCODE[script[s]]) + result.append('[%s]' % ld) + s += 1 + script[s + 1] + 1 elif script[s] == OPCODE["OP_PUSHDATA2"]: - s += 2 + struct.unpack('= 0): + raise TypeError("v_out invalid") + if not isinstance(sequence, int) or not (sequence <= 0xffffffff and sequence >= 0): + raise TypeError("sequence invalid") + + if private_key: + if not isinstance(private_key, PrivateKey): + private_key = PrivateKey(private_key) + if amount: + if not isinstance(amount, int) or not amount >= 0 and amount <= MAX_AMOUNT: + raise TypeError("amount invalid") + + if tx_in_witness: + if not isinstance(tx_in_witness, list): + raise TypeError("tx_in_witness invalid") + l = 0 + witness = [] + for w in tx_in_witness: + if isinstance(w, str): + witness.append(unhexlify(w) if self["format"] == "raw" else w) else: - self["coinbase"] = False - if sw: - self["segwit"] = True - self["hash"] = double_sha256(b) - self["txId"] = double_sha256(b[:4] + b[6:sw] + b[-4:]) + witness.append(w if self["format"] == "raw" else unhexlify(w)) + l += 1 + len(w) + if len(w) >= 0x4c: + l += 1 + if len(w) > 0xff: + l += 1 + # witness script limit + if not l <= 10000: + raise TypeError("tx_in_witness invalid") + + if tx_id == b"\x00" * 32: + if not (v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100): + raise TypeError("coinbase tx invalid") + self["coinbase"] = True + + # script_pub_key + if script_pub_key: + if isinstance(script_pub_key, str): + script_pub_key = unhexlify(script_pub_key) + if not isinstance(script_pub_key, bytes): + raise TypeError("script_pub_key tx invalid") + + if redeem_script: + if isinstance(redeem_script, str): + redeem_script = unhexlify(redeem_script) + if not isinstance(redeem_script, bytes): + raise TypeError("redeem_script tx invalid") + + if address is not None: + if isinstance(address, str): + net = True if address_net_type(address) == 'mainnet' else False + if not net != self["testnet"]: + raise TypeError("address invalid") + script = address_to_script(address) + elif type(address) in (Address, ScriptAddress): + script = address_to_script(address.address) + if script_pub_key: + assert script_pub_key == script + else: + script_pub_key = script + + k = len(self["vIn"]) + self["vIn"][k] = dict() + self["vIn"][k]["vOut"] = v_out + self["vIn"][k]["sequence"] = sequence + if self["format"] == "raw": + self["vIn"][k]["txId"] = tx_id + self["vIn"][k]["scriptSig"] = script_sig + if script_pub_key: + self["vIn"][k]["scriptPubKey"] = script_pub_key + if redeem_script: + self["vIn"][k]["redeemScript"] = redeem_script + else: + self["vIn"][k]["txId"] = rh2s(tx_id) + self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode() + self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig) + self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1) + if script_pub_key: + self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() + self["vIn"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) + self["vIn"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) + if redeem_script: + self["vIn"][k]["redeemScript"] = hexlify(redeem_script).decode() + self["vIn"][k]["redeemScriptOpcodes"] = decode_script(redeem_script) + self["vIn"][k]["redeemScriptAsm"] = decode_script(script_pub_key, 1) + if tx_in_witness: + self["segwit"] = True + self["vIn"][k]["txInWitness"] = witness + if amount: + self["vIn"][k]["value"] = amount + if private_key: + self["vIn"][k].private_key = private_key + self.__refresh__() + return self + + def add_output(self, amount, address=None, script_pub_key=None): + assert address is not None or script_pub_key is not None + assert not (address is None and script_pub_key is None) + assert type(amount) == int + assert amount >= 0 and amount <= MAX_AMOUNT + if script_pub_key: + if type(script_pub_key) == str: + script_pub_key = unhexlify(script_pub_key) + assert type(script_pub_key) == bytes + else: + if type(address) == Address: + address = address.address + script_pub_key = address_to_script(address) + + k = len(self["vOut"]) + self["vOut"][k] = dict() + self["vOut"][k]["value"] = amount + segwit = True if "segwit" in self else False + s = parse_script(script_pub_key, segwit) + self["vOut"][k]["nType"] = s["nType"] + self["vOut"][k]["type"] = s["type"] + + if self["format"] == "raw": + self["vOut"][k]["scriptPubKey"] = script_pub_key + if self["data"] is None: + if s["nType"] == 3: + self["data"] = s["data"] + if s["nType"] not in (3, 4, 7): + self["vOut"][k]["addressHash"] = s["addressHash"] + self["vOut"][k]["reqSigs"] = s["reqSigs"] + else: + self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() + if self["data"] is None: + if s["nType"] == 3: + self["data"] = hexlify(s["data"]).decode() + if s["nType"] not in (3, 4, 7): + self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode() + self["vOut"][k]["reqSigs"] = s["reqSigs"] + self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) + self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) + sh = True if self["vOut"][k]["nType"] in (1, 5) else False + witness_version = None if self["vOut"][k]["nType"] < 5 else 0 + if "addressHash" in self["vOut"][k]: + self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"], + self["testnet"], + sh, + witness_version) + self.__refresh__() + return self + + def del_output(self, n=None): + if not self["vOut"]: + return self + if n is None: + n = len(self["vOut"]) - 1 + new_out = dict() + c = 0 + for i in range(len(self["vOut"])): + if i != n: + new_out[c] = self["vOut"][i] + c += 1 + self["vOut"] = new_out + self.__refresh__() + return self + + def del_input(self, n): + if not self["vIn"]: + return self + if n is None: + n = len(self["vIn"]) - 1 + new_in = dict() + c = 0 + for i in range(len(self["vIn"])): + if i != n: + new_in[c] = self["vIn"][i] + c += 1 + self["vIn"] = new_in + self.__refresh__() + return self + + def sign_input(self, n, private_key=None, script_pub_key=None, + redeem_script=None, + sighash_type=SIGHASH_ALL, + address=None, amount=None, witness_version=0, + p2sh_p2wsh=False): + # private key + if not private_key: + try: + private_key = self["vIn"][n].private_key.key + except: + raise RuntimeError("no private key") + if isinstance(private_key, list): + public_key = [PublicKey(p).key for p in private_key] + private_key = [p.key if isinstance(p, PrivateKey) else PrivateKey(p).key for p in private_key] + else: + if not isinstance(private_key, PrivateKey): + private_key = PrivateKey(private_key) + public_key = [PublicKey(private_key).key] + private_key = [private_key.key] + + if address is not None: + if isinstance(address, str): + net = True if address_net_type(address) == 'mainnet' else False + if not net != self["testnet"]: + raise TypeError("address invalid") + script_pub_key = address_to_script(address) + elif type(address) in (Address, ScriptAddress): + script_pub_key = address_to_script(address.address) + # script pub key + if script_pub_key is None: + + if "scriptPubKey" in self["vIn"][n]: + script_pub_key = self["vIn"][n]["scriptPubKey"] + st = parse_script(script_pub_key) + elif redeem_script or "redeemScript" in self["vIn"][n]: + if witness_version is None or p2sh_p2wsh: + st = {"type": "P2SH"} + elif witness_version == 0: + st = {"type": "P2WSH"} else: - self["segwit"] = False - self["txId"] = double_sha256(b) - self["hash"] = self["txId"] + raise RuntimeError("not implemented") + else: + raise RuntimeError("no scriptPubKey key") + else: + st = parse_script(script_pub_key) + if isinstance(script_pub_key, str): + script_pub_key = unhexlify(script_pub_key) - def decode(self, testnet=None): - if self["format"] == "decoded": - self.encode() - self["format"] = "decoded" - if testnet is not None: - self["testnet"] = testnet - if type(self["txId"]) == bytes: - self["txId"] = rh2s(self["txId"]) - if "flag" in self: - if type(self["flag"]) == bytes: - self["flag"] = rh2s(self["flag"]) - if type(self["hash"]) == bytes: - self["hash"] = rh2s(self["hash"]) - if type(self["rawTx"]) == bytes: - self["rawTx"] = hexlify(self["rawTx"]).decode() - for i in self["vIn"]: - if type(self["vIn"][i]["txId"]) == bytes: - self["vIn"][i]["txId"] = rh2s(self["vIn"][i]["txId"]) - if type(self["vIn"][i]["scriptSig"]) == bytes: - self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode() - try: - t = list() - for w in self["vIn"][i]["txInWitness"]: - if type(w) == bytes: - w = hexlify(w).decode() - t.append(w) - self["vIn"][i]["txInWitness"] = t - self["vIn"][i]["txInWitnessAsm"] = [decode_script(ws, 1) for ws in - self["vIn"][i]["txInWitness"]] - self["vIn"][i]["txInWitnessOpcodes"] = [decode_script(ws) for ws in - self["vIn"][i]["txInWitness"]] - except: - pass - try: - if type(self["vIn"][i]["addressHash"]) == bytes: - self["vIn"][i]["addressHash"] = hexlify(self["vIn"][i]["addressHash"]).decode() - sh = True if self["vIn"][i]["nType"] in (1, 5) else False - witness_version = None if self["vIn"][i]["nType"] < 5 else 0 - self["vIn"][i]["address"] = hash_to_address(self["vIn"][i]["addressHash"], - self["testnet"], - sh, - witness_version) - except: - pass - if "scriptPubKey" in self["vIn"][i]: - if type(self["vIn"][i]["scriptPubKey"]) == bytes: - self["vIn"][i]["scriptPubKey"] = hexlify(self["vIn"][i]["scriptPubKey"]).decode() - self["vIn"][i]["scriptPubKeyOpcodes"] = decode_script(self["vIn"][i]["scriptPubKey"]) - self["vIn"][i]["scriptPubKeyAsm"] = decode_script(self["vIn"][i]["scriptPubKey"], 1) - if "redeemScript" in self["vIn"][i]: - if type(self["vIn"][i]["redeemScript"]) == bytes: - self["vIn"][i]["redeemScript"] = hexlify(self["vIn"][i]["redeemScript"]).decode() - self["vIn"][i]["redeemScriptOpcodes"] = decode_script(self["vIn"][i]["redeemScript"]) - self["vIn"][i]["redeemScriptAsm"] = decode_script(self["vIn"][i]["redeemScript"], 1) - if not self["coinbase"]: - if type(self["vIn"][i]["scriptSig"]) == bytes: - self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode() - self["vIn"][i]["scriptSigOpcodes"] = decode_script(self["vIn"][i]["scriptSig"]) - self["vIn"][i]["scriptSigAsm"] = decode_script(self["vIn"][i]["scriptSig"], 1) + # sign input + if st["type"] == "PUBKEY": + script_sig = self.__sign_pubkey__(n, private_key, script_pub_key, sighash_type) + elif st["type"] == "P2PKH": + script_sig = self.__sign_p2pkh__(n, private_key, public_key, script_pub_key, sighash_type) + elif st["type"] == "P2SH": + script_sig = self.__sign_p2sh(n, private_key, public_key, redeem_script, sighash_type, amount, p2sh_p2wsh) + elif st["type"] == "P2WPKH": + script_sig = self.__sign_p2wpkh(n, private_key, public_key, script_pub_key, sighash_type, amount) + elif st["type"] == "P2WSH": + script_sig = self.__sign_p2wsh(n, private_key, public_key, script_pub_key, + redeem_script, sighash_type, amount) + else: + raise RuntimeError("not implemented") - for i in self["vOut"]: - if type(self["vOut"][i]["scriptPubKey"]) == bytes: - self["vOut"][i]["scriptPubKey"] = hexlify(self["vOut"][i]["scriptPubKey"]).decode() - try: - if type(self["vOut"][i]["addressHash"]) == bytes: - self["vOut"][i]["addressHash"] = hexlify(self["vOut"][i]["addressHash"]).decode() - sh = True if self["vOut"][i]["nType"] in (1, 5) else False - witness_version = None if self["vOut"][i]["nType"] < 5 else 0 - self["vOut"][i]["address"] = hash_to_address(self["vOut"][i]["addressHash"], - self["testnet"], - sh, - witness_version) - except: - pass - self["vOut"][i]["scriptPubKeyOpcodes"] = decode_script(self["vOut"][i]["scriptPubKey"]) - self["vOut"][i]["scriptPubKeyAsm"] = decode_script(self["vOut"][i]["scriptPubKey"], 1) - if "data" in self: - if type(self["data"]) == bytes: - self["data"] = hexlify(self["data"]).decode() - return self + if self["format"] == "raw": + self["vIn"][n]["scriptSig"] = script_sig + else: + self["vIn"][n]["scriptSig"] = hexlify(script_sig).decode() + self["vIn"][n]["scriptSigOpcodes"] = decode_script(script_sig) + self["vIn"][n]["scriptSigAsm"] = decode_script(script_sig, 1) + self.__refresh__() + return self - def encode(self): - if type(self["txId"]) == str: - self["txId"] = s2rh(self["txId"]) - if "flag" in self: - if type(self["flag"]) == str: - self["flag"] = s2rh(self["flag"]) - if type(self["hash"]) == str: - self["hash"] = s2rh(self["hash"]) - if type(self["rawTx"]) == str: - self["rawTx"] = unhexlify(self["rawTx"]) + def __sign_pubkey__(self, n, private_key, script_pub_key, sighash_type): + sighash = self.sig_hash(n, script_pub_key=script_pub_key, sighash_type=sighash_type) + sighash = s2rh(sighash) if isinstance(sighash, str) else sighash + signature = sign_message(sighash, private_key[0], 0) + bytes([sighash_type]) + return b''.join([bytes([len(signature)]), signature]) - for i in self["vIn"]: - if type(self["vIn"][i]["txId"]) == str: - self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"]) - if type(self["vIn"][i]["scriptSig"]) == str: - self["vIn"][i]["scriptSig"] = unhexlify(self["vIn"][i]["scriptSig"]) - try: - t = list() - for w in self["vIn"][i]["txInWitness"]: - if type(w) == str: - w = unhexlify(w) - t.append(w) - self["vIn"][i]["txInWitness"] = t - if "txInWitnessAsm" in self["vIn"][i]: - del self["vIn"][i]["txInWitnessAsm"] - if "txInWitnessOpcodes" in self["vIn"][i]: - del self["vIn"][i]["txInWitnessOpcodes"] - except: - pass - try: - if type(self["vIn"][i]["addressHash"]) == str: - self["vIn"][i]["addressHash"] = unhexlify(self["vIn"][i]["addressHash"]) - if "address" in self["vIn"][i]: - del self["vIn"][i]["address"] - except: - pass - if "scriptSigAsm" in self["vIn"][i]: - del self["vIn"][i]["scriptSigAsm"] - if "scriptSigOpcodes" in self["vIn"][i]: - del self["vIn"][i]["scriptSigOpcodes"] + def __sign_p2pkh__(self, n, private_key, public_key, script_pub_key, sighash_type): + sighash = self.sig_hash(n, script_pub_key=script_pub_key, sighash_type=sighash_type) + sighash = s2rh(sighash) if isinstance(sighash, str) else sighash + signature = sign_message(sighash, private_key[0], 0) + bytes([sighash_type]) + script_sig = b''.join([bytes([len(signature)]), + signature, + bytes([len(public_key[0])]), + public_key[0]]) + return script_sig - for i in self["vOut"]: - if type(self["vOut"][i]["scriptPubKey"]) == str: - self["vOut"][i]["scriptPubKey"] = unhexlify(self["vOut"][i]["scriptPubKey"]) - try: - if type(self["vOut"][i]["addressHash"]) == str: - self["vOut"][i]["addressHash"] = unhexlify(self["vOut"][i]["addressHash"]) - if "address" in self["vOut"][i]: - del self["vOut"][i]["address"] - except: - pass - if "scriptPubKeyOpcodes" in self["vOut"][i]: - del self["vOut"][i]["scriptPubKeyOpcodes"] - if "scriptPubKeyAsm" in self["vOut"][i]: - del self["vOut"][i]["scriptPubKeyAsm"] + def __sign_p2sh(self, n, private_key, public_key, redeem_script, sighash_type, amount, p2sh_p2wsh): + if not redeem_script: + if "redeemScript" in self["vIn"][n]: + redeem_script = self["vIn"][n]["redeemScript"] + else: + raise RuntimeError("no redeem script") + if isinstance(redeem_script, str): + redeem_script = unhexlify(redeem_script) + rst = parse_script(redeem_script) - if "data" in self: - if type(self["data"]) == str: - self["data"] = unhexlify(self["data"]) - self["format"] = "raw" - return self + if p2sh_p2wsh: + return self.__sign_p2sh_p2wsh(n, private_key, + public_key, redeem_script, sighash_type, amount) + if rst["type"] == "MULTISIG": + return self.__sign_p2sh_multisig(n, private_key, public_key, redeem_script, sighash_type) + elif rst["type"] == "P2WPKH": + return self.__sign_p2sh_p2wpkh(n, private_key, public_key, redeem_script, sighash_type, amount) + else: + return self.__sign_p2sh_custom(n, private_key, public_key, redeem_script, sighash_type, amount) - def get_stream(self, 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 + def __sign_p2sh_multisig(self, n, private_key, public_key, redeem_script, sighash_type): + sighash = self.sig_hash(n, script_pub_key=redeem_script, sighash_type=sighash_type) + sighash = s2rh(sighash) if isinstance(sighash, str) else sighash + sig = [sign_message(sighash, p, 0) + bytes([sighash_type]) for p in private_key] + return b''.join(self.__get_multisig_script_sig__(self["vIn"][n]["scriptSig"], + public_key, sig, + redeem_script, + redeem_script, + n)) - def serialize(self, segwit=True, hex=True): - chunks = [] - chunks.append(struct.pack('= 0 - assert type(sequence) == int - assert sequence <= 0xffffffff and sequence >= 0 - assert type(script_sig) == bytes - assert len(script_sig) <= 520 - if private_key: - if type(private_key) != PrivateKey: - private_key = PrivateKey(private_key) - if amount: - assert type(amount) == int - assert amount >= 0 and amount <= MAX_AMOUNT - if tx_in_witness: - assert type(tx_in_witness) == list - l = 0 - witness = [] - for w in tx_in_witness: - if type(w) == str: - witness.append(unhexlify(w) if self["format"] == "raw" else w) - else: - witness.append(w if self["format"] == "raw" else unhexlify(w)) - l += 1 + len(w) - if len(w) >= 0x4c: - l += 1 - if len(w) > 0xff: - l += 1 - # witness script limit - assert l <= 10000 - if tx_id == b"\x00" * 32: - assert v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100 - self["coinbase"] = True + def sig_hash(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL): + # check n + assert n >= 0 + tx_in_count = len(self["vIn"]) - # script_pub_key - if script_pub_key: - if type(script_pub_key) == str: - script_pub_key = unhexlify(script_pub_key) - type(script_pub_key) == bytes - if address is not None: - if type(address) == str: - net = True if address_net_type(address) == 'mainnet' else False - assert not net == self["testnet"] - script = address_to_script(address) - elif type(address) in (Address, ScriptAddress): - assert type(address) == Address - script = address_to_script(address.address) - if script_pub_key: - assert script_pub_key == script - else: - script_pub_key = script + if n >= tx_in_count: + if self["format"] == "raw": + return b'\x01' + b'\x00' * 31 + else: + return rh2s(b'\x01' + b'\x00' * 31) - k = len(self["vIn"]) - self["vIn"][k] = dict() - self["vIn"][k]["vOut"] = v_out - self["vIn"][k]["sequence"] = sequence - if self["format"] == "raw": - self["vIn"][k]["txId"] = tx_id - self["vIn"][k]["scriptSig"] = script_sig - if script_pub_key: - self["vIn"][k]["scriptPubKey"] = script_pub_key + # check script_pub_key for input + if script_pub_key is not None: + script_code = script_pub_key + else: + assert "scriptPubKey" in self["vIn"][n] + script_code = self["vIn"][n]["scriptPubKey"] + if type(script_code) == str: + script_code = unhexlify(script_code) + assert type(script_code) == bytes + + # remove opcode separators + script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) + preimage = bytearray() + + if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))): + if self["format"] == "raw": + return b'\x01' + b'\x00' * 31 + else: + return rh2s(b'\x01' + b'\x00' * 31) + + preimage += struct.pack(' n and (sighash_type & 31) == SIGHASH_SINGLE: + continue + if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): + preimage += b'\xff' * 8 + b'\x00' else: - self["vIn"][k]["txId"] = rh2s(tx_id) - self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode() - self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig) - self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1) - if script_pub_key: - self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() - if tx_in_witness: - self["segwit"] = True - self["vIn"][k]["txInWitness"] = witness - if amount: - self["vIn"][k]["value"] = amount - if private_key: - self["vIn"][k].private_key = private_key - self.__refresh__() - return self + preimage += self["vOut"][i]["value"].to_bytes(8, 'little') + preimage += int_to_var_int(len(script_pub_key)) + script_pub_key - def add_output(self, amount, address=None, script_pub_key=None): - assert address is not None or script_pub_key is not None - assert not (address is None and script_pub_key is None) - assert type(amount) == int - assert amount >= 0 and amount <= MAX_AMOUNT - if script_pub_key: - if type(script_pub_key) == str: - script_pub_key = unhexlify(script_pub_key) - assert type(script_pub_key) == bytes - else: - if type(address) == Address: - address = address.address - script_pub_key = address_to_script(address) + preimage += self["lockTime"].to_bytes(4, 'little') + preimage += struct.pack(b"= 0 + tx_in_count = len(self["vIn"]) - if self["format"] == "raw": - self["vOut"][k]["scriptPubKey"] = script_pub_key - if self["data"] is None: - if s["nType"] == 3: - self["data"] = s["data"] - if s["nType"] not in (3, 4, 7): - self["vOut"][k]["addressHash"] = s["addressHash"] - self["vOut"][k]["reqSigs"] = s["reqSigs"] - else: - self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode() - if self["data"] is None: - if s["nType"] == 3: - self["data"] = hexlify(s["data"]).decode() - if s["nType"] not in (3, 4, 7): - self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode() - self["vOut"][k]["reqSigs"] = s["reqSigs"] - self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key) - self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1) - sh = True if self["vOut"][k]["nType"] in (1, 5) else False - witness_version = None if self["vOut"][k]["nType"] < 5 else 0 - if "addressHash" in self["vOut"][k]: - self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"], - self["testnet"], - sh, - witness_version) - self.__refresh__() - return self + if n >= tx_in_count: + if self["format"] == "raw": + return b'\x01' + b'\x00' * 31 + else: + return rh2s(b'\x01' + b'\x00' * 31) - def del_output(self, n=None): - if not self["vOut"]: - return self - if n is None: - n = len(self["vOut"]) - 1 - new_out = dict() - c = 0 - for i in range(len(self["vOut"])): - if i != n: - new_out[c] = self["vOut"][i] - c += 1 - self["vOut"] = new_out - self.__refresh__() - return self + # check script_pub_key for input + if script_pub_key is not None: + script_code = script_pub_key + else: + assert "scriptPubKey" in self["vIn"][n] + script_code = self["vIn"][n]["scriptPubKey"] + if type(script_code) == str: + script_code = unhexlify(script_code) + assert type(script_code) == bytes - def del_input(self, n): - if not self["vIn"]: - return self - if n is None: - n = len(self["vIn"]) - 1 - new_in = dict() - c = 0 - for i in range(len(self["vIn"])): - if i != n: - new_in[c] = self["vIn"][i] - c += 1 - self["vIn"] = new_in - self.__refresh__() - return self + # remove opcode separators + preimage = bytearray() + # 1. nVersion of the transaction (4-byte little endian) + preimage += struct.pack('= 0 - tx_in_count = len(self["vIn"]) - - if n >= tx_in_count: + def __refresh__(self): + if not self["vOut"] or not self["vIn"]: + return + if self["segwit"]: + for i in self["vIn"]: + if "txInWitness" not in self["vIn"][i]: if self["format"] == "raw": - return b'\x01' + b'\x00' * 31 + self["vIn"][i]["txInWitness"] = [] else: - return rh2s(b'\x01' + b'\x00' * 31) + self["vIn"][i]["txInWitness"] = [] + no_segwit_view = self.serialize(segwit=False, hex=False) + self["txId"] = double_sha256(no_segwit_view) + self["rawTx"] = self.serialize(segwit=True, hex=False) + self["hash"] = double_sha256(self["rawTx"]) - # check script_pub_key for input - if script_pub_key is not None: - script_code = script_pub_key - else: - assert "scriptPubKey" in self["vIn"][n] - script_code = self["vIn"][n]["scriptPubKey"] - if type(script_code) == str: - script_code = unhexlify(script_code) - assert type(script_code) == bytes + self["size"] = len(self["rawTx"]) + self["bSize"] = len(no_segwit_view) + self["weight"] = self["bSize"] * 3 + self["size"] + self["vSize"] = math.ceil(self["weight"] / 4) - # remove opcode separators - script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"]) - preimage = bytearray() + if self["format"] != "raw": + self["txId"] = rh2s(self["txId"]) + self["hash"] = rh2s(self["hash"]) + self["rawTx"] = hexlify(self["rawTx"]).decode() - if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))): - if self["format"] == "raw": - return b'\x01' + b'\x00' * 31 - else: - return rh2s(b'\x01' + b'\x00' * 31) + input_sum = 0 + for i in self["vIn"]: + if "value" in self["vIn"][i]: + input_sum += self["vIn"][i]["value"] + else: + input_sum = None + break - preimage += struct.pack(' n and (sighash_type & 31) == SIGHASH_SINGLE: - continue - if (sighash_type & 31) == SIGHASH_SINGLE and (n != i): - preimage += b'\xff' * 8 + b'\x00' - else: - preimage += self["vOut"][i]["value"].to_bytes(8, 'little') - preimage += int_to_var_int(len(script_pub_key)) + script_pub_key - - preimage += self["lockTime"].to_bytes(4, 'little') - preimage += struct.pack(b"