def get_address_hash( address: str, script: bool = False) -> Union[str, P2pkhScript, P2shScript]: """ Get Bitcoin address hash. :param address: Bitcoin address. :type address: str :param script: Return script (P2pkhScript, P2shScript), default to False. :type script: bool :returns: str -- Bitcoin address hash. >>> from swap.providers.bitcoin.utils import get_address_hash >>> get_address_hash(address="mrmtGq2HMmqAogSsGDjCtXUpxrb7rHThFH", script=False) "7b7c4431a43b612a72f8229935c469f1f6903658" """ if not is_address(address=address): raise AddressError(f"Invalid Bitcoin '{address}' address.") loaded_address = Address.from_string(address) get_type = loaded_address.get_type() if not script: return loaded_address.hash.hex() if str(get_type) == "p2pkh": return P2pkhScript(loaded_address) elif str(get_type) == "p2sh": return P2shScript(loaded_address)
def build_transaction(self, wallet, htlc, amount, locktime=0): """ Build bitcoin fund transaction. :param wallet: bitcoin sender wallet. :type wallet: bitcoin.wallet.Wallet :param htlc: bitcoin hash time lock contract (HTLC). :type htlc: bitcoin.htlc.HTLC :param amount: bitcoin amount to fund. :type amount: int :param locktime: bitcoin transaction lock time, defaults to 0. :type locktime: int :returns: FundTransaction -- bitcoin fund transaction instance. >>> from shuttle.providers.bitcoin.transaction import FundTransaction >>> fund_transaction = FundTransaction(network="testnet") >>> fund_transaction.build_transaction(sender_wallet, htlc, 10000) <shuttle.providers.bitcoin.transaction.FundTransaction object at 0x0409DAF0> """ # Checking build transaction arguments instance if not isinstance(wallet, Wallet): raise TypeError( "invalid wallet instance, only takes bitcoin Wallet class") if not isinstance(htlc, HTLC): raise TypeError( "invalid htlc instance, only takes bitcoin HTLC class") if not isinstance(amount, int): raise TypeError("invalid amount instance, only takes integer type") # Setting wallet, htlc, amount and unspent self.wallet, self.htlc, self.amount = wallet, htlc, amount # Getting unspent transaction output self.unspent = self.wallet.unspent() # Setting previous transaction indexes self.previous_transaction_indexes = \ self.get_previous_transaction_indexes(amount=self.amount) # Getting transaction inputs and amount inputs, amount = self.inputs(self.unspent, self.previous_transaction_indexes) # Calculating bitcoin fee self.fee = fee_calculator(len(inputs), 2) if amount < (self.amount + self.fee): raise BalanceError("insufficient spend utxos") # Building mutable bitcoin transaction self.transaction = MutableTransaction( version=self.version, ins=inputs, outs=[ # Funding into hash time lock contract script hash TxOut(value=self.amount, n=0, script_pubkey=P2shScript.unhexlify(self.htlc.hash())), # Controlling amounts when we are funding on htlc script. TxOut(value=amount - (self.fee + self.amount), n=1, script_pubkey=P2pkhScript.unhexlify(self.wallet.p2pkh())) ], locktime=Locktime(locktime)) return self
def p2pkh_script(network: str, address: str) -> P2pkhScript: '''create pay-to-key-hash (P2PKH) script''' network_params = net_query(network) addr = Address.from_string(network=network_params, string=address) return P2pkhScript(addr)
def script_from_address(address, network="testnet"): if not is_address(address, network): raise AddressError("invalid %s %s address!" % (network, address)) load_address = Address.from_string(address) get_type = load_address.get_type() if str(get_type) == "p2pkh": return P2pkhScript(load_address) elif str(get_type) == "p2sh": return P2shScript(load_address)
def p2sh_p2pkh_script(network: str, address: str) -> P2shScript: '''p2sh embedding p2pkh''' network_params = net_query(network) addr = Address.from_string(network=network_params, string=address) p2pkh = P2pkhScript(addr) return P2shScript(p2pkh)
def p2sh(self, address=None): """ Get Bitcoin wallet p2sh. :param address: Bitcoin p2sh, default is None. :type address: str :return: str -- Bitcoin p2sh. >>> from shuttle.providers.bitcoin.wallet import Wallet >>> wallet = Wallet(network="testnet") >>> wallet.from_passphrase("meherett") >>> wallet.p2sh() "a914a3c4995d9cd0303e5f89ee1433212c797d04ee5d87" """ if address is None: return P2shScript(P2pkhScript(self._address)).hexlify() if not is_address(address=address, network=self.network): raise AddressError("invalid %s %s address" % (self.network, address)) address = Address.from_string(address) return P2shScript(P2pkhScript(address)).hexlify()
def p2pkh(self, address=None): """ Get Bitcoin wallet p2pkh. :param address: Bitcoin p2pkh, default is None. :type address: str :return: str -- Bitcoin p2pkh. >>> from shuttle.providers.bitcoin.wallet import Wallet >>> wallet = Wallet(network="testnet") >>> wallet.from_passphrase("meherett") >>> wallet.p2pkh() "76a9143c8acde1c7cf370d970725f13eff03bf74b3fc6188ac" """ if address is None: return P2pkhScript(self._address).hexlify() if not is_address(address=address, network=self.network): raise AddressError("invalid %s %s address" % (self.network, address)) address = Address.from_string(address) return P2pkhScript(address).hexlify()
def p2pkh(self) -> str: """ Get Bitcoin wallet public key/address p2pkh. :return: str -- Bitcoin wallet public key/address p2pkh. >>> from swap.providers.bitcoin.wallet import Wallet >>> wallet = Wallet(network="testnet") >>> wallet.from_entropy("72fee73846f2d1a5807dc8c953bf79f1") >>> wallet.from_path("m/44'/0'/0'/0/0") >>> wallet.p2pkh() "76a91433ecab3d67f0e2bde43e52f41ec1ecbdc73f11f888ac" """ return P2pkhScript(Address.from_string(self.address())).hexlify()
def script_from_address(address, network="testnet"): """ Get script from address. :param address: Bitcoin address. :type address: str :param network: Bitcoin network, defaults to testnet. :type network: str :returns: P2pkhScript, P2shScript -- Bitcoin p2pkh or p2sh script instance. >>> from shuttle.providers.bitcoin.utils import script_from_address >>> script_from_address("mrmtGq2HMmqAogSsGDjCtXUpxrb7rHThFH", "testnet") P2pkhScript('7b7c4431a43b612a72f8229935c469f1f6903658') """ if not is_address(address, network): raise AddressError("invalid %s %s address!" % (network, address)) load_address = Address.from_string(address) get_type = load_address.get_type() if str(get_type) == "p2pkh": return P2pkhScript(load_address) elif str(get_type) == "p2sh": return P2shScript(load_address)
def sweepTx(MultiSigTx, MultiSigTxOutput, MultiSigTxSolver, to_pubkey, to_index, to_value): to_spend = MultiSigTx unsigned = MutableSegWitTransaction( version=1, ins=[ TxIn( txid=to_spend, #.txid, txout=to_index, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=[ TxOut(value=to_value - txFee, n=0, script_pubkey=P2pkhScript(to_pubkey)) ], # todo make funding_pubkey a parameter. This must sweep back tp A & B locktime=Locktime(0)) solver = MultiSigTxSolver signed = unsigned.spend( [MultiSigTxOutput], [solver]) #print ("Return tx signed ",signed.hexlify()) return signed.hexlify()
def get_outscript( pubContract): #, pubkey_other): #ToDO generate using Pybtc library pubscript = P2pkhScript(pubContract) return pubscript
def build_transaction(self, transaction_id, wallet, amount, locktime=0): """ Build Bitcoin refund transaction. :param transaction_id: Bitcoin fund transaction id to redeem. :type transaction_id: str :param wallet: Bitcoin sender wallet. :type wallet: bitcoin.wallet.Wallet :param amount: Bitcoin amount to withdraw. :type amount: int :param locktime: Bitcoin transaction lock time, defaults to 0. :type locktime: int :returns: RefundTransaction -- Bitcoin refund transaction instance. >>> from shuttle.providers.bitcoin.transaction import RefundTransaction >>> from shuttle.providers.bitcoin.wallet import Wallet >>> sender_wallet = Wallet(network="testnet").from_passphrase("meherett") >>> refund_transaction = RefundTransaction(network="testnet") >>> refund_transaction.build_transaction(transaction_id="1006a6f537fcc4888c65f6ff4f91818a1c6e19bdd3130f59391c00212c552fbd", wallet=sender_wallet, amount=10000) <shuttle.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0> """ # Checking parameter instances if not isinstance(transaction_id, str): raise TypeError("invalid amount instance, only takes string type") if not isinstance(wallet, Wallet): raise TypeError( "invalid wallet instance, only takes Bitcoin Wallet class") # Setting transaction_id and wallet self.transaction_id, self.wallet = transaction_id, wallet # Getting transaction detail by id self.transaction_detail = get_transaction_detail(self.transaction_id) # Getting Hash time lock contract output detail self.htlc_detail = self.transaction_detail["outputs"][0] # Getting HTLC funded amount balance htlc_amount = self.htlc_detail["value"] # Calculating fee self._fee = fee_calculator(1, 1) if amount < self._fee: raise BalanceError("insufficient spend utxos") elif not htlc_amount >= (amount - self._fee): raise BalanceError("insufficient spend utxos", f"maximum you can withdraw {htlc_amount}") # Building mutable Bitcoin transaction self.transaction = MutableTransaction( version=self.version, ins=[ TxIn(txid=self.transaction_id, txout=0, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=[ TxOut(value=(amount - self._fee), n=0, script_pubkey=P2pkhScript.unhexlify( hex_string=self.wallet.p2pkh())) ], locktime=Locktime(locktime)) self._type = "bitcoin_refund_unsigned" return self
def build_transaction(self, wallet, htlc, amount, locktime=0): """ Build Bitcoin fund transaction. :param wallet: Bitcoin sender wallet. :type wallet: bitcoin.wallet.Wallet :param htlc: Bitcoin hash time lock contract (HTLC). :type htlc: bitcoin.htlc.HTLC :param amount: Bitcoin amount to fund. :type amount: int :param locktime: Bitcoin transaction lock time, defaults to 0. :type locktime: int :returns: FundTransaction -- Bitcoin fund transaction instance. >>> from shuttle.providers.bitcoin.htlc import HTLC >>> from shuttle.providers.bitcoin.transaction import FundTransaction >>> from shuttle.providers.bitcoin.wallet import Wallet >>> htlc = HTLC(network="testnet").init("821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e0158", "muTnffLDR5LtFeLR2i3WsKVfdyvzfyPnVB", "mphBPZf15cRFcL5tUq6mCbE84XobZ1vg7Q", 1000) >>> sender_wallet = Wallet(network="testnet").from_passphrase("meherett") >>> fund_transaction = FundTransaction(network="testnet") >>> fund_transaction.build_transaction(wallet=sender_wallet, htlc=htlc, amount=10000) <shuttle.providers.bitcoin.transaction.FundTransaction object at 0x0409DAF0> """ # Checking parameter instances if not isinstance(wallet, Wallet): raise TypeError( "invalid wallet instance, only takes Bitcoin Wallet class") if not isinstance(htlc, HTLC): raise TypeError( "invalid htlc instance, only takes Bitcoin HTLC class") if not isinstance(amount, int): raise TypeError("invalid amount instance, only takes integer type") # Setting wallet, htlc, amount and unspent self.wallet, self.htlc, self.amount = wallet, htlc, amount # Getting unspent transaction output self.unspent = self.wallet.unspent() # Setting previous transaction indexes self.previous_transaction_indexes = \ self.get_previous_transaction_indexes(amount=self.amount) # Getting transaction inputs and amount inputs, amount = self.inputs( utxos=self.unspent, previous_transaction_indexes=self.previous_transaction_indexes) # Calculating Bitcoin fee self._fee = fee_calculator(len(inputs), 2) if amount < (self.amount + self._fee): raise BalanceError("insufficient spend utxos") # Building mutable Bitcoin transaction self.transaction = MutableTransaction( version=self.version, ins=inputs, outs=[ # Funding into hash time lock contract script hash TxOut(value=self.amount, n=0, script_pubkey=P2shScript.unhexlify( hex_string=self.htlc.hash())), # Controlling amounts when we are funding on htlc script TxOut(value=(amount - (self._fee + self.amount)), n=1, script_pubkey=P2pkhScript.unhexlify( hex_string=self.wallet.p2pkh())) ], locktime=Locktime(locktime)) self._type = "bitcoin_fund_unsigned" return self
def p2pkh_script(address: str) -> P2pkhScript: '''create pay-to-key-hash (P2PKH) script''' addr = Address.from_string(address) return P2pkhScript(addr)
def build_transaction(self, transaction_id, wallet, amount, secret=None, locktime=0): """ Build bitcoin refund transaction. :param transaction_id: bitcoin fund transaction id to redeem. :type transaction_id: str :param wallet: bitcoin sender wallet. :type wallet: bitcoin.wallet.Wallet :param amount: bitcoin amount to withdraw. :type amount: int :param secret: secret passphrase. :type secret: str :param locktime: bitcoin transaction lock time, defaults to 0. :type locktime: int :returns: RefundTransaction -- bitcoin refund transaction instance. >>> from shuttle.providers.bitcoin.transaction import RefundTransaction >>> refund_transaction = RefundTransaction(network="testnet") >>> refund_transaction.build_transaction(fund_transaction_id, sender_wallet, 10000) <shuttle.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0> """ # Checking build transaction arguments instance if not isinstance(transaction_id, str): raise TypeError("invalid amount instance, only takes string type") if not isinstance(wallet, Wallet): raise TypeError( "invalid wallet instance, only takes bitcoin Wallet class") if secret is not None and not isinstance(secret, str): raise TypeError("invalid secret instance, only takes string type") # Setting transaction_id and wallet self.transaction_id, self.wallet, self.secret = transaction_id, wallet, secret # Getting transaction detail by id self.transaction_detail = get_transaction_detail(self.transaction_id) # Checking transaction outputs if "outputs" not in self.transaction_detail: raise NotFoundError("not found htlc in this %s hash" % self.transaction_id) # Hash time lock contract output self.htlc = self.transaction_detail["outputs"][0] # Sender account output sender_address = P2pkhScript.unhexlify( self.transaction_detail["outputs"][1]["script"]).address( mainnet=self.mainnet) self.sender_account = Wallet(network=self.network).from_address( str(sender_address)) # HTLC info's htlc_amount = self.htlc["value"] htlc_script = P2shScript.unhexlify(self.htlc["script"]) # Calculating fee self.fee = fee_calculator(1, 1) if amount < self.fee: raise BalanceError("insufficient spend utxos") elif not htlc_amount >= (amount - self.fee): raise BalanceError("insufficient spend utxos", "maximum withdraw %d" % htlc_amount) # Building mutable bitcoin transaction self.transaction = MutableTransaction( version=self.version, ins=[ TxIn(txid=self.transaction_id, txout=0, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=[ TxOut(value=(amount - self.fee), n=0, script_pubkey=P2pkhScript.unhexlify( self.sender_account.p2pkh())) ], locktime=Locktime(locktime)) return self
# committer pubk_hex = '0380557a219119218f7830bf3cdb2bb3c8220cac15db97e255498fb992e68c04a9' privk_hex = '385acd25450e50ecd5ad0fffec7b871c8f75eb3ba9ecded8d35a0765f4763d7e' pubk = PublicKey.unhexlify(pubk_hex) privk = PrivateKey.unhexlify(privk_hex) # 创建输入脚本 secret = 'I have an apple' # 需要展示的秘密 p2pkh_solver = P2pkhSolver(privk) hasklock_solver = HashlockSolver(secret.encode(), p2pkh_solver) if_solver = IfElseSolver( Branch.IF, # branch selection hasklock_solver) # 创建输出脚本 script = P2pkhScript(pubk) # 获取commit交易 to_spend_hash = "5a98103afa86f00c54ef8cb971ec5b3ad03404e646c46f006efd654bd46d4073" to_spend_raw = get_raw_tx(to_spend_hash, coin_symbol) to_spend = TransactionFactory.unhexlify(to_spend_raw) # 获取罚金数额 penalty = int(float(to_spend.to_json()['vout'][0]['value']) * (10**8)) # 估算挖矿费用 print('estimating mining fee...') mining_fee_per_kb = get_mining_fee_per_kb(coin_symbol, api_key, condidence='high') estimated_tx_size = cal_tx_size_in_byte(inputs_num=1, outputs_num=1)