Расчет "всего получено" и баланса по адресу

Это то, что, как мне кажется, должно быть простым, но это сводит меня с ума. См., например, этот список транзакций, связанных с адресом:

https://blockchain.info/address/1EZK42jGEJVniyBn1wXrUx92wzyUkYB8kJ?format=json

Что я хочу сделать, так это просто получить ту же статистику total_received, total_sent и balance для заданных данных.

Очевидное требование – не засчитывать «сдачу» как новый платеж в адрес. Моя идея заключалась в том, чтобы просто суммировать все выходные данные в транзакциях, чьи входные данные не относятся к одному и тому же целевому адресу, например, если транзакция имеет входные данные (или несколько входных данных) из A и выходные данные в B и A, обработайте этот вывод как изменение и не делайте этого. не включать его в сумму — только это не дает той же суммы, что и blockchain.info! В этом случае он выдает 158,11536395 против 251,24164662. Кажется, я что-то упускаю?

Следующим шагом будет получение списка результатов, полученных на первом этапе, и просмотр, какие из них потрачены, а какие нет. Сумма неизрасходованных выходов и есть текущий баланс. Это правильно?

Кто угодно?

Вы находитесь в правильном направлении, но независимо от того, является ли это изменением адреса, не имеет значения, сколько он получил/отправил.
Их документы API показывают, что вам нужно использовать параметры ?limit и ?offset. Произошла разбивка на страницы, поэтому вы фактически не использовали все данные транзакции, когда вычисляли 158,11536395 против 251,24164662 (я получил те же числа с данными на момент вопроса ).

Ответы (3)

Как отмечалось в другом ответе, для определенного адреса технически не существует баланса. Это связано с тем, что может быть много выходов, которые адресованы на адрес, которым вы владеете, но вы можете тратить эти выходы только дискретно (т.е. вы можете сказать вычесть x с моей учетной записи, вы можете только сказать вычесть эту сумму вывода, которая ранее была присуждена этому адрес).

Однако мы можем определить псевдопонятие баланса, назовем его разблокируемым значением адреса (UV), потому что владелец закрытого ключа для этого адреса может разблокировать некоторое количество монет, зная закрытый ключ для этого адреса. адрес.

Теперь, чтобы рассчитать баланс UV, общее количество полученных и отправленных адресов, вы следуете этому псевдоалгоритму:

Address a;
Uv = 0;
Received = 0;
Sent = 0;
For each (output in blockchain):
    If (output is unlock-able by owner of address a):
        Received += output.amount;
        If (output has been spent):
            Sent += output.amount;
        Else:
            Uv += output.amount;

В конце всех выходов в блокчейне сумма Uv будет иметь значение, аналогичное концепции баланса. Но здесь мы также можем увидеть, почему это очень сложное понятие:

  • Как узнать, может ли выход разблокироваться владельцем адреса? Иногда это может знать только сам владелец ключа.
  • Что, если выход можно разблокировать, скажем, одним из трех ключей? Вы зачисляете балансы всех трех ключей или ни одного?

Я подозреваю, что способ, которым blockchain.info делает это, в основном запускается вышеописанным псевдоалгоритмом (хотя он гораздо более оптимизирован и использует базу данных), но только с учетом выходных данных в формате Pay-to-Pubkey-Hash.


Хорошо, связавшись со службой поддержки blockchain.info и проведя некоторые тесты, я обнаружил, что проблема заключалась в том, что ссылка на API, указанная в вопросе, разбита на страницы и ограничена по скорости. Следующий PHP-код выдает те же значения, которые blockchain.info показывает на своем сайте, при условии, что вы не столкнетесь с проблемами ограничения скорости.

<?php

// Method: POST, PUT, GET etc
// Data: array("param" => "value") ==> index.php?param=value

function CallAPI($method, $url, $data = false)
{
    $curl = curl_init();

    switch ($method)
    {
        case "POST":
            curl_setopt($curl, CURLOPT_POST, 1);

            if ($data)
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            break;
        case "PUT":
            curl_setopt($curl, CURLOPT_PUT, 1);
            break;
        default:
            if ($data)
                $url = sprintf("%s?%s", $url, http_build_query($data));
    }

    // Optional Authentication:
    curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
    // curl_setopt($curl, CURLOPT_USERPWD, "username:password");

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($curl);

    curl_close($curl);

    return $result;
}

$address = "1EZK42jGEJVniyBn1wXrUx92wzyUkYB8kJ";
$offset=0;
$txs = array();

while (true)
{
    $val = json_decode(CallAPI("GET", "https://blockchain.info/address/$address?format=json&limit=10&offset=$offset"), true);
    if (count($val["txs"]) == 0)
        break;
    else 
        $txs = array_merge($txs, $val["txs"]);

    $offset += 10;
}

$uv       = 0;
$received = 0;
$sent     = 0;
$nrefs    = 0;

foreach ($txs as $tx)
{   
    $ininputs = false;
    $totalin = 0;
    foreach ($tx["inputs"] as $input)
    {
        if ($input["prev_out"]["addr"] == $address)
        {
            $ininputs = true;
            $nrefs += 1;
            $totalin += $input["prev_out"]["value"];
        }
    }

    $inoutputs = false;
    $totalout = 0;
    foreach ($tx["out"] as $output)
    {
        if ($output["addr"] == $address)
        {
            $inoutputs = true;
            $nrefs += 1;
            $totalout += $output["value"];
        }
    }

    $uv       += ($totalout - $totalin);

    // $received += $totalout;
    // $sent     += $totalin;

    if ($ininputs && $inoutputs && $totalout >= $totalin)
    {
        $received += $totalout - $totalin;
    }
    else if ($ininputs && $inoutputs && $totalout < $totalin)
    {
        $sent += $totalin - $totalout;
    }
    else if ($inoutputs)
    {
        $received += $totalout;
    }
    else if ($ininputs)
    {
        $sent += $totalin;
    }
}
echo "\n";

echo "nrefs    : " . $nrefs . "\n";
echo "uv       : " . $uv . "\n";
echo "received : " . $received . "\n";
echo "sent     : " . $sent . "\n";

Результат работы программы

введите описание изображения здесь

Я не думаю, что ваш алгоритм завершен. Во-первых, он дважды учитывает «сдачу» для платежей, которые возвращаются на первоначальный адрес (например, адрес A тратит выходы и платит на адрес B, а сдача возвращается на адрес A, где она засчитывается так, как была первоначально дана ему, т.е. считается дважды: один раз в исходных выходах и еще раз как сдача). В этом суть моего первоначального вопроса: вы не можете запустить простую сумму, как в вашем собственном примере, так что вы можете сделать?
Хм, так вы говорите, что не хотите дважды учитывать адрес, отправляющий монеты самому себе, в общее количество полученных? Но что, если два адреса отправляют монеты туда и обратно другому, даже если оба принадлежат одному и тому же пользователю, есть ли разница? Вы можете ориентироваться только на то, что находится в цепочке блоков, и если цепочка блоков говорит, что адрес был «израсходован» и присуждены монеты в одной и той же транзакции, то это то, что произошло.
Попытка вывести информацию из реального мира приведет вас только к полному кролику, где вы можете быть уверены, что не охватите все крайние случаи. Лучшее, что вы можете сделать, это использовать блокчейн. Теперь можно подсчитать общее количество уникальных монет, которыми когда-либо владел адрес, с поправкой на смешивание, происходящее в транзакции. Хотя это число было бы интересно вычислить, я не думаю, что это имеет смысл для «общего полученного» числа. Смотрите это для получения дополнительной информации об отслеживании % монет.
Ну, по сути, мне нужно делать то, что делает blockchain.info, и похоже, что одна из вещей, которые он делает, — это отсутствие двойного подсчета сдачи в общей сумме полученных. Похоже, он делает даже больше, что я надеюсь выяснить.
@IvanVoras, см. обновленный ответ.

Сумма неизрасходованных выходов и есть текущий баланс.

Не совсем, это просто неизрасходованные выходы для определенного хэша pubkeyhash. Балансов в биткойнах не существует, и людей будет крайне обманывать, предполагая, что они существуют, используя эту терминологию.

Что я хочу сделать, так это просто получить ту же статистику total_received, total_sent и balance для заданных данных.

Это не часть сети, поэтому даже не определено, что этот термин может означать. Отображаемая информация не согласуется между веб-сайтами, утверждающими, что они могут отображать «всего полученное» для адреса.

Так кто прав?

Лучше просто не путать всех, показывая неверные данные для начала. Экстраполяция этой информации на людей не приносит никакой пользы, и если сервисы строятся с использованием такого рода ошибочных предположений о поведении сети, это может быть потенциально катастрофическим и привести к потере средств.

К лучшему или к худшему, люди в реальном мире думают с точки зрения «баланса» аккаунта, для своих аккаунтов и аккаунтов других людей, поэтому я не думаю, что это можно изменить. Что вы предлагаете в качестве альтернативного имени? "Расходная сумма"? Для начала я хотел бы воспроизвести некоторые или любые нетривиальные результаты (521.29736505 — это просто сумма выходных данных, включая изменение). Есть идеи, как это делает blockchain.info?
Суммарная расходуемая продукция была бы лучше, но было бы лучше, если бы такие вещи не показывались конечным пользователям с самого начала. Я не думаю, что кто-либо, кроме blockchain.info, мог бы рассказать вам, как они рассчитывают свои.
Balances don't exist in Bitcoin,Это может быть правдой на уровне протокола, но люди не создают биткойн-транзакции напрямую — они используют клиентов, которые абстрагируются от этих деталей. Я имею в виду, вы можете применить аналогичную логику, чтобы сказать, что доменных имен не существует, поскольку вы действительно общаетесь с IP-адресом.
У кошелька может быть баланс, а у адреса, скорее всего, нет, у него есть набор атомарных выходов, которые можно либо потратить, либо не потратить. «Баланс» подразумевает множество вещей, которые просто неприменимы. Что касается приведенных выше результатов, я не думаю, что кто-то на самом деле понимает, как кто-то еще это вычисляет, что объясняет дисперсию.
@Bitcoin A "balance" implies many things that are just simply not applicable.И то, что вы открываете свой веб-браузер для доменного имени, также подразумевает вещи, которые не соответствуют действительности. И тем не менее, это полезная абстракция. I don't think anybody actually understands how anybody else is calculating this,Верно для total_received, но баланс определен гораздо лучше.
Люди не говорят, что просматривают веб-домен, они говорят, что просматривают веб-сайт. Абстракция баланса просто служит для того, чтобы запутать людей («Я потратил 0,1 BTC, но 0,9 идут на этот странный адрес, о котором я не знаю, откуда меня украли!») и не служит абсолютно никакой полезной цели. «Баланс» также различается между разными веб-сайтами и реализациями.

Некоторые из этих ответов абсурдны. Да, есть разница между наличием ключа для траты с адреса и простым суммированием. Но если у вас есть пара ключей, которую вы можете потратить с адреса, ее окончательная сумма в точности соответствует «расходуемому балансу».

Я понятия не имею, как исследователи блоков не могут прийти к единому мнению относительно остатков на счетах. Я предполагаю, что большие числа являются точными суммами адресов ввода/вывода. Неточные будут ниже и будут неточными, вероятно, потому, что они не вычисляют должным образом базу монет для каждого ввода, который заканчивается адресом и не тратится.

Значения Coinbase не хранятся в цепочке, они находятся в параметрах цепочки, что можно назвать «эфемерным консенсусом». Значения Coinbase рассчитываются в соответствии с графиком халвинга вознаграждения за блок. Получение другой суммы в конце может быть только потому, что вы не считаете все действительные входы и выходы, из которых coinbase очень важна.

Coinbase values are not stored in the chain, they are in the chain parameters. Это неверно, текущее вознаграждение за блок + сборы очень сильно зависят от цепочки, в результате транзакции coinbase. Любой обозреватель блоков, суммирующий выходные данные, должен иметь возможность подобрать его без дополнительной работы.
Вы имеете в виду, что вы можете вывести значение coinbase по общей стоимости выходов, которые он использовал в качестве входных данных?
Но вы не можете сказать, какое значение coinbase следующего блока должно быть основано на данных в цепочке. Строго говоря, несмотря на то, что будет установлено, что точное количество блоков имеет постоянную стоимость coinbase, режим халвинга, т. е. количество блоков, первоначальная субсидия, НЕ кодируется, хотя результат кодируется. Функция GetBlockValue и начальное и минимальное значения субсидий в исходном коде определяют вознаграждение за блок. Как контрольные точки... Это то, что я называю эфемерным консенсусом, и любой узел, не знающий об этом, разветвит цепочку.
Конечно, но вы можете предсказать награду, основываясь на росте. Более того, текущая награда не имеет значения при расчете общей суммы входов и выходов для адреса, поскольку вся необходимая для этого информация находится в цепочке. Текущая награда за блок не имеет к этому никакого отношения.
Текущее вознаграждение за блок имеет значение для генерирующих транзакций. Однако, как я узнал сегодня, поле «значение» в любом случае говорит вам, и если это единственный tx в блоке, это совершенно ясно. Но вы можете сделать вывод только из более чем вдвое меньшего количества блоков, когда это происходит, и это, конечно, крайний случай, но ошибившись, вы потеряете награду за блок.