def add_output(self, serial_id: int, script: str, sats: int) -> None: txout = CTxOut(sats, CScript(bytes.fromhex(script))) self.outputs.append({'output': txout, 'serial_id': serial_id})
def add_witnesses(self, witness_stack) -> str: wits = [] for idx, _in in enumerate(self.inputs): privkey = _in['privkey'] serial_id = _in['serial_id'] if privkey: inkey = privkey_expand(privkey) inkey_pub = coincurve.PublicKey.from_secret(inkey.secret) address = P2WPKHBitcoinAddress.from_scriptPubKey(CScript([script.OP_0, Hash160(inkey_pub.format())])) sighash = script.SignatureHash(address.to_redeemScript(), self.tx, idx, script.SIGHASH_ALL, amount=_in['sats'], sigversion=script.SIGVERSION_WITNESS_V0) sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL]) wits.append(CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))) continue # Every input from the witness stack will be the accepter's # which is always an odd serial assert(serial_id % 2 == 1) elems = witness_stack.pop(0)['witness_element'] stack = [] for elem in elems: stack.append(bytes.fromhex(elem['witness'])) wits.append(CTxInWitness(CScriptWitness(stack))) self.tx.wit = CTxWitness(wits) return self.tx.serialize().hex()
def create_trx(op_return_val, issuing_transaction_fee, issuing_address, tx_outs, tx_inputs): """ :param op_return_val: :param issuing_transaction_fee: :param issuing_address: :param tx_outs: :param tx_input: :return: """ cert_out = CMutableTxOut(0, CScript([OP_RETURN, op_return_val])) tx_ins = [] value_in = 0 for tx_input in tx_inputs: tx_ins.append(CTxIn(COutPoint(tx_input.tx_hash, tx_input.tx_out_index))) value_in += tx_input.coin_value # send change back to our address amount = value_in - issuing_transaction_fee if amount > 0: change_out = create_transaction_output(issuing_address, amount) tx_outs = tx_outs + [change_out] tx_outs = tx_outs + [cert_out] transaction = CMutableTransaction(tx_ins, tx_outs) return transaction
def from_utxo(txid_in: str, tx_index_in: int, sats: int, privkey: str, fee: int, local_node_privkey: str, local_funding_privkey: str, remote_node_privkey: str, remote_funding_privkey: str, chain_hash: str = regtest_hash) -> Tuple['Funding', str]: """Make a funding transaction by spending this utxo using privkey: return Funding, tx.""" # Create dummy one to start: we will fill in txid at the end. funding = Funding('', 0, sats - fee, local_node_privkey, local_funding_privkey, remote_node_privkey, remote_funding_privkey, chain_hash) # input private key. inkey = privkey_expand(privkey) inkey_pub = coincurve.PublicKey.from_secret(inkey.secret) txin = CTxIn(COutPoint(bytes.fromhex(txid_in), tx_index_in)) txout = CTxOut( sats - fee, CScript([script.OP_0, sha256(funding.redeemscript()).digest()])) tx = CMutableTransaction([txin], [txout]) # now fill in funding txid. funding.txid = tx.GetTxid().hex() # while we're here, sign the transaction. address = P2WPKHBitcoinAddress.from_scriptPubKey( CScript([script.OP_0, Hash160(inkey_pub.format())])) sighash = script.SignatureHash(address.to_redeemScript(), tx, 0, script.SIGHASH_ALL, amount=sats, sigversion=script.SIGVERSION_WITNESS_V0) sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL]) tx.wit = CTxWitness( [CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))]) return funding, tx.serialize().hex()
def mock_listunspent(self, addrs): output1 = {'outpoint': COutPoint(lx('34eb81bc0d1a822369f75174fd4916b1ec490d8fbcba33168e820cc78a52f608'), 0), 'confirmations': 62952, 'address': P2PKHBitcoinAddress('mz7poFND7hVGRtPWjiZizcCnjf6wEDWjjT'), 'spendable': False, 'amount': 49000000, 'solvable': False, 'scriptPubKey': CScript( [OP_DUP, OP_HASH160, x('cc0a909c4c83068be8b45d69b60a6f09c2be0fda'), OP_EQUALVERIFY, OP_CHECKSIG]), 'account': ''} output2 = {'address': P2PKHBitcoinAddress('mz7poFND7hVGRtPWjiZizcCnjf6wEDWjjT'), 'amount': 2750, 'account': '', 'spendable': False, 'solvable': False, 'confirmations': 62932, 'outpoint': COutPoint(lx('6773785b4dc5d2cced67d26fc0820329307a8e10dfaef50d506924984387bf0b'), 1), 'scriptPubKey': CScript( [OP_DUP, OP_HASH160, x('cc0a909c4c83068be8b45d69b60a6f09c2be0fda'), OP_EQUALVERIFY, OP_CHECKSIG])} output3 = {'address': P2PKHBitcoinAddress('mz7poFND7hVGRtPWjiZizcCnjf6wEDWjjT'), 'amount': 2750, 'account': '', 'spendable': False, 'solvable': False, 'confirmations': 62932, 'outpoint': COutPoint(lx('6773785b4dc5d2cced67d26fc0820329307a8e10dfaef50d506924984387bf0b'), 5), 'scriptPubKey': CScript( [OP_DUP, OP_HASH160, x('cc0a909c4c83068be8b45d69b60a6f09c2be0fda'), OP_EQUALVERIFY, OP_CHECKSIG])} unspent_outputs = [output1, output2, output3] return unspent_outputs
def forge_prevtx( vouts: Sequence[Tuple[str, int]], network: str = "mainnet") -> Tuple[bytes, messages.TransactionType]: """ Forge a transaction with the given vouts. """ bitcoin.SelectParams(network) input = messages.TxInputType( prev_hash=b"\x00" * 32, prev_index=0xFFFFFFFF, script_sig=b"\x00", sequence=0xFFFFFFFF, ) outputs = [ messages.TxOutputBinType( amount=amount, script_pubkey=bytes(CBitcoinAddress(address).to_scriptPubKey()), ) for address, amount in vouts ] tx = messages.TransactionType( version=1, inputs=[input], bin_outputs=outputs, lock_time=0, ) cin = CTxIn( COutPoint(input.prev_hash, input.prev_index), CScript(input.script_sig), input.sequence, ) couts = [ CTxOut(output.amount, CScript(output.script_pubkey)) for output in tx.bin_outputs ] txhash = CTransaction([cin], couts, tx.lock_time, tx.version).GetTxid()[::-1] bitcoin.SelectParams("mainnet") return txhash, tx
def get_unspent_outputs(self, address): unspent_outputs = [] # this calls a different api not accessible through localhost proxy unspent_url = 'https://blockchain.info/unspent?active=%s&format=json' % address unspent_response = try_get(unspent_url) r = unspent_response.json() for u in r['unspent_outputs']: tx_out = TransactionOutput( COutPoint(unhexlify(u['tx_hash']), u['tx_output_n']), CBitcoinAddress(address), CScript(unhexlify(u['script'])), int(u['value'])) unspent_outputs.append(tx_out) return unspent_outputs
def create_trx(op_return_val, issuing_transaction_cost, issuing_address, txouts, tx_input): cert_out = CMutableTxOut(0, CScript([OP_RETURN, op_return_val])) txins = [CTxIn(tx_input.outpoint)] value_in = tx_input.amount # send change back to our address amount = value_in - issuing_transaction_cost.total if amount > 0: change_out = create_transaction_output(issuing_address, amount) txouts = txouts + [change_out] txouts = txouts + [cert_out] tx = CMutableTransaction(txins, txouts) return tx
def _build_certificate_transactions(wallet, issuing_address, revocation_address, certificate_metadata, fees, tail): """Make transactions for the certificates. """ logging.info('Creating tx of certificate for recipient uid: %s ...', certificate_metadata.uid) with open(certificate_metadata.certificate_hash_file_name, 'rb') as in_file: # this is the recipient-specific hash that will be recorded on the # blockchain hashed_certificate = in_file.read() cert_out = CMutableTxOut(0, CScript([OP_RETURN, hashed_certificate])) # send a transaction to the recipient's public key, and to a revocation # address txouts = create_recipient_outputs(certificate_metadata.pubkey, revocation_address, fees.min_per_output) # define transaction inputs unspent_outputs = wallet.get_unspent_outputs(issuing_address) last_input = unspent_outputs[tail] txins = [CTxIn(last_input.outpoint)] value_in = last_input.amount # very important! If we don't send the excess change back to ourselves, # some lucky miner gets it! amount = value_in - fees.cost_per_transaction if amount > 0: change_out = create_transaction_output(issuing_address, amount) txouts = txouts + [change_out] txouts = txouts + [cert_out] tx = CMutableTransaction(txins, txouts) # this is the transaction for a recipient, unsigned hextx = hexlify(tx.serialize()) with open(certificate_metadata.unsigned_tx_file_name, 'wb') as out_file: out_file.write(bytes(hextx, 'utf-8')) logging.info('Created unsigned tx for recipient; last_input=%s', last_input) return last_input
def add_input_output(bitcoind, tx): """Add an input and an output to a CMutableTransaction, SIGHASH_ALL.""" # First we get some coins privkey = CKey(os.urandom(32)) scriptPubKey = CScript([OP_0, Hash160(privkey.pub)]) address = CBitcoinAddress.from_scriptPubKey(scriptPubKey) amount = Decimal("50") * Decimal(COIN) - Decimal("500") # This creates a one-output transaction txid = bitcoind.pay_to(str(address), amount / Decimal(COIN)) # We bump the fees by 5000 tx.vout.append(CTxOut(amount - Decimal("5000"), scriptPubKey)) tx.vin.append(CTxIn(COutPoint(lx(txid), 0))) # Sign the new output with ALL tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL, int(amount), SIGVERSION_WITNESS_V0) sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL]) tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))
def add_input_output(bitcoind, tx, coin, fees): """Add another input to the transaction to bump the feerate.""" coin_amount = Decimal(coin["amount"]) * Decimal(COIN) # First get the private key from bitcoind's wallet. privkey = CKey(wif_decode(bitcoind.dumpprivkey(coin["address"]))) # Add the fetched coin as a new input. tx.vin.append(CTxIn(COutPoint(lx(coin["txid"]), coin["vout"]))) # And likely add an output, otherwise all goes to the fees. scriptPubKey = CScript([OP_0, Hash160(privkey.pub)]) if coin_amount > fees + 294: # For simplicity, pay to the same script tx.vout.append(CTxOut(coin_amount - Decimal(fees), scriptPubKey)) address = CBitcoinAddress.from_scriptPubKey(scriptPubKey) # We only do this once, sign it with ALL tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL, int(coin_amount), SIGVERSION_WITNESS_V0) sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL]) tx.wit.vtxinwit.append( CTxInWitness(CScriptWitness([sig, privkey.pub])) ) return tx
def close_tx(self, fee: int, privkey_dest: str) -> str: """Create a (mutual) close tx""" txin = CTxIn(COutPoint(bytes.fromhex(self.txid), self.output_index)) out_privkey = privkey_expand(privkey_dest) txout = CTxOut( self.amount - fee, CScript([ script.OP_0, Hash160( coincurve.PublicKey.from_secret( out_privkey.secret).format()) ])) tx = CMutableTransaction(vin=[txin], vout=[txout]) sighash = script.SignatureHash(self.redeemscript(), tx, inIdx=0, hashtype=script.SIGHASH_ALL, amount=self.amount, sigversion=script.SIGVERSION_WITNESS_V0) sigs = [ key.sign(sighash, hasher=None) for key in self.funding_privkeys_for_tx() ] # BOLT #3: # ## Closing Transaction # ... # * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>` witness = CScriptWitness([ bytes(), sigs[0] + bytes([script.SIGHASH_ALL]), sigs[1] + bytes([script.SIGHASH_ALL]), self.redeemscript() ]) tx.wit = CTxWitness([CTxInWitness(witness)]) return tx.serialize().hex()
def transfer(self, src_addrs_key_map: OrderedDict, dst_addrs_amount_map: dict, txfee: Decimal, auto_calc_pay_back: bool, pay_back_index: int = 0xfffffff, ensure_one_txout: bool = False) -> str: """ :param src_addrs_key_map: {'addr1': 'privkey1', 'addr2': 'privkey2' } 私钥为 hex字符串 :param dst_addrs_amount_map: {'addr1':Decimal(0.1234), 'addr2':Deciaml(0.234) } :param txfee: 矿工费 :param auto_calc_pay_back: 是否自动计算并找零 :param pay_back_index: 找零地址索引 即在 src_addrs_key_map 中的索引 :param ensure_one_txout: 确认只有一个交易输出(比如: 提币不需要找零的情况, 归集) :return: txid """ #仅支持 P2PKH 类型的from地址 assert isinstance(src_addrs_key_map, OrderedDict), 'src_addrs_key_map is not OrderedDict' assert isinstance(dst_addrs_amount_map, dict), 'dst_addrs_amount_map is not dict' assert Decimal('0.00001') <= txfee <= Decimal( '0.001'), 'invalid txfee, please check txfee' assert len( src_addrs_key_map) >= 1, 'src_addrs_key_map length must >= 1' assert not (True == auto_calc_pay_back == ensure_one_txout) , \ 'True == auto_calc_pay_back == ensure_one_txout , must be mutex ' if ensure_one_txout: assert (len(dst_addrs_amount_map) == 1 ), 'dst_addrs_amount_map length must equal 1' elif not auto_calc_pay_back: assert (len(dst_addrs_amount_map) >= 1), 'dst_addrs_amount_map length must >= 2' if auto_calc_pay_back and pay_back_index >= len(src_addrs_key_map): raise Exception('pay_back_index is to large') self.logger.info( f'dst_addrs_amount_map is { json.dumps(dst_addrs_amount_map, indent=4, default=decimal_default) }' ) assert self.ping() == True, 'bitcoind rpc is gone' total_amount = sum(dst_addrs_amount_map.values()) + txfee self.logger.info(f'total_amount is {total_amount}') source_addrs = list(src_addrs_key_map.keys()) #WARNING: 禁止打印私钥!!! is_enough, founded_utxos = self.search_utxo(addrs=source_addrs, total_amount=total_amount) if not is_enough: msg = 'balance is not enough' self.logger.error(msg) raise Exception(msg) self.logger.info( f'founded_utxos is { json.dumps(founded_utxos, indent=4, default=decimal_default) }' ) #设置全局变量 SelectParams(self.net_type) #构造inputs txins = [] utxo_owner_map = dict() for addr, utxos in founded_utxos.items(): assert addr in src_addrs_key_map, 'addr is not in src_addrs_key_map' for utxo in utxos: txin = CMutableTxIn( prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout'])) txins.append(txin) #因为顺序不会被打乱, 所以可以直接使用 索引进行对应 utxo_owner_map[len(txins) - 1] = addr #构造outputs txouts = [] for to_addr, amount in dst_addrs_amount_map.items(): out = CMutableTxOut( nValue=amount * COIN, scriptPubKey=CBitcoinAddress(to_addr).to_scriptPubKey()) txouts.append(out) #自动结算 if auto_calc_pay_back: sum_in_satoshi = 0 for addr, utxos in founded_utxos.items(): for utxo in utxos: sum_in_satoshi += utxo['value'] pay_back_in_satoshi = int(sum_in_satoshi - int(total_amount * COIN)) #判断找零的金额, 如果太小就不要了, 作为矿工费 if pay_back_in_satoshi >= MIN_AVAILABLE_UTXO_VALUE_IN_SATOSHI: pay_back_addr = list(src_addrs_key_map.keys())[pay_back_index] pay_back_out = CMutableTxOut( nValue=pay_back_in_satoshi, scriptPubKey=CBitcoinAddress( pay_back_addr).to_scriptPubKey()) txouts.append(pay_back_out) muttx = CMutableTransaction(vin=txins, vout=txouts) #对每个 input 进行签名 for n in range(len(txins)): #查找这个utxo属于哪个地址 owner_addr = utxo_owner_map[n] privkey = src_addrs_key_map[owner_addr] if len(privkey) == 64: # hex wif_key = PrivKeyToWIFCompress(privkey, self.net_type != 'mainnet') seckey = CBitcoinSecret(s=wif_key) elif len(privkey) == 52: #base58格式 seckey = CBitcoinSecret(s=privkey) else: raise Exception("invalid privkey") txin_script_pubkey = CScript([ OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG ]) sig_hash = SignatureHash(txin_script_pubkey, muttx, n, SIGHASH_ALL) sig = seckey.sign(sig_hash) + bytes([SIGHASH_ALL]) muttx.vin[n].scriptSig = CScript([sig, seckey.pub]) #TODO: 处理验签失败抛异常 VerifyScript(muttx.vin[n].scriptSig, txin_script_pubkey, muttx, n, (SCRIPT_VERIFY_P2SH, )) pass raw_tx_hex = b2x(muttx.serialize()) self.logger.info(f'raw_tx_hex is: {raw_tx_hex}') # local_tx_hash = muttx.GetTxid() # self.logger.info(f'local_tx_hash is: {b2x(local_tx_hash)}') #广播交易 assert self.ping( ) == True, 'bitcoind rpc is gone' #测试 bitcoind的 rpc服务是否还在 txid = self.send_raw_tx(raw_tx_hex=raw_tx_hex) self.logger.info(f'send_raw_tx txid is: {txid}') return txid
for x in hashes_missing_in_genesis_block: if missing_addresses[x] in ignore_because_refunded: continue print " ->",x," height:" + str(missing_heights[x]) + " XEL:" + amtamt(missing_heights[x],float(missing_amounts_per_address[missing_addresses[x]])) + " [" + missing_addresses[x] + "] =",missing_amounts_per_address[missing_addresses[x]],"BTC in total (dont count addrs twice)" print "\n\nNow, Handing the Abnormal ..." # Now handling the abnormals abnormal_add_map = {} btc_abnormal = 0 for x in abnormal: btc_abnormal += float(x["btc_amount"]) scr = CScript(hash_to_script[x["btc_tx"]].decode("hex")) reas="" for kk in iter(scr): reas = kk scr2 = CScript(reas) reas="" want = 0 have = 0 cnt=0 for kk in iter(scr2): if cnt==0: want=kk else: if len(str(kk))>30: reas += str(P2PKHBitcoinAddress.from_pubkey(kk)) + "-" else:
def locking_script_keys(key_one: coincurve.PublicKey, key_two: coincurve.PublicKey) -> CScript: return CScript([script.OP_0, sha256(Funding.redeemscript_keys(key_one, key_two)).digest()])
def redeemscript_keys(key_one: coincurve.PublicKey, key_two: coincurve.PublicKey) -> CScript: return CScript([script.OP_2] + [k.format() for k in Funding.sort_by_keys(key_one, key_two, key_one, key_two)] + [script.OP_2, script.OP_CHECKMULTISIG])
def create_raw_velvet_tx(payload_buf): VELVET_FORK_MARKER = b'interlink' digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, VELVET_FORK_MARKER, payload_buf]))] tx = CMutableTransaction([], digest_outs) return tx.serialize().hex()
def __init__(self, raw_hex): self._hex = raw_hex self._script = CScript(self._hex) self._operations = None
def redeemscript(self) -> CScript: return CScript([script.OP_2] + [k.format() for k in self.funding_pubkeys_for_tx()] + [script.OP_2, script.OP_CHECKMULTISIG])