1200wd / bitcoinlib

Bitcoin and other Cryptocurrencies Library for Python. Includes a fully functional wallet, Mnemonic key generation and management and connection with various service providers to receive and send blockchain and transaction information.
http://bitcoinlib.readthedocs.io/
GNU General Public License v3.0
609 stars 204 forks source link

Error validating transaction: witness script detected in tx without witness data #415

Closed Gabi201265 closed 2 months ago

Gabi201265 commented 2 months ago

Hi,

I have an issue. I try to develop a script to automate transaction.

Here is my code :


import requests
from bip_utils import Bip39SeedGenerator, Bip44Changes, Bip84, Bip84Coins
from bitcoinlib.transactions import Transaction, Output
from bitcoinlib.keys import Key
from bitcoinlib.wallets import Wallet

# Seed phrase
seed_phrase = "my_seed_phrase"

# BIP84
destination_address = ""

# Transaction fee (BTC)
tx_fee = 0.0000025  # 

# BlockCypher API base URL et token
base_url = "https://api.blockcypher.com/v1/btc/main"
api_token = ""  # Remplace by your token

def get_balance(address):
    response = requests.get(f"{base_url}/addrs/{address}/balance?token={api_token}")
    data = response.json()
    return data.get('final_balance', 0) / 100000000  # Satoshis to BTC

# UTXOs
def get_utxos(address):
    response = requests.get(f"{base_url}/addrs/{address}?token={api_token}&unspentOnly=true")
    return response.json().get('txrefs', [])

# Convert BTC to Satoshis
def btc_to_satoshis(btc_amount):
    return int(btc_amount * 100000000)

# seed generation
seed_bytes = Bip39SeedGenerator(seed_phrase).Generate()

# BIP84
bip84_mst = Bip84.FromSeed(seed_bytes, Bip84Coins.BITCOIN)
bip84_acc = bip84_mst.Purpose().Coin().Account(0)
bip84_chg = bip84_acc.Change(Bip44Changes.CHAIN_EXT)
bip84_addr = bip84_chg.AddressIndex(0)

# BIP84
private_key_84 = bip84_addr.PrivateKey().ToWif()
public_address_84 = bip84_addr.PublicKey().ToAddress()

# Affichage des informations BIP84
print(f"Adresse publique (BIP84): {public_address_84}")
print(f"Clé privée (WIF) (BIP84): {private_key_84}")

balance_84 = get_balance(public_address_84)
print(f"Solde disponible (BIP84): {balance_84} BTC")

# UTXOs available
utxos = get_utxos(public_address_84)

if not utxos:
    print("Aucun UTXO disponible pour effectuer la transaction.")
    exit(1)

# Build transaction
tx = Transaction(network='bitcoin', version=2) 

amount_to_send = balance_84 - tx_fee

amount_to_send_satoshis = btc_to_satoshis(amount_to_send)

# Ajouter les entrées
for utxo in utxos:
    tx.add_input(
        prev_txid=utxo['tx_hash'],
        output_n=utxo['tx_output_n'],
    )

tx.add_output(amount_to_send_satoshis, destination_address)

# Sign transaction
key = Key.from_wif(private_key_84)

# Sign
for i in range(len(utxos)):
    tx.sign(keys=key, index_n=i) 

# Obtenir la transaction signée en hex
signed_tx_hex = tx.raw_hex()

print(f"Transaction signée en hex: {signed_tx_hex}")

# Diffuser la transaction
response = requests.post(f"{base_url}/txs/push?token={api_token}", json={'tx': signed_tx_hex})
print(response.json())

And here is my result :

Adresse publique (BIP84): Clé privée (WIF) (BIP84): Solde disponible (BIP84): 0.00011554 BTC Transaction signée en hex: 0200000001ddef60775377f2cb8743c4ff4a86bb773d9d89aedc5063d253594860aae211fd140000006b483045022100daf2d94a17c8625bf7e5cd6a831ae3e3cb64c30a46470f9b860937c95673106f02201b38853e1edd750af6dacc0b84e42805d2f0e2b6b8d068e961a5b03326c1aeaa0121034c440c9f262ca3d0e1f9819b884868576759c8e3d415621e73fd5573ca9768e6ffffffff01272c00000000000016001461f16ac29f99560627fcd688be0fbb8bc438f4f000000000 {'error': 'Error validating transaction: witness script detected in tx without witness data.'}

Someone can help me ? Thanks, Best regards, Gabriel

mccwdev commented 2 months ago

Input needs a value and a witness_type.

With this it works:

for utxo in utxos:
    tx.add_input(
        prev_txid=utxo['tx_hash'],
        output_n=utxo['tx_output_n'],
        witness_type='segwit',
        value=btc_to_satoshis(balance_84)
    )

Not sure why it needs it witness_type though, it should be segwit by default...

mccwdev commented 2 months ago

Couldn't resist to make a version without the bip_utils library:

seed_phrase = 'screen plunge tiny enrich salmon unfair anxiety embrace offer uniform gym dose'
network = 'testnet'
masterkey = HDKey().from_passphrase(seed_phrase, network=network)
newkey = masterkey.subkey_for_path("m/84'/1'/0'/0/0")
destination_address = "tb1qarfay6n0djw8hvr5272sy3dptnwjutjdqjf4rl"
tx_fee = 0.0000025  #

print(f"Adresse publique (BIP84): {newkey.address()}")
print(f"Clé privée (WIF) (BIP84): {newkey.wif_key()}")

base_url = "https://api.blockcypher.com/v1/btc/test3"

def get_balance(address):
    response = requests.get(f"{base_url}/addrs/{address}/balance?token={api_token}")
    data = response.json()
    return data.get('final_balance', 0) / 100000000  # Satoshis to BTC

def get_utxos(address):
    response = requests.get(f"{base_url}/addrs/{address}?token={api_token}&unspentOnly=true")
    # return response.json().get('unconfirmed_txrefs', [])
    return response.json().get('txrefs', [])

def btc_to_satoshis(btc_amount):
    return int(btc_amount * 100000000)

balance_84 = get_balance(newkey.address())
print(f"Solde disponible (BIP84): {balance_84} BTC")

utxos = get_utxos(newkey.address())
if not utxos:
    print("Aucun UTXO disponible pour effectuer la transaction.")
    exit(1)

# Build transaction
tx = Transaction(network=network)
amount_to_send = balance_84 - tx_fee
amount_to_send_satoshis = btc_to_satoshis(amount_to_send)

for utxo in utxos:
    tx.add_input(
        prev_txid=utxo['tx_hash'],
        output_n=utxo['tx_output_n'],
        witness_type='segwit',
        value=btc_to_satoshis(balance_84)
    )

tx.add_output(amount_to_send_satoshis, destination_address)

for i in range(len(utxos)):
    tx.sign(keys=newkey.wif_key())

signed_tx_hex = tx.raw_hex()

print(f"Transaction signée en hex: {signed_tx_hex}")`

The get_balance and get_balance can also be replaces by methods in the Service class.