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 get_secret(open_tx_hash): # 获取Open交易 raw_open_tx = get_raw_tx(open_tx_hash, coin_symbol) open_tx_raw = TransactionFactory.unhexlify( raw_open_tx) # decode raw transaction open_tx_json = open_tx_raw.to_json() # 获取输入脚本 scriptSig = open_tx_json['vin'][0]['scriptSig']['asm'] # 将输入脚本按空格分割 scriptSig_list = scriptSig.split() # 如无意外,秘密应该在脚本的倒数第二个位置 secret_hex = scriptSig_list[-2] # 从hex将秘密解码出来 print('secret hex: ', secret_hex) print('decoded secret: ', bytes.fromhex(secret_hex).decode())
# 估算挖矿费用 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=2) 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)
from utils import * from btcpy.setup import setup from btcpy.structs.transaction import TransactionFactory # global setup('testnet', strict=True) coin_symbol = 'btc-testnet' # 获取Open交易 open_tx_hash = '61fe8f28851536c3778b90ba58d649751e5b088af26069992fe52faca0239828' # open交易的hash raw_open_tx = get_raw_tx(open_tx_hash, coin_symbol) open_tx = TransactionFactory.unhexlify(raw_open_tx) # decode raw transaction open_tx = open_tx.to_json() # 获取输入脚本 scriptSig = open_tx['vin'][0]['scriptSig']['asm'] # 将输入脚本按空格分割 scriptSig_list = scriptSig.split() # 如无意外,秘密应该在脚本的倒数第二个位置 secret_hex = scriptSig_list[-2] # 从hex将秘密解码出来 print('secret hex: ', secret_hex) print('decoded secret: ', bytes.fromhex(secret_hex).decode())
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 = TransactionFactory.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 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]) print('Spending `{}`, sighashes: {}'.format( get_type(unspent['solver']), ', '.join([ str(sh) for sh in unspent['solver'].get_sighashes() ]))) tx = MutableTransaction(2, ins, outs, Locktime(0)) mutable = copy.deepcopy(tx) tx = tx.spend([unspent['txout'] for unspent in utxo], [unspent['solver'] for unspent in utxo]) # print(json.dumps({'signed': tx.to_json()})) # 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 'relativetime' in prev_type: generate = True utxo.append({ 'txid': tx.txid, 'txout': output, 'solver': self.all[i + j][1][0], # solver 'next_seq': Sequence.max(), 'next_locktime': Locktime(0) }) 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()
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']