MetacoSA / QBitNinja

An Open Source and powerful blockchain API
MIT License
68 stars 42 forks source link

Send money when the wallet has more than 1 transaction #104

Closed ljchuello closed 4 years ago

ljchuello commented 4 years ago

Hi friend!.

Sorry for my bad English, I only speak Spanish.

I have problems sending money, when the wallet has more than two transactions, the sending of btc simply does not work.

When the wallet has a single transaction (without changing any code) it works without problem.

Success = True Error.Code = "INVALID" Error.Reason = "Unknown"

After several tests I came to the conclusion that it gives problems when there is more than one transaction in the wallet. I don't use Network.TestNet, I try directly on Network.Main.

The wallet I try to spend is this: 18qBnMxspy28YNSFdxvwXqD5GpQ2ukQmaN

I attached the source code so we can solve it together.

Thank you.

Key privateKey = Key.Parse(llavePrivada);
BitcoinSecret bitcoinPrivateKey = new BitcoinSecret(llavePrivada);
BitcoinAddress address = bitcoinPrivateKey.GetAddress(ScriptPubKeyType.Legacy);

QBitNinjaClient client = new QBitNinjaClient(Network.Main);
uint256 transactionId = uint256.Parse("42119368712db98a8fb75763fd9158848dc37ff5b180413e30a5063113b4aaa1");
GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;

// Creamos el objeto Transaction
Transaction transaction = Transaction.Create(Network.Main);

List<ICoin> receivedCoins = transactionResponse.ReceivedCoins;
OutPoint outPointToSpend = null;
foreach (var coin in receivedCoins)
{
    if (coin.TxOut.ScriptPubKey == privateKey.ScriptPubKey)
    {
        outPointToSpend = coin.Outpoint;
    }
}

// Validamos si existe algun ScriptPubKey
if (outPointToSpend == null)
{
    throw new Exception("TxOut doesn't contain our ScriptPubKey");
}

// Creamos la entrada
TxIn tEtrada = new TxIn();
tEtrada.PrevOut = outPointToSpend;
transaction.Inputs.Add(tEtrada);

// Salida principal
TxOut tSalidaPrincipal = new TxOut();
tSalidaPrincipal.Value = new Money(0.00060035m, MoneyUnit.BTC);
tSalidaPrincipal.ScriptPubKey = new BitcoinPubKeyAddress(destino).ScriptPubKey;

// Añadimos la salida principal a la transacción
transaction.Outputs.Add(tSalidaPrincipal);

// Mensaje
if (!Cadena.Vacia(mensaje))
{
    TxOut tMensaje = new TxOut();
    tMensaje.Value = Money.Zero;
    tMensaje.ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(Encoding.UTF8.GetBytes(mensaje));
    transaction.Outputs.Add(tMensaje);
}

transaction.Inputs[0].ScriptSig = address.ScriptPubKey;

// Firmamos la transacción  
transaction.Sign(bitcoinPrivateKey, receivedCoins.ToArray());

BroadcastResponse broadcastResponse = client.Broadcast(transaction).Result;

if (!broadcastResponse.Success)
{
    Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
    Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
}
else
{
    Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
    Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
    Console.WriteLine("Success! You can check out the hash of the transaciton in any block explorer:");
    Console.WriteLine(transaction.GetHash());
}
ljchuello commented 4 years ago

I can spend btc of wallet 1GnCszF6E3MSPSHjyXaPuofjvUfKG7sj7C

I can't spend btc of the wallet: 183ghuYGPyw8iZsZsaNW8RVeK46E8BDz5A

For some strange reason, from one wallet I can use btc, and from the other not, I am using the same code. I don't understand where the problem may be.

Thank you.

Success = True Error.Code = "INVALID" Error.Reason = "Unknown"

Update my code:

        // Obtenemos los detalles de la billetera
        WalletSummary walletSummary = new WalletSummary();

        // Obtenemos la lista de las transacciones
        WalletListTransaction walletListTransaction = new WalletListTransaction();

        // Saldo
        decimal saldo = 0;

        // Instanciamos
        Key privateKey = Key.Parse(llavePrivada);
        BitcoinSecret bitcoinPrivateKey = new BitcoinSecret(llavePrivada);
        BitcoinAddress address = bitcoinPrivateKey.GetAddress(ScriptPubKeyType.Legacy);

        // Obtenemos el saldo de la billetera
        walletSummary = walletSummary.Get($"{address}");

        // Validamos que el immature = 0
        if (walletSummary.immature.amount != 0)
        {
            throw new NotImplementedException("immature > 0");
        }

        // Validamos que spendable sea igual a unConfirmed + confirmed
        if (walletSummary.spendable.amount != (walletSummary.unConfirmed.amount + walletSummary.confirmed.amount))
        {
            throw new NotImplementedException("spendable != (unConfirmed + confirmed)");
        }

        // Obtenemos el saldo
        saldo = Math.Round(walletSummary.spendable.amount / 100000000m, 8);

        // Validar montos
        if ((mtoEnvio + fee) > saldo)
        {
            throw new NotImplementedException("mtoEnvio + fee no puede ser mayor a saldo");
        }

        // Obtenemos las transacciones
        //walletListTransaction = walletListTransaction.Get($"{address}", 1);

        QBitNinjaClient client = new QBitNinjaClient(Network.Main);

        BalanceModel balanceModel = ListarTransacciones($"{address}");

        uint256 transactionId = balanceModel.Operations.Select(x => x.TransactionId).FirstOrDefault();
        GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;

        // Creamos el objeto Transaction
        Transaction transaction = Transaction.Create(Network.Main);

        List<ICoin> receivedCoins = transactionResponse.ReceivedCoins;
        OutPoint outPointToSpend = null;
        foreach (var coin in receivedCoins)
        {
            if (coin.TxOut.ScriptPubKey == privateKey.ScriptPubKey)
            {
                outPointToSpend = coin.Outpoint;
            }
        }

        // Validamos si existe algun ScriptPubKey
        if (outPointToSpend == null)
        {
            throw new Exception("TxOut doesn't contain our ScriptPubKey");
        }

        // Creamos la entrada
        TxIn tEtrada = new TxIn();
        tEtrada.PrevOut = outPointToSpend;
        transaction.Inputs.Add(tEtrada);

        // Salida principal
        TxOut tSalidaPrincipal = new TxOut();
        tSalidaPrincipal.Value = new Money(mtoEnvio, MoneyUnit.BTC);
        tSalidaPrincipal.ScriptPubKey = new BitcoinPubKeyAddress(destino).ScriptPubKey;

        // Añadimos la salida principal a la transacción
        transaction.Outputs.Add(tSalidaPrincipal);

        // Salida sobrante
        if ((saldo - mtoEnvio - fee) > 0)
        {
            TxOut tSalidaSobrante = new TxOut();
            tSalidaSobrante.Value = new Money(saldo - mtoEnvio - fee, MoneyUnit.BTC);
            tSalidaSobrante.ScriptPubKey = address.ScriptPubKey;

            // Añadimos la salida sobrante
            transaction.Outputs.Add(tSalidaSobrante);
        }

        // Mensaje
        if (!Cadena.Vacia(mensaje))
        {
            TxOut tMensaje = new TxOut();
            tMensaje.Value = Money.Zero;
            tMensaje.ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(Encoding.UTF8.GetBytes(mensaje));
            transaction.Outputs.Add(tMensaje);
        }

        transaction.Inputs[0].ScriptSig = bitcoinPrivateKey.ScriptPubKey;

        // Firmamos la transacción  
        transaction.Sign(bitcoinPrivateKey, receivedCoins.ToArray());

        BroadcastResponse broadcastResponse = client.Broadcast(transaction).Result;

        if (!broadcastResponse.Success)
        {
            Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
            Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
        }
        else
        {
            Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
            Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
            Console.WriteLine("Success! You can check out the hash of the transaciton in any block explorer:");
            Console.WriteLine(transaction.GetHash());
        }
ljchuello commented 4 years ago

I can not send: 🔴 183ghuYGPyw8iZsZsaNW8RVeK46E8BDz5A 🔴 16Kk4TC82WNJ2pVpnWFGUQNvoHdAwertbo

I can send: 🔵 1GnCszF6E3MSPSHjyXaPuofjvUfKG7sj7C

I really don't know what's happening. But I checked that it has nothing to do with the amount of transactions.

😢😡😔

NicolasDorier commented 4 years ago

By default, I think client.GetTransaction returns you coins that are already spent. Instead use client.GetTransaction(address, true) to get only the unspent coins.