def get_txid(tx): try: t = Transaction.from_string(tx) except: t = LTransaction.from_string(tx) for inp in t.vin: inp.scriptSig = Script(b"") return t.txid().hex()
def test_understandTransaction(): mytx = Transaction.from_string(tx1_confirmed["hex"]) assert mytx.version == 2 assert mytx.locktime == 415 assert type(mytx.vin[0]) == TransactionInput assert (hexlify( mytx.vin[0].txid ) == b"c7c9dd852fa9cbe72b2f6e3b2eeba1a2b47dc4422b3719a55381be8010d7993f") assert mytx.vout[0].value == 1999999890 assert (hexlify(mytx.txid( )) == b"42f5c9e826e52cde883cde7a6c7b768db302e0b8b32fc52db75ad3c5711b4a9e")
def __init__(self, rpc, addresses, **kwargs): self.rpc = rpc self._addresses = addresses # copy kwargs = dict(**kwargs) # replace with None or convert for i, k in enumerate(self.columns): v = kwargs.get(k, "") kwargs[k] = None if v in ["", None] else self.type_converter[i](v) super().__init__(**kwargs) # parse transaction self.tx = (None if not self["hex"] else Transaction.parse( bytes.fromhex(self["hex"])))
def convert_rawtransaction_to_psbt(wallet_rpc, rawtransaction) -> str: """ Converts a raw transaction in HEX format into a PSBT in b64 format """ tx = Transaction.from_string(rawtransaction) psbt = PSBT(tx) # this empties the signatures psbt = wallet_rpc.walletprocesspsbt(str(psbt), False).get("psbt", str(psbt)) psbt = PSBT.from_string(psbt) # we need the class object again # Recover signatures (witness or scriptsig) if available in raw tx for vin, psbtin in zip(tx.vin, psbt.inputs): if vin.witness: psbtin.final_scriptwitness = vin.witness if vin.script_sig: psbtin.final_scriptsig = vin.script_sig b64_psbt = str(psbt) return b64_psbt
def decoderawtransaction(hextx, chain="main"): raw = bytes.fromhex(hextx) tx = Transaction.parse(raw) txhash = sha256(sha256(raw).digest()).digest()[::-1].hex() txsize = len(raw) if tx.is_segwit: # tx size - flag - marker - witness non_witness_size = ( txsize - 2 - sum([len(inp.witness.serialize()) for inp in tx.vin])) witness_size = txsize - non_witness_size weight = non_witness_size * 4 + witness_size vsize = math.ceil(weight / 4) else: vsize = txsize weight = txsize * 4 result = { "txid": tx.txid().hex(), "hash": txhash, "version": tx.version, "size": txsize, "vsize": vsize, "weight": weight, "locktime": tx.locktime, "vin": [decoderawinput(vin) for vin in tx.vin], "vout": [ dict(decoderawoutput(vout, chain), n=i) for i, vout in enumerate(tx.vout) ], } return result
def main(): # all from the same private key prv = ec.PrivateKey.from_wif( "L2e5y14ZD3U1J7Yr62t331RtYe2hRW2TBBP8qNQHB8nSPBNgt6dM") pub = prv.get_public_key() inputs = [ # legacy { "txid": unhexlify( "7f0c7538e898bbe5531fa47d4057b52c914ec45e20ae1a5572ea1005a8ba50f8" ), "vout": 0, "value": int(1e8), "script": script.p2pkh(pub), }, # native segwit { "txid": unhexlify( "f51e6fc2392558a70ae970e93538f368828ad2800a7370f372a652de463429fc" ), "vout": 0, "value": int(2e8), "script": script.p2wpkh(pub), }, # nested segwit { "txid": unhexlify( "2e4cb680ed008b6e529c4c83f00d55326a2e68b48ddf11267e3f5323006966a6" ), "vout": 1, "value": int(3e8), "script": script.p2sh(script.p2wpkh(pub)), "redeem": script.p2wpkh(pub), }, ] # sending back almost the same amount vin = [TransactionInput(inp["txid"], inp["vout"]) for inp in inputs] vout = [ TransactionOutput(inp["value"] - 1500, inp["script"]) for inp in inputs ] tx = Transaction(vin=vin, vout=vout) print("Unsigned transaction:") print(hexlify(tx.serialize()).decode("utf-8")) for i in range(len(inputs)): inp = inputs[i] script_type = inp["script"].script_type() # legacy input if script_type == "p2pkh": h = tx.sighash_legacy(i, inp["script"]) sig = prv.sign(h) tx.vin[i].script_sig = script.script_sig_p2pkh(sig, pub) # native segwit elif script_type == "p2wpkh": sc = script.p2pkh_from_p2wpkh(inp["script"]) h = tx.sighash_segwit(i, sc, inp["value"]) sig = prv.sign(h) tx.vin[i].witness = script.witness_p2wpkh(sig, pub) # nested segwit elif script_type == "p2sh": if "redeem" in inp and inp["redeem"].script_type() == "p2wpkh": sc = script.p2pkh_from_p2wpkh(inp["redeem"]) h = tx.sighash_segwit(i, sc, inp["value"]) sig = prv.sign(h) tx.vin[i].script_sig = script.script_sig_p2sh(inp["redeem"]) tx.vin[i].witness = script.witness_p2wpkh(sig, pub) else: raise NotImplementedError("Script type is not supported") else: raise NotImplementedError("Script type is not supported") print("Signed transaction:") print(hexlify(tx.serialize()).decode("utf-8"))
def test_miniscript(self): # and(pk(A),after(100)) -> and_v(v:pk(A),after(100)) path = "49h/1h/0h/2h" fgp = sim.query("fingerprint").decode() xpub = sim.query(f"xpub m/{path}").decode() desc = f"wsh(and_v(v:pk([{fgp}/{path}]{xpub}"+"/{0,1}/*),after(10)))" res = sim.query("addwallet mini&"+desc, [True]) wname = wallet_prefix+"_mini" d = Descriptor.from_string(desc) addr = d.derive(5).address(NETWORKS['regtest']) # check it finds the wallet correctly sc = d.derive(5).witness_script().data.hex() res = sim.query(f"showaddr wsh m/{path}/0/5 {sc}", [True]) self.assertEqual(res.decode(), addr) d1 = d.derive(2, branch_index=0) d2 = d.derive(3, branch_index=1) # recv addr 2 addr1 = d1.address(NETWORKS['regtest']) # change addr 3 addr2 = d2.address(NETWORKS['regtest']) res = sim.query(f"bitcoin:{addr1}?index=2", [True]) # check it's found self.assertFalse(b"Can't find wallet" in res) rpc.createwallet(wname, True, True) w = rpc.wallet(wname) res = w.importmulti([{ "scriptPubKey": {"address": addr1},#d1.script_pubkey().data.hex(), # "witnessscript": d1.witness_script().data.hex(), # "pubkeys": [k.sec().hex() for k in d1.keys], "internal": False, "timestamp": "now", "watchonly": True, },{ "scriptPubKey": {"address": addr2},#d2.script_pubkey().data.hex(), # "witnessscript": d2.witness_script().data.hex(), # "pubkeys": [k.sec().hex() for k in d2.keys], "internal": True, "timestamp": "now", "watchonly": True, }],{"rescan": False}) self.assertTrue(all([k["success"] for k in res])) wdefault.sendtoaddress(addr1, 0.1) rpc.mine() unspent = w.listunspent() self.assertTrue(len(unspent) > 0) unspent = [{"txid": u["txid"], "vout": u["vout"]} for u in unspent[:1]] tx = w.createrawtransaction(unspent, [{wdefault.getnewaddress(): 0.002},{addr2: 0.09799}]) psbt = PSBT.from_base64(w.converttopsbt(tx)) # locktime magic :) psbt.tx.locktime = 11 psbt.tx.vin[0].sequence = 10 # fillinig psbt with data psbt.inputs[0].witness_script = d1.witness_script() pub = ec.PublicKey.parse(d1.keys[0].sec()) psbt.inputs[0].bip32_derivations[pub] = DerivationPath(bytes.fromhex(fgp), bip32.parse_path(f"m/{path}")+[0,2]) tx = w.gettransaction(unspent[0]["txid"]) t = Transaction.from_string(tx["hex"]) psbt.inputs[0].witness_utxo = t.vout[unspent[0]["vout"]] psbt.outputs[1].witness_script = d2.witness_script() pub2 = ec.PublicKey.parse(d2.keys[0].sec()) psbt.outputs[1].bip32_derivations[pub2] = DerivationPath(bytes.fromhex(fgp), bip32.parse_path(f"m/{path}")+[1,3]) unsigned = psbt.to_base64() # confirm signing signed = sim.query("sign "+unsigned, [True]) stx = PSBT.from_base64(signed.decode()) # signed tx t = psbt.tx # print(stx) t.vin[0].witness = Witness([stx.inputs[0].partial_sigs[pub], psbt.inputs[0].witness_script.data]) # broadcast with self.assertRaises(Exception): res = rpc.sendrawtransaction(t.serialize().hex()) rpc.mine(11) res = rpc.sendrawtransaction(t.serialize().hex()) rpc.mine() self.assertEqual(len(bytes.fromhex(res)), 32)
per_input_size += sigs_size # Now when we have all utxos and size estimates # we can construct a transaction # Very stupid coin selection: # we just go through utxos and add them until we have enough for destination + fee spending_amount = 0 fee = fee_rate * no_input_size inputs = [] for utxo in utxos: # get full prev tx if we are using legacy (or Trezor) if not d.is_segwit: utxo["non_witness_utxo"] = Transaction.from_string( s.get(f"{API}/tx/{utxo['txid']}/hex").text) inputs.append(utxo) spending_amount += utxo["value"] fee += per_input_size * fee_rate if spending_amount >= AMOUNT + fee: break if spending_amount < AMOUNT + fee: raise RuntimeError("Not enough funds") # round fee to satoshis fee = int(fee) + 1 vin = [ TransactionInput(bytes.fromhex(inp["txid"]), inp["vout"]) for inp in inputs ] vout = [TransactionOutput(AMOUNT, script.address_to_scriptpubkey(DESTINATION))]