def select_inputs(self, address: str, amount: int) -> dict: '''finds apropriate utxo's to include in rawtx, while being careful to never spend old transactions with a lot of coin age. Argument is intiger, returns list of apropriate UTXO's''' utxos = [] utxo_sum = Decimal(0) for tx in sorted(self.listunspent(address=address), key=itemgetter('confirmations')): if tx["address"] not in (self.pa_parameters.P2TH_addr, self.pa_parameters.test_P2TH_addr): utxos.append( MutableTxIn(txid=tx['txid'], txout=tx['vout'], sequence=Sequence.max(), script_sig=ScriptSig.empty())) utxo_sum += Decimal(tx["amount"]) if utxo_sum >= amount: return {'utxos': utxos, 'total': utxo_sum} if utxo_sum < amount: raise InsufficientFunds("Insufficient funds.") raise Exception("undefined behavior :.(")
def select_inputs(self, total_amount, address=None): '''finds apropriate utxo's to include in rawtx, while being careful to never spend old transactions with a lot of coin age. Argument is intiger, returns list of apropriate UTXO's''' utxos = [] utxo_sum = Decimal(-0.01) # starts from negative due to minimal fee for tx in sorted(self.listunspent(address=address), key=itemgetter('confirmations')): if tx["address"] not in (self.pa_parameters.P2TH_addr, self.pa_parameters.test_P2TH_addr): utxos.append( MutableTxIn(txid=tx['txid'], txout=tx['vout'], sequence=Sequence.max(), script_sig=ScriptSig.empty())) utxo_sum += Decimal(tx["amount"]) if utxo_sum >= total_amount: return {'utxos': utxos, 'total': utxo_sum} if utxo_sum < total_amount: raise InsufficientFunds("Insufficient funds.")
def tx_in(tx, inscript): #TODO change from 1 i/p to multi i/p to_spend = Transaction.unhexlify(tx) intxs = [ TxIn(txid=to_spend.txid, txout=0, sequence=Sequence.max(), script_sig=inscript) ] #ScriptSig.<something> return intxs
def inputs(utxos, previous_transaction_indexes=None): inputs, amount = list(), int() for index, utxo in enumerate(utxos): if previous_transaction_indexes is None or index in previous_transaction_indexes: amount += utxo["amount"] inputs.append( TxIn(txid=utxo["hash"], txout=utxo["output_index"], script_sig=ScriptSig.empty(), sequence=Sequence.max())) return inputs, amount
def _get_unsigned_txn(self): # outputs_amounts is copied so any instance can be modified with change_fee, # and will still function correctly, i.e the change address won't already # be in the self._outputs_amounts dict self._modified_outputs_amounts = self.outputs_amounts.copy() # adding change address to outputs, if there is leftover balance that isn't dust if self._change_amount > 0: self._modified_outputs_amounts[self.change_address] = self._change_amount outputs = [] for i, (addr, amount) in enumerate(self._modified_outputs_amounts.items()): outputs.append(TxOut( value=amount, n=i, script_pubkey=self.get_script_pubkey(addr) )) inputs = [] for t in self._specific_utxo_data: # build inputs using the UTXO data in self._specific_utxo_data, # script_sig is empty as the transaction will be signed later inputs.append( TxIn(txid=t[0], txout=t[1], script_sig=ScriptSig.empty(), sequence=Sequence.max(), witness=Witness([StackData.zero()])) if self.is_segwit else None, ) if self.is_segwit: transaction = MutableSegWitTransaction( version=TX_VERSION, ins=inputs, outs=outputs, locktime=Locktime(self.locktime), ) else: transaction = MutableTransaction( version=TX_VERSION, ins=inputs, outs=outputs, locktime=Locktime(self.locktime) ) return transaction
def createContractInput(fundingTx): to_spend = fundingTx #TODO Failing here #Signer=fundingTxn() #Todo LATEST Failing here .Change to spend input Transaction Signer = ScriptSig.empty() #TOdo create SignFundingTx(FundingTx, Solver) SigScript = [ MutableTxIn( txid=to_spend, # Input need not be a segwit transaction. txout=0, script_sig=Signer, sequence=Sequence.max()) ] return SigScript
def _build_inputs( utxos: list, previous_transaction_indexes: Optional[list] = None) -> tuple: inputs, amount = [], 0 for index, utxo in enumerate(utxos): if previous_transaction_indexes is None or index in previous_transaction_indexes: amount += utxo["value"] inputs.append( TxIn(txid=utxo["tx_hash"], txout=utxo["tx_output_n"], script_sig=ScriptSig.empty(), sequence=Sequence.max())) return inputs, amount
def open_tx(committer, secret, commit_tx_hash): # 创建输入脚本 p2pkh_solver = P2pkhSolver(committer.privk) hasklock_solver = HashlockSolver(secret.encode(), p2pkh_solver) if_solver = IfElseSolver( Branch.IF, # branch selection hasklock_solver) # 创建输出脚本 script = P2pkhScript(committer.pubk) # 获取commit交易 to_spend_raw = get_raw_tx(commit_tx_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, committer.api_key, condidence='high') estimated_tx_size = cal_tx_size_in_byte(inputs_num=1, outputs_num=1) mining_fee = int(mining_fee_per_kb * (estimated_tx_size / 1000)) * 2 # 创建交易 unsigned = MutableTransaction(version=2, ins=[ TxIn(txid=to_spend.txid, txout=0, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=[ TxOut(value=penalty - mining_fee, n=0, script_pubkey=script), ], locktime=Locktime(0)) # 修改交易 signed = unsigned.spend([to_spend.outs[0]], [if_solver]) # 广播交易 print('open_tx_hex: ', signed.hexlify()) msg = pushtx(coin_symbol=coin_symbol, api_key=committer.api_key, tx_hex=signed.hexlify()) format_output(msg) return msg['tx']['hash']
def select_inputs(self, address: str, amount: int) -> dict: utxos = [] utxo_sum = Decimal(-0.01) # starts from negative due to minimal fee for tx in self.listunspent(address=address): utxos.append( TxIn(txid=tx['tx_hash'], txout=tx['tx_ouput_n'], sequence=Sequence.max(), script_sig=ScriptSig.empty()) ) utxo_sum += Decimal(tx['value'] / 10**8) if utxo_sum >= amount: return {'utxos': utxos, 'total': utxo_sum} if utxo_sum < amount: raise InsufficientFunds('Insufficient funds.')
def spendCb(fee, reward, objTx, outputs, cbSolver, dictIdToPub): """ create a single coinbase spend :param fee: fee :param reward: block reward in sat :param objTx: coinbase tx :param outputs: lst of channels (copies from originals) :param cbSolver: solver :param bPubMiner: pub of the miner :return: tx that spends coinbase """ outs = [] totVal = 0 chanIds = [] for o in outputs: v = int(o.value) bPubN1 = dictIdToPub[str(o.node1.nodeid)] bPubN2 = dictIdToPub[str(o.node2.nodeid)] if bPubN1.compressed < bPubN2.compressed: # lexicographical ordering multisig_script = MultisigScript(2, bPubN1, bPubN2, 2) else: multisig_script = MultisigScript(2, bPubN2, bPubN1, 2) p2wsh_multisig = P2wshV0Script(multisig_script) totVal += v outs += [TxOut(value=v, n=0, script_pubkey=p2wsh_multisig)] chanIds += [o.channelid] change = reward - totVal - fee outsWithChange = outs + [ TxOut(value=change, n=0, script_pubkey=objTx.outs[0].script_pubkey) ] unsignedCb = MutableTransaction(version=1, ins=[ TxIn(txid=objTx.txid, txout=0, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=outsWithChange, locktime=Locktime(0)) cbTx = unsignedCb.spend([objTx.outs[0]], [cbSolver]) return cbTx, (cbTx.txid, chanIds)
def select_inputs(self, address: str, amount: int) -> dict: utxos = [] utxo_sum = Decimal(-0.01) # starts from negative due to minimal fee for tx in self.listunspent(address=address): script = self.getrawtransaction( tx['txid'])['vout'][0]['scriptPubKey']['hex'] utxos.append( TxIn(txid=tx['txid'], txout=tx['vout'], sequence=Sequence.max(), script_sig=ScriptSig.unhexlify(script))) utxo_sum += Decimal(tx['amount']) if utxo_sum >= amount: return {'utxos': utxos, 'total': utxo_sum} if utxo_sum < amount: raise InsufficientFunds('Insufficient funds.') raise Exception("undefined behavior :.(")
def fundingTxn(): Signer = ScriptSig.empty() outvalue = fundingInput_value(fundingTxIn, 0) - 400 MultiSigTx = MutableSegWitTransaction( version=1, #Publish to the Blockchain ins=[ TxIn(txid=fundingTxIn_id, txout=0, script_sig=Signer, sequence=Sequence.max()) ], outs=[TxOut(value=outvalue, n=0, script_pubkey=FundingScript) ], # todo must change this to access the contract script locktime=Locktime(0)) MultiSigTxSigned = MultiSigTx.spend( [fundingTxIn.outs[0]], [funding_sig]) # ToDo Failing here - spend attribute not found print("funding tx signed ", MultiSigTxSigned.hexlify()) # return MultiSigTx,p2sh_solver,MultiSigTxSigned.outs[0]; # TODo when to Return Signed MultiSigTransaction return MultiSigTxSigned.txid, MultiSigTxSigned.outs[0]
def select_inputs(self, address: str, amount: int) -> dict: '''select UTXOs''' utxos = [] utxo_sum = Decimal(-0.01) # starts from negative due to minimal fee for tx in sorted(self.listunspent(address=address), key=itemgetter('confirmations')): utxos.append( TxIn(txid=tx['tx_hash'], txout=tx['tx_ouput_n'], sequence=Sequence.max(), script_sig=ScriptSig.unhexlify(tx['script'])) ) utxo_sum += Decimal(int(tx['value']) / 100000000) if utxo_sum >= amount: return {'utxos': utxos, 'total': utxo_sum} if utxo_sum < amount: raise InsufficientFunds('Insufficient funds.') raise Exception("undefined behavior :.(")
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 test_all(self): global keys priv = ExtendedPrivateKey.decode(keys[0][1]).key pk = priv.pub() addr_string = str(pk.to_address()) utxo = [] for i in range(3): # create 3 tx to add to UTXO txid = regtest.send_rpc_cmd(['sendtoaddress', addr_string, '100'], 0) to_spend = Transaction.unhexlify( regtest.send_rpc_cmd(['getrawtransaction', txid, '0'], 0)) txout = None for out in to_spend.outs: if str(out.script_pubkey.address()) == addr_string: txout = out break assert txout is not None utxo.append({ 'txid': txid, 'txout': txout, 'solver': P2pkhSolver(priv), 'next_seq': Sequence.max(), 'next_locktime': Locktime(0) }) regtest.send_rpc_cmd(['generate', '100'], 0) generate = False next_locktime = Locktime(0) next_sequence = Sequence.max() i = 0 while i < len(self.all) - 2: print('{:04d}\r'.format(i), end='', flush=True) ins = [ MutableTxIn(unspent['txid'], unspent['txout'].n, ScriptSig.empty(), unspent['next_seq']) for unspent in utxo ] outs = [] prev_types = [] for j, (unspent, script) in enumerate(zip(utxo, self.all[i:i + 3])): outs.append( TxOut(unspent['txout'].value - 1000000, j, script[0])) prev_types.append(script[2]) tx = MutableTransaction( 2, ins, outs, min_locktime(unspent['next_locktime'] for unspent in utxo)) mutable = copy.deepcopy(tx) tx = tx.spend([unspent['txout'] for unspent in utxo], [unspent['solver'] for unspent in utxo]) # print('====================') # print('txid: {}'.format(tx.txid)) # print() # print(tx) # print() # print('raw: {}'.format(tx.hexlify())) # print('prev_scripts, amounts, solvers:') print('TX: {}'.format(i)) regtest.send_rpc_cmd(['sendrawtransaction', tx.hexlify()], 0) print('Mempool size: {}'.format( len(regtest.send_rpc_cmd(['getrawmempool'], 0)))) if cmdline_args.dumpfile is not None: with open(cmdline_args.dumpfile, 'a') as out: for j, unspent in enumerate(utxo): json.dump( self.json_dump(unspent, tx.ins[j], j, copy.deepcopy(mutable).to_segwit()), out) out.write('\n') utxo = [] for j, (output, prev_type) in enumerate(zip(tx.outs, prev_types)): if 'time' in prev_type: if 'absolute' in prev_type: next_locktime = Locktime(100) next_sequence = Sequence(0xfffffffe) if 'relative' in prev_type: next_sequence = Sequence(3) generate = True else: next_locktime = Locktime(0) next_sequence = Sequence.max() utxo.append({ 'txid': tx.txid, 'txout': output, 'solver': self.all[i + j][1][0], # solver 'next_seq': next_sequence, 'next_locktime': next_locktime }) if generate: regtest.send_rpc_cmd(['generate', '4'], 0) generate = False if not i % 10: print('generating 2') regtest.send_rpc_cmd(['generate', '2'], 0) i += 1 ins = [ MutableTxIn(unspent['txid'], unspent['txout'].n, ScriptSig.empty(), unspent['next_seq']) for unspent in utxo ] tx = MutableTransaction( 2, ins, [ TxOut( sum(unspent['txout'].value for unspent in utxo) - 1000000, 0, self.final['script']) ], min_locktime(unspent['next_locktime'] for unspent in utxo)) tx = tx.spend([unspent['txout'] for unspent in utxo], [unspent['solver'] for unspent in utxo]) # print('====================') # print('txid: {}'.format(tx.txid)) # print() # print(tx) # print() # print('raw: {}'.format(tx.hexlify())) # print('prev_scripts, amounts, solvers:') # for unspent in utxo: # print(unspent['txout'].script_pubkey, unspent['txout'].value, unspent['solver'].__class__.__name__) regtest.send_rpc_cmd(['sendrawtransaction', tx.hexlify()], 0) regtest.teardown()
mining_fee = int(mining_fee_per_kb * (estimated_tx_size / 1000)) * 2 # 设置罚金 penalty = 100000 assert penalty + mining_fee <= balance, 'committer账户余额不足' # 创建交易 to_spend_raw = get_raw_tx(to_spend_hash, coin_symbol) to_spend = TransactionFactory.unhexlify(to_spend_raw) unsigned = MutableTransaction(version=2, ins=[ TxIn(txid=to_spend.txid, txout=0, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=[ TxOut(value=penalty, n=0, script_pubkey=lock_time_script), TxOut(value=balance - penalty - mining_fee, n=1, script_pubkey=change_script) ], locktime=Locktime(0)) # 输入脚本 solver = P2pkhSolver(privk) # 修改交易 signed = unsigned.spend([to_spend.outs[0]], [solver])
def test_all(self): global keys priv = PrivateKey.from_bip32(keys[0][1]) pk = priv.pub() addr_string = str(pk.to_address()) utxo = [] for i in range(3): # create 3 tx to add to UTXO txid = regtest.send_rpc_cmd(['sendtoaddress', addr_string, '100'], 0) to_spend = Transaction.unhexlify( regtest.send_rpc_cmd(['getrawtransaction', txid, '0'], 0)) txout = None for out in to_spend.outs: if str(out.script_pubkey.address()) == addr_string: txout = out break assert txout is not None utxo.append({ 'txid': txid, 'txout': txout, 'solver': P2pkhSolver(priv), 'next_seq': Sequence.max(), 'next_locktime': Locktime(0) }) regtest.send_rpc_cmd(['generate', '100'], 0) generate = False next_locktime = Locktime(0) next_sequence = Sequence.max() i = 0 # 1785 # 382 # 2376 # 1180 # while i < len(self.all) - 2: print('{:04d}\r'.format(i), end='') ins = [ MutableTxIn(unspent['txid'], unspent['txout'].n, ScriptSig.empty(), unspent['next_seq']) for unspent in utxo ] outs = [] prev_types = [] for j, (unspent, script) in enumerate(zip(utxo, self.all[i:i + 3])): outs.append( TxOut(unspent['txout'].value - 1000000, j, script[0])) prev_types.append(script[2]) tx = MutableTransaction( 2, ins, outs, min_locktime(unspent['next_locktime'] for unspent in utxo)) tx = tx.spend([unspent['txout'] for unspent in utxo], [unspent['solver'] for unspent in utxo]) # print('====================') # print('txid: {}'.format(tx.txid)) # print() # print(tx) # print() # print('raw: {}'.format(tx.hexlify())) # print('prev_scripts, amounts, solvers:') for unspent in utxo: if isinstance(unspent['solver'], P2shSolver): if isinstance(unspent['solver'].redeem_script_solver, P2wshV0Solver): prev = unspent[ 'solver'].redeem_script_solver.witness_script else: prev = unspent['solver'].redeem_script elif isinstance(unspent['solver'], P2wshV0Solver): prev = unspent['solver'].witness_script else: prev = unspent['txout'].script_pubkey print(prev, unspent['txout'].value, unspent['solver'].__class__.__name__) regtest.send_rpc_cmd(['sendrawtransaction', tx.hexlify()], 0) utxo = [] for j, (output, prev_type) in enumerate(zip(tx.outs, prev_types)): if 'time' in prev_type: if 'absolute' in prev_type: next_locktime = Locktime(100) next_sequence = Sequence(0xfffffffe) if 'relative' in prev_type: next_sequence = Sequence(3) generate = True else: next_locktime = Locktime(0) next_sequence = Sequence.max() utxo.append({ 'txid': tx.txid, 'txout': output, 'solver': self.all[i + j][1][0], # solver 'next_seq': next_sequence, 'next_locktime': next_locktime }) if generate: regtest.send_rpc_cmd(['generate', '4'], 0) generate = False if not i % 10: regtest.send_rpc_cmd(['generate', '2'], 0) i += 1 ins = [ MutableTxIn(unspent['txid'], unspent['txout'].n, ScriptSig.empty(), unspent['next_seq']) for unspent in utxo ] tx = MutableTransaction( 2, ins, [ TxOut( sum(unspent['txout'].value for unspent in utxo) - 1000000, 0, self.final['script']) ], min_locktime(unspent['next_locktime'] for unspent in utxo)) tx = tx.spend([unspent['txout'] for unspent in utxo], [unspent['solver'] for unspent in utxo]) # print('====================') # print('txid: {}'.format(tx.txid)) # print() # print(tx) # print() # print('raw: {}'.format(tx.hexlify())) # print('prev_scripts, amounts, solvers:') for unspent in utxo: print(unspent['txout'].script_pubkey, unspent['txout'].value, unspent['solver'].__class__.__name__) regtest.send_rpc_cmd(['sendrawtransaction', tx.hexlify()], 0) regtest.teardown()
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, address: str, transaction_hash: str, locktime: int = config["locktime"]) -> "RefundTransaction": """ Build Bitcoin refund transaction. :param address: Bitcoin sender address. :type address: str :param transaction_hash: Bitcoin funded transaction hash/id. :type transaction_hash: str :param locktime: Bitcoin transaction lock time, defaults to ``0``. :type locktime: int :returns: RefundTransaction -- Bitcoin refund transaction instance. >>> from swap.providers.bitcoin.transaction import RefundTransaction >>> refund_transaction: RefundTransaction = RefundTransaction("testnet") >>> refund_transaction.build_transaction(address="n1wgm6kkzMcNfAtJmes8YhpvtDzdNhDY5a", transaction_hash="a211d21110756b266925fee2fbf2dc81529beef5e410311b38578dc3a076fb31") <swap.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0> """ # Check parameter instances if not is_address(address, self._network): raise AddressError( f"Invalid Bitcoin sender '{address}' {self._network} address.") # Set address and transaction_hash self._address, self._transaction_hash, = address, transaction_hash # Get transaction self._transaction_detail = get_transaction( transaction_hash=self._transaction_hash, network=self._network) # Find HTLC UTXO self._htlc_utxo = find_p2sh_utxo(transaction=self._transaction_detail) if self._htlc_utxo is None: raise ValueError( "Invalid transaction id, there is no pay to script hash (P2SH) address." ) self._amount = self._htlc_utxo["value"] # Calculate the fee self._fee = fee_calculator(1, 1) outputs: list = [ TxOut(value=(self._amount - self._fee), n=0, script_pubkey=get_address_hash(address=self._address, script=True)) ] # Build mutable transaction self._transaction = MutableTransaction( version=self._version, ins=[ TxIn(txid=self._transaction_hash, txout=self._htlc_utxo["position"], script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=outputs, locktime=Locktime(locktime)) # Set transaction type self._type = "bitcoin_refund_unsigned" return self
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
def commit_tx(committer, receiver, secret, type, lock_time, penalty): if type not in ['Timed', 'Height']: raise ValueError("type should be 'Timed' or 'Height'") secret_hash = hashlib.sha256(hashlib.sha256( secret.encode()).digest()).digest() secret_hash = StackData.from_bytes(secret_hash) print("秘密经hash256加密结果:", secret_hash) # 创建输出脚本 if type is 'Height': # Relative - HeightBased sequence = lock_time lock_time_script = IfElseScript( # if branch Hashlock256Script(secret_hash, P2pkhScript(committer.pubk)), # else branch RelativeTimelockScript( # timelocked script HeightBasedSequence(sequence), # expiration P2pkhScript(receiver.pubk))) else: # Relative - TimedBased time_delta = datetime.timedelta(minutes=lock_time) time_now = datetime.datetime.now() print("current time: ", time_now.strftime("%y-%m-%d %H:%M:%S")) print("pay deposit time: ", (time_now + time_delta).strftime("%y-%m-%d %H:%M:%S")) lock_time_script = IfElseScript( # if branch Hashlock256Script(secret_hash, P2pkhScript(committer.pubk)), # else branch RelativeTimelockScript( # timelocked script TimeBasedSequence.from_timedelta(time_delta), # expiration P2pkhScript(receiver.pubk))) # 找零脚本 change_script = P2pkhScript(committer.pubk) # 清理资产 print("sweeping fund...") to_spend_hash, balance = sweep_fund(privkey=str(committer.privk), address=str(committer.address), coin_symbol=coin_symbol, api_key=committer.api_key) # 估算挖矿费用 print('estimating mining fee...') mining_fee_per_kb = get_mining_fee_per_kb(coin_symbol, committer.api_key, condidence='high') estimated_tx_size = cal_tx_size_in_byte(inputs_num=1, outputs_num=2) mining_fee = int(mining_fee_per_kb * (estimated_tx_size / 1000)) * 2 # 设置罚金 assert penalty + mining_fee <= balance, 'committer账户余额不足' # 创建交易 to_spend_raw = get_raw_tx(to_spend_hash, coin_symbol) to_spend = TransactionFactory.unhexlify(to_spend_raw) unsigned = MutableTransaction(version=2, ins=[ TxIn(txid=to_spend.txid, txout=0, script_sig=ScriptSig.empty(), sequence=Sequence.max()) ], outs=[ TxOut(value=penalty, n=0, script_pubkey=lock_time_script), TxOut(value=balance - penalty - mining_fee, n=1, script_pubkey=change_script) ], locktime=Locktime(0)) # 输入脚本 solver = P2pkhSolver(committer.privk) # 修改交易 signed = unsigned.spend([to_spend.outs[0]], [solver]) print('commit_tx_hex: ', signed.hexlify()) # 发送交易 tx = pushtx(coin_symbol=coin_symbol, api_key=committer.api_key, tx_hex=signed.hexlify()) format_output(tx) return tx['tx']['hash']