Esempio n. 1
0
size_t satoshi_raw_size(const block& packet)
{
    size_t block_size = 80 + variable_uint_size(packet.transactions.size());
    for (const message::transaction& tx: packet.transactions)
        block_size += satoshi_raw_size(tx);
    return block_size;
}
bool leveldb_common::save_transaction(leveldb_transaction_batch& batch,
    uint32_t block_height, uint32_t tx_index,
    const hash_digest& tx_hash, const transaction_type& block_tx)
{
    if (duplicate_exists(tx_hash, block_height, tx_index))
        return true;
    data_chunk tx_data(8 + satoshi_raw_size(block_tx));
    // Serialize tx.
    auto serial = make_serializer(tx_data.begin());
    serial.write_4_bytes(block_height);
    serial.write_4_bytes(tx_index);
    // Actual tx data.
    auto end_iter = satoshi_save(block_tx, serial.iterator());
    BITCOIN_ASSERT(
        std::distance(tx_data.begin(), end_iter) ==
        8 + satoshi_raw_size(block_tx));
    // Save tx to leveldb
    batch.tx.Put(slice(tx_hash), slice(tx_data));
    // Add inputs to spends database.
    // Coinbase inputs do not spend anything.
    if (!is_coinbase(block_tx))
        for (uint32_t input_index = 0; input_index < block_tx.inputs.size();
            ++input_index)
        {
            const transaction_input_type& input =
                block_tx.inputs[input_index];
            const input_point inpoint{tx_hash, input_index};
            if (!mark_spent_outputs(batch.spend,
                    input.previous_output, inpoint))
                return false;
            if (!add_debit(batch.debit,
                    input, {tx_hash, input_index}, block_height))
                return false;
        }
    // Save address -> output mappings.
    for (uint32_t output_index = 0; output_index < block_tx.outputs.size();
        ++output_index)
    {
        const transaction_output_type& output =
            block_tx.outputs[output_index];
        if (!add_credit(batch.credit,
                output, {tx_hash, output_index}, block_height))
            return false;
    }
    return true;
}
Esempio n. 3
0
hash_digest hash_transaction_impl(const message::transaction& tx, 
    uint32_t* hash_type_code)
{
    data_chunk serialized_tx(satoshi_raw_size(tx));
    satoshi_save(tx, serialized_tx.begin());
    if (hash_type_code != nullptr)
        extend_data(serialized_tx, uncast_type(*hash_type_code));
    return generate_sha256_hash(serialized_tx);
}
Esempio n. 4
0
hash_digest hash_transaction_impl(const transaction_type& tx,
    uint32_t* hash_type_code)
{
    data_chunk serialized_tx(satoshi_raw_size(tx));
    satoshi_save(tx, serialized_tx.begin());
    if (hash_type_code != nullptr)
        extend_data(serialized_tx, to_little_endian(*hash_type_code));
    return bitcoin_hash(serialized_tx);
}
Esempio n. 5
0
data_chunk create_raw_message(const Message& packet)
{
    data_chunk payload(satoshi_raw_size(packet));
    satoshi_save(packet, payload.begin());
    // Make the header packet and serialise it
    message::header head;
    head.magic = magic_value;
    head.command = satoshi_command(packet);
    head.payload_length = payload.size();
    head.checksum = generate_sha256_checksum(payload);
    data_chunk raw_header(satoshi_raw_size(head));
    satoshi_save(head, raw_header.begin());
    // Construct completed packet with header + payload
    data_chunk whole_message = raw_header;
    extend_data(whole_message, payload);
    // Probably not the right place for this
    // Networking output in an exporter
    log_info(log_domain::network) << "s: " << head.command
        << " (" << payload.size() << " bytes)";
    return whole_message;
}
Esempio n. 6
0
data_chunk create_raw_message(const Message& packet)
{
    data_chunk payload(satoshi_raw_size(packet));
    satoshi_save(packet, payload.begin());
    // Make the header packet and serialise it
    header_type head;
    head.magic = magic_value();
    head.command = satoshi_command(packet);
    head.payload_length = static_cast<uint32_t>(payload.size());
    head.checksum = bitcoin_checksum(payload);
    data_chunk raw_header(satoshi_raw_size(head));
    satoshi_save(head, raw_header.begin());
    // Construct completed packet with header + payload
    data_chunk whole_message = raw_header;
    extend_data(whole_message, payload);
    // Probably not the right place for this
    // Networking output in an exporter
    log_debug(LOG_NETWORK) << "s: " << head.command
        << " (" << payload.size() << " bytes)";
    return whole_message;
}
Esempio n. 7
0
Status
watcherBridgeRawTx(Wallet &self, const char *szTxID,
    DataChunk &result)
{
    Watcher *watcher = nullptr;
    ABC_CHECK(watcherFind(watcher, self));

    bc::hash_digest txid;
    if (!bc::decode_hash(txid, szTxID))
        return ABC_ERROR(ABC_CC_ParseError, "Bad txid");
    auto tx = watcher->find_tx(txid);
    result.resize(satoshi_raw_size(tx));
    bc::satoshi_save(tx, result.begin());

    return Status();
}
Esempio n. 8
0
bool leveldb_common::get_transaction(leveldb_tx_info& tx_info,
    const hash_digest& tx_hash, bool read_parent, bool read_tx)
{
    // First we try to read the bytes from the database.
    std::string value;
    leveldb::Status status = db_.tx->Get(
        leveldb::ReadOptions(), slice(tx_hash), &value);
    if (status.IsNotFound())
        return false;
    else if (!status.ok())
    {
        log_fatal(LOG_BLOCKCHAIN) << "get_transaction("
            << tx_hash << "): " << status.ToString();
        return false;
    }
    // Read the parent block height and our index in that block (if neccessary).
    BITCOIN_ASSERT(value.size() > 8);
    if (read_parent)
    {
        auto deserial = make_deserializer(value.begin(), value.begin() + 8);
        tx_info.height = deserial.read_4_bytes();
        tx_info.index = deserial.read_4_bytes();
    }
    if (!read_tx)
        return true;
    // Read the actual transaction (if neccessary).
    try
    {
        BITCOIN_ASSERT(value.size() > 8);
        satoshi_load(value.begin() + 8, value.end(), tx_info.tx);
    }
    catch (end_of_stream)
    {
        return false;
    }
    BITCOIN_ASSERT(satoshi_raw_size(tx_info.tx) + 8 == value.size());
    BITCOIN_ASSERT(hash_transaction(tx_info.tx) == tx_hash);
    return true;
}
Esempio n. 9
0
static
tABC_CC ABC_BridgeDoSweep(WatcherInfo *watcherInfo,
                          PendingSweep& sweep,
                          tABC_Error *pError)
{
    tABC_CC cc = ABC_CC_Ok;
    char *szID = NULL;
    char *szAddress = NULL;
    uint64_t funds = 0;
    abcd::unsigned_transaction utx;
    bc::transaction_output_type output;
    abcd::key_table keys;
    std::string malTxId, txId;

    // Find utxos for this address:
    auto utxos = watcherInfo->watcher.get_utxos(sweep.address);

    // Bail out if there are no funds to sweep:
    if (!utxos.size())
    {
        // Tell the GUI if there were funds in the past:
        if (watcherInfo->watcher.db().has_history(sweep.address))
        {
            if (sweep.fCallback)
            {
                sweep.fCallback(ABC_CC_Ok, NULL, 0);
            }
            else
            {
                if (watcherInfo->fAsyncCallback)
                {
                    tABC_AsyncBitCoinInfo info;
                    info.eventType = ABC_AsyncEventType_IncomingSweep;
                    info.sweepSatoshi = 0;
                    info.szTxID = NULL;
                    watcherInfo->fAsyncCallback(&info);
                }
            }
            sweep.done = true;
        }
        return ABC_CC_Ok;
    }

    // There are some utxos, so send them to ourselves:
    tABC_TxDetails details;
    memset(&details, 0, sizeof(tABC_TxDetails));
    details.amountSatoshi = 0;
    details.amountCurrency = 0;
    details.amountFeesAirbitzSatoshi = 0;
    details.amountFeesMinersSatoshi = 0;
    details.szName = const_cast<char*>("");
    details.szCategory = const_cast<char*>("");
    details.szNotes = const_cast<char*>("");
    details.attributes = 0x2;

    // Create a new receive request:
    ABC_CHECK_RET(ABC_TxCreateReceiveRequest(watcherInfo->wallet,
        &details, &szID, false, pError));
    ABC_CHECK_RET(ABC_TxGetRequestAddress(watcherInfo->wallet, szID,
        &szAddress, pError));

    // Build a transaction:
    utx.tx.version = 1;
    utx.tx.locktime = 0;
    for (auto &utxo : utxos)
    {
        bc::transaction_input_type input;
        input.sequence = 0xffffffff;
        input.previous_output = utxo.point;
        funds += utxo.value;
        utx.tx.inputs.push_back(input);
    }
    if (10000 < funds)
        funds -= 10000; // Ugh, hard-coded mining fee
    ABC_CHECK_ASSERT(!outputIsDust(funds), ABC_CC_InsufficientFunds, "Not enough funds");
    output.value = funds;
    ABC_CHECK_NEW(outputScriptForAddress(output.script, szAddress));
    utx.tx.outputs.push_back(output);

    // Now sign that:
    keys[sweep.address] = sweep.key;
    ABC_CHECK_SYS(abcd::gather_challenges(utx, watcherInfo->watcher), "gather_challenges");
    ABC_CHECK_SYS(abcd::sign_tx(utx, keys), "sign_tx");

    // Send:
    {
        bc::data_chunk raw_tx(satoshi_raw_size(utx.tx));
        bc::satoshi_save(utx.tx, raw_tx.begin());
        ABC_CHECK_NEW(broadcastTx(raw_tx));
    }

    // Save the transaction in the database:
    malTxId = bc::encode_hash(bc::hash_transaction(utx.tx));
    txId = ABC_BridgeNonMalleableTxId(utx.tx);
    ABC_CHECK_RET(ABC_TxSweepSaveTransaction(watcherInfo->wallet,
        txId.c_str(), malTxId.c_str(), funds, &details, pError));

    // Done:
    if (sweep.fCallback)
    {
        sweep.fCallback(ABC_CC_Ok, txId.c_str(), output.value);
    }
    else
    {
        if (watcherInfo->fAsyncCallback)
        {
            tABC_AsyncBitCoinInfo info;
            info.eventType = ABC_AsyncEventType_IncomingSweep;
            info.sweepSatoshi = output.value;
            info.szTxID = stringCopy(txId);
            watcherInfo->fAsyncCallback(&info);
            ABC_FREE_STR(info.szTxID);
        }
    }
    sweep.done = true;
    watcherInfo->watcher.send_tx(utx.tx);

exit:
    ABC_FREE_STR(szID);
    ABC_FREE_STR(szAddress);

    return cc;
}
Esempio n. 10
0
bool leveldb_common::save_transaction(leveldb_transaction_batch& batch,
    uint32_t block_height, uint32_t tx_index,
    const hash_digest& tx_hash, const transaction_type& block_tx)
{
    if (is_special_duplicate(block_height, tx_index))
        return true;
    data_chunk tx_data(8 + satoshi_raw_size(block_tx));
    // Serialize tx.
    auto serial = make_serializer(tx_data.begin());
    serial.write_4_bytes(block_height);
    serial.write_4_bytes(tx_index);
    // Actual tx data.
    auto end_iter = satoshi_save(block_tx, serial.iterator());
    BITCOIN_ASSERT(
        tx_data.begin() + 8 + satoshi_raw_size(block_tx) == end_iter);
    // Save tx to leveldb
    batch.tx.Put(slice(tx_hash), slice(tx_data));
    // Add inputs to spends database.
    // Coinbase inputs do not spend anything.
    if (!is_coinbase(block_tx))
        for (uint32_t input_index = 0; input_index < block_tx.inputs.size();
            ++input_index)
        {
            const transaction_input_type& input =
                block_tx.inputs[input_index];
            const input_point inpoint{tx_hash, input_index};
            if (!mark_spent_outputs(batch.spend,
                    input.previous_output, inpoint))
                return false;
            if (!add_debit(batch.debit,
                    input, {tx_hash, input_index}, block_height))
                return false;
        }
    // A stack of size 1. Keep the stealth_data from
    // one iteration to the next.
    data_chunk stealth_data_store;
    auto unload_stealth_store = [&]()
    {
        return std::move(stealth_data_store);
    };
    // Save address -> output mappings.
    for (uint32_t output_index = 0; output_index < block_tx.outputs.size();
        ++output_index)
    {
        const transaction_output_type& output =
            block_tx.outputs[output_index];
        // If a stealth output then skip processing.
        if (process_stealth_output_info(output, stealth_data_store))
            continue;
        data_chunk stealth_data = unload_stealth_store();
        // Try to extract an address.
        payment_address address;
        if (!extract(address, output.script))
            continue;
        // Process this output.
        if (!stealth_data.empty())
            add_stealth_info(stealth_data, address, tx_hash, *db_stealth_);
        if (!add_credit(batch.credit, address, output.value,
                {tx_hash, output_index}, block_height))
            return false;
    }
    return true;
}
Esempio n. 11
0
static Status
bridgeDoSweep(Wallet &wallet, PendingSweep &sweep,
              tABC_BitCoin_Event_Callback fAsyncCallback, void *pData)
{
    // Find utxos for this address:
    AddressSet addresses;
    addresses.insert(sweep.address);
    auto utxos = wallet.txCache.get_utxos(addresses);

    // Bail out if there are no funds to sweep:
    if (!utxos.size())
    {
        // Tell the GUI if there were funds in the past:
        if (wallet.txCache.has_history(sweep.address))
        {
            ABC_DebugLog("IncomingSweep callback: wallet %s, value: 0",
                         wallet.id().c_str());
            tABC_AsyncBitCoinInfo info;
            info.pData = pData;
            info.eventType = ABC_AsyncEventType_IncomingSweep;
            Status().toError(info.status, ABC_HERE());
            info.szWalletUUID = wallet.id().c_str();
            info.szTxID = nullptr;
            info.sweepSatoshi = 0;
            fAsyncCallback(&info);

            sweep.done = true;
        }
        return Status();
    }

    // Build a transaction:
    bc::transaction_type tx;
    tx.version = 1;
    tx.locktime = 0;

    // Set up the output:
    Address address;
    wallet.addresses.getNew(address);
    bc::transaction_output_type output;
    ABC_CHECK(outputScriptForAddress(output.script, address.address));
    tx.outputs.push_back(output);

    // Set up the inputs:
    uint64_t fee, funds;
    ABC_CHECK(inputsPickMaximum(fee, funds, tx, utxos));
    if (outputIsDust(funds))
        return ABC_ERROR(ABC_CC_InsufficientFunds, "Not enough funds");
    tx.outputs[0].value = funds;

    // Now sign that:
    KeyTable keys;
    keys[sweep.address] = sweep.key;
    ABC_CHECK(signTx(tx, wallet.txCache, keys));

    // Send:
    bc::data_chunk raw_tx(satoshi_raw_size(tx));
    bc::satoshi_save(tx, raw_tx.begin());
    ABC_CHECK(broadcastTx(wallet, raw_tx));

    // Calculate transaction information:
    const auto info = wallet.txCache.txInfo(tx, wallet.addresses.list());

    // Save the transaction metadata:
    Tx meta;
    meta.ntxid = info.ntxid;
    meta.txid = info.txid;
    meta.timeCreation = time(nullptr);
    meta.internal = true;
    meta.metadata.amountSatoshi = funds;
    meta.metadata.amountFeesAirbitzSatoshi = 0;
    ABC_CHECK(gContext->exchangeCache.satoshiToCurrency(
                  meta.metadata.amountCurrency, info.balance,
                  static_cast<Currency>(wallet.currency())));
    ABC_CHECK(wallet.txs.save(meta));

    // Update the transaction cache:
    if (wallet.txCache.insert(tx))
        watcherSave(wallet).log(); // Failure is not fatal
    wallet.balanceDirty();
    ABC_CHECK(wallet.addresses.markOutputs(info.ios));

    // Done:
    ABC_DebugLog("IncomingSweep callback: wallet %s, txid: %s, value: %d",
                 wallet.id().c_str(), info.txid.c_str(), output.value);
    tABC_AsyncBitCoinInfo async;
    async.pData = pData;
    async.eventType = ABC_AsyncEventType_IncomingSweep;
    Status().toError(async.status, ABC_HERE());
    async.szWalletUUID = wallet.id().c_str();
    async.szTxID = info.txid.c_str();
    async.sweepSatoshi = output.value;
    fAsyncCallback(&async);

    sweep.done = true;

    return Status();
}