Bitcoin: выходы, транзакции и баланс
В связи с вопросами о невозможности отправлять платежи «хотя на балансе есть средства», мы решили описать принцип работы сети Bitcoin (и ему подобных Litecoin, Dash, Dogecoin и т.д.) в легкой и доступной форме. Даже если вы уже имеете представление «как это работает», настоятельно рекомендуем вам прочитать эту статью и «освежить» свои знания. Возможно, вы узнаете для себя что-то новое или «разложите по полочкам» то, что уже знали.
Сначала немного математики (упрощенно)... Если взять любое (сгенерировать) очень большое число (назовем его «приватный ключ»), то по определенному (известному всем) алгоритму можно найти соответствующее ему очень большое число («публичный ключ»). Обратного алгоритма не существует. Нельзя из публичного ключа получить приватный! Приватный ключ всегда должен быть известен только владельцу! Публичный ключ сообщается всем. Особенность этой пары чисел в том, что если сообщение (набор данных) «подписать» (посчитать подпись) приватным ключом, то эта подпись может быть легко проверена при помощи только публичного ключа. Это является доказательством того, что сообщение было создано именно владельцем этой пары ключей. Bitcoin-адрес (упрощенно) это и есть публичный ключ.
Являясь владельцем адреса, вы можете подписывать сообщения «от имени адреса».
Когда вы в приложении создаете новый адрес, то на самом деле вам генерируется приватный ключ, а из него публичный ключ и адрес. Очень маловероятно (практически невероятно) что вам попадется пара ключей, которая уже кому-то выдавались ранее. Удаление приложения или порча его файлов обычно приводит к потере приватного ключа, а значит и к потере средств, которые находятся на этом адресе.
Как и любая сеть, блокчейн-сеть Bitcoin – это связанные между собой устройства, на которых запущено специальное ПО. Каждое такое устройство (узел блокчейн-сети) обычно называется «нода». Все ноды равнозначны между собой в правах. Очень мощные ноды могут «майнить» (см. далее).
Каждая нода хранит у себя копию базы данных (журнала) сети. Журнал состоит из записей. В этот журнал можно только добавлять новые записи. Редактирование или удаление существующих записей невозможно. Каждая запись – это данные о «списании» и/или «зачислении» средств с адреса на адрес.
Выходы
Журнал не хранит баланс адреса! Журнал хранит все зачисления («выходы») и все списания («входы») адреса!
Например: Адрес А: пришло X1, пришло X2, ушло Y1, пришло X3, ушло Y2..
Текущий баланс адреса это сумма всех выходов минус сумма всех входов (см. выше).
Например: На адрес А (за все время) пришло X, а ушло Y. Значит, баланс адреса равен X – Y.
Транзакция
Когда вы отправляете средства, приложение формирует сообщение-«транзакцию», которая содержит выходы в формате «адрес, выход» для списания (траты) и входы в формате «адрес, сумма» для зачисления. Заметим, что по транзакции должно быть списано больше или равно, чем зачислено. Разность между списано и зачислено называется «плата за транзакцию» (fee) и она уходит «майнерам» (см. далее). Теоретически можно сформировать транзакцию с fee = 0, и это не будет ошибкой. Если плата за транзакцию получается больше желаемой, то излишки («сдачу») забирают обратно, добавляя запись в список входов. Транзакция имеет размер. Чем больше элементов в списках – тем больше данных и тем длиннее транзакция.
Например: Адрес А: выход X1 на сумму «1», выход X2 на сумму «2». Баланс А = 1 + 2 = 3. Нужно перечислить на адрес Б сумму «2». Выхода X1 недостаточно, нам подходит сумма выхода X2.
Первоначально транзакция выглядит так: выходы: «Адрес А, X2», входы: «Адрес Б, 2». Списано «2», зачислено «2». У такой транзакции fee = «2 – 2» = «0». Врядли майнерам это понравится. Добавим fee = «0.001». Выход X2 полностью использован, значит используем другой выход X1.
Транзакция выглядит так: выходы: «Адрес А, X2» «Адрес А, X1», входы: «Адрес Б, 2». Списано «3», зачислено «2». У такой транзакции fee = «3 – 2» = «1». Это больше чем нужно. Вернем сдачу = «1 - 0.001» = «0.999».
Конечный вариант транзакции выглядит так: выходы: «Адрес А, X2» «Адрес А, X1», входы: «Адрес Б, 2» «Адрес А, 0.999». Списано «3», зачислено «2.999». У такой транзакции fee = «3 - 2.999» = «0.001». Теперь то что нужно! Заметим, хотя зачислено и равно «2.999», но оно включает нашу сдачу «0.999», которая вернется к нам и фактически не будет потрачена.
Транзакции тратят выход(ы) целиком! Нельзя потратить часть выхода, но можно вернуть себе «излишне потраченное» через сдачу!
Нельзя создать больше транзакций, чем имеется выходов. Максимально: один выход = одна транзакция. Но можно один выход = несколько входов (получателей)!
Выход – это любое зачисление (приход) средств, в том числе и сдача.
Нода
Сформированную и подписанную «приватными ключами» транзакцию можно отправить через любую ноду. Нода проверяет действительность подписи, правильность формирования и доступность выходов. После этого транзакция помещается в «mempool» – хранилище новых (необработанных) транзакций. Транзакции, находящиеся в mempool, видны всем нодам. С этого момента все выходы, использованные в этой транзакции считаются использованными или «потраченными» и их нельзя использовать в других транзакциях!
Добавление новых записей в журнал происходит пачками (блоками) сразу по несколько тысяч штук. Блоки формируются нодами-«майнерами». Блоки имеют лимит на размер. Нельзя сформировать блок размером, больше максимального. Несколько (тысяч) транзакций из mempool объединяются в один блок и начинается «майнинг». Майнинг – это поиск определенного хеша блока методом подбора. Тот, кто первый «намайнит» блок – отдает его на проверку остальным нодам. Каждая нода сверяет записи блока с предыдущими записями своего журнала. Они должны не конфликтовать. Если блок прошел проверку, то он добавляется целиком в журнал этой ноды. Каждая нода работает только со своим журналом.
Каждая нода работает независимо от других и только со своим журналом!
Если на каких то нодах блок не принят, то появляется расхождение в журналах или «отдельная версия блокчейна». В таком случае ноды синхронизируются, и «побеждает» самая распространенная версия. Именно из-за возможности рассинхронизации, для окончательного подтверждения требуется наличие еще несколько блоков после текущего, чтобы убедиться, что блок и все транзакции в нем попали в верную версию блокчейна.
Транзакции подтверждаются, когда за их блоком появляются новые блоки.
Можно использовать выходы только из подтвержденных транзакций.
Цена за байт
За верный блок майнер получает фиксированное вознаграждение, плюс сумму всех плат за все транзакции в этом блоке. Логично, что выгоднее всего формировать блок из транзакций с максимальной платой, тогда и награда будет больше. Но нужно также помнить, что размер блока фиксирован, а значит нужно также смотреть длину транзакции. На самом деле, выгоднее всего включать транзакции, в которых «плотность fee» (плата за единицу информации) выше. Этот показатель назвали «sat/B» (сатоши на байт). Она равна «fee в сатошах» делить на «длина транзакции».
Никто не знает по какому принципу майнер выбирает транзакции из mempool. Нельзя никак заставить майнера выбрать вашу транзакцию. Но для майнера выгоднее в первую очередь брать транзакции у которых показатель sat/B выше остальных.
Хотите чтобы ваши транзакции скорее попадали в блок (т.е. подтверждались) – ставьте такое fee, при котором у вашей транзакции показатель sat/B выше, чем средний по mempool.
В случае если все одновременно захотят, чтобы их транзакции уходили быстрее (например резкий скачок цены), то все будут повышать значение sat/B. А значит оно может меняется в зависимости от нагрузки сети. Это сложный выбор между скоростью и платой за транзакцию.
Баланс
Пусть у вас есть адрес А. На нем нет выходов. Его баланс = 0 btc.
На ваш адрес А приходят (это значит транзакция на такую сумму подтверждена) средства = 1 btc. Теперь на нем один выход на 1 btc. И его баланс = 1 btc.
На ваш адрес А приходят средства = 0.2 btc. Теперь на нем выходы: 1 btc и 0.2 btc. И его баланс = 1.2 btc.
Вы делаете перевод с адреса А на адрес Б сумму 0.3 btc. Для упрощения, пусть fee = 0.
Логично, что выхода на 0.2 btc недостаточно, значит используем на 1 btc.
Значит в транзакции будет выход «1 btc» и входы «адрес Б, 0.3 btc» «адрес А(сдача), 0.7 btc».
Сразу после отправки и помещения в mempool на вашем адресе останется только один выход на 0.2 btc, потому что выход на 1 btc был только что использован (потрачен) в транзакции. С этого момента «доступный» баланс вашего адреса = 0.2 btc!
Пока эта транзакция не подтвердится – сдача 0.7 не вернется! А значит этой суммой нельзя будет воспользоваться!
Теоретически у вас есть 0.2 + 0.7 = 0.9 btc, но практически вы не можете распоряжаться этими 0.7 btc.
Это значит, что сразу же после отправки вы не сможете перевести даже 0.20000001 btc.
На время, пока ваша транзакция не подтвердится, ваши 0.7 btc будут как бы «заморожены».
Итог
Как же сделать так, чтобы все работало «как часы» и не морозилось?
Имейте больше выходов. Т.е. либо отправляйте себе на адрес несколькими транзакциями, либо имейте больше адресов.
Тратьте меньше выходов. Т.е. делайте меньше транзакций, но больше получателей в одной транзакции.
Делайте так, чтобы сдачи быстрее возвращались. Т.е. ставьте такое fee, при котором показатель sat/B будет конкурентным в mempool в данный момент.
Зачем нужны два параметра label и uniqID? →