Обновление Safe Network 🇷🇺 21 октября 2021 г

Это машинный перевод. Оригинал на английском здесь: Update 21 October, 2021

Зачем использовать фиксированные номиналы в цифровой валюте, если в ваших платежах могут использоваться произвольные суммы? В конце концов, мы говорим здесь о числах, а не о клочках металла и клочках бумаги. Ответ - конфиденциальность. Если вы совершите транзакцию 4589234127 SNT, эта цифра, скорее всего, будет уникальной и, следовательно, отслеживаемой.

Однако, даже если мы принимаем деноминации как способ объединения анонимности, остается вопрос эффективности. Небольшое количество возможных номиналов (скажем, 1 и 10) сделало бы транзакции чрезвычайно сложными для связывания и максимизировало бы взаимозаменяемость, но за счет создания неприемлемой дополнительной работы для сети.

Исследование транзакций с биткойнами, проведенное @mav, показывает, что степень детализации в 8 значащих десятичных знаков является обычным явлением, предположительно потому, что платежи в биткойнах рассчитываются на основе фиатных валют и зависят от обменного курса. То же самое и для SNT, поэтому мы не можем ожидать идеально равных сумм.

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

Это резюме. Для тех, кто хочет копнуть глубже, @danda излагает наши современные взгляды во всем математическом великолепии ниже.

Общий прогресс

@chriso упорно трудился, чтобы объединить api и cli. В коде все включено, но слияние этих репозиториев означало, что мы должны адаптировать нашу непрерывную интеграцию и потоки обновления версий. Были найдены приемлемые решения, что должно означать, что к каждому выпуску safe_network будут прикреплены все ящики, но с их собственными отдельными версиями (наш старый поток означал бы только одну версию и много вероятной путаницы в ней). Итак, теперь он работает над внедрением этих изменений.

@Chris.Connelly продолжил копаться в qp2p и подтвердил, что отправка большого количества сообщений между двумя узлами намного быстрее через одно соединение, чем через множество соединений. Это следует из того, что QUIC использует TLS, что означает, что каждое соединение должно выполнять протокол установления связи. К счастью, QUIC также поддерживает множество логически независимых потоков через одно соединение, поэтому здесь нет реальных недостатков - меньше соединений, меньше проблем. Это проинформирует о последних этапах удаления пула соединений из qp2p, где нам нужно будет позаботиться о том, чтобы мы по-прежнему были эффективны с соединениями.

@Lionel.Faber работает над улучшением процесса развертывания тестовых сетей в Digital Ocean, и теперь требуется одобрение для каждого шага развертывания и отключения тестовой сети, что позволяет нам перезапускать каждый шаг или использовать тестовую сеть извне вместо ее автоматического уничтожения. .

Мы также доработали запуск узла / клиента. С помощью @yogesh, реализующего запись карты префиксов сети на диск, чтобы к ней можно было делиться, и поэтому клиенты и узлы могут начать с минимальных знаний о сети. И мы добавляли и улучшали некоторые локальные проверки журналов, чтобы было легче выявлять и отслеживать проблемы.

Хорошие новости из лабораторий DBC. Spentproofs сейчас работают в тестовой среде и скоро будут готовы к слиянию.


Фон номиналов

DBC Mint, использующий слепые подписи, не может видеть содержимое выходного DBC до его подписания, но он должен проверить, что сумма (входы) == сумма (выходы). Монетный двор не может доверять перевыпускающей стороне указать сумму на выходе, потому что это может быть ложью. Возможны схемы фиксации, но полученные переиздания становятся связанными, что противоречит цели слепых подписей.

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

Но оказывается, что это очень плохо для конфиденциальности и взаимозаменяемости. Уникальные суммы, такие как 4589234127, позволяют связать одно переиздание с другим. Подумайте на мгновение о деньгах. В долларах США мы платим бумажными номиналами 1, 2, 5, 10, 50, 100. А также монеты: .01, .05, .10, .25, .50. Когда вы покупаете что-то по цене 365,23 доллара, вы можете заплатить тремя 100, одним 50, одним 10, одним 5, двумя 0,20 и тремя 0,01. Каждую из этих банкнот или монет очень сложно связать с другими транзакциями, потому что очень много других людей используют те же самые суммы. Тем не менее, если бы вы могли каким-то образом изготовить бумажный счет на сумму 365,23, что ж, это довольно уникально и сделает ваш счет действительно заметным.

Следовательно, мы можем сказать, что каждое номинал представляет собой набор анонимности или пул, в котором ваша транзакция может скрыться. Чем больше у нас деноминаций (или пулов), тем меньше каждый пул. Таким образом, с точки зрения конфиденциальности / взаимозаменяемости, мы стремимся к минимально возможному количеству пулов, что на самом деле1, то есть самая маленькая из имеющихся единиц.

Однако мы должны также учитывать эффективность. Это измеряется количеством «монет» (банкнот или монет), необходимых для внесения сдачи на определенную сумму. В нашем примере с ценой 365,23 доллара нам потребовалось 11 отдельных монет. Кроме того, учтите, что в типичном переиздании DBC будет две логические выходные суммы: 1: платеж получателя и 2: сдача для отправителя. Создание, подписание и проверка монет DBC представляет собой работу для узлов монетного двора, а также значительный сетевой трафик, связанный с расходом каждого DBC. Поэтому мы должны попытаться свести к минимуму количество монет сдачи, необходимых для любой транзакционной суммы.

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

Суммы сделки по сравнению с номиналами

Когда мы думаем о номиналах, мы должны внимательно различать «суммы, подлежащие транзакции», и «суммы номиналов».

«Сумма транзакции» - это цена или сумма платежа, по которой осуществляется транзакция. Одна или несколько номинальных сумм могут быть объединены / добавлены для получения «транзакционной суммы».

В дальнейшем мы будем называть «номинальная сумма» просто «деноминация».

Мы будем использовать термин «монета» для обозначения конкретного экземпляра номинала.

Наш первый подход деноминации

примечание: код находится здесь.

Первоначально мы определили «транзакционную сумму» как 128-битное целое число (u128).

Затем мы определили ряд номиналов, основанных на степенях десяти, например:

'' enum { Один, два, три, четыре, пять, шесть, семь, восемь, девять, Десять, Двадцать, Тридцать, Сорок, Пятьдесят, Шестьдесят, Семьдесят, Восемь, Девяносто, Сотня, двести, триста, четыреста, пятьсот, шестьсот, семьсот, восемьсот, девятьсот, Тысяча, две тысячи, три тысячи, четыре тысячи, пять тысяч, шесть тысяч, семь тысяч, восемь тысяч, девять тысяч, Десять тысяч, двадцать тысяч, тридцать тысяч, сорок тысяч, пятьдесят тысяч, шестьдесят тысяч, семьдесят тысяч, восемьдесят тысяч, девяносто тысяч } ‘’

И так далее, до 10 ^ 38, что приближается к пределу u128. Общее количество купюр было около 340.

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

'' тау → таус мил → мил bil → bils триллы → триллы квад → квадроциклы квинт → квинт sic → sics набор → наборы ott → otts не → не det → dets unt → unts ‘’

Основная идея состоит в том, что «One» представляет собой наименьшую единицу, например, эквивалент в биткойне называется Satoshi. Мы вообще не будем определять (произвольное) расположение десятичной точки, а скорее рынок решит, какой набор единиц измерения будет использоваться для повседневных транзакций вскоре после запуска, и со временем, по мере увеличения «рыночной капитализации», он будет прогрессировать. от больших единиц, например, наборов, до небольших, например, квадроциклов.

В любом случае, десятичная точка - это только вещь, обращенная к пользователю (отображаемая), поскольку значение представлено как u128. Так что мы не будем здесь больше обсуждать это.

У этого подхода есть несколько хороших свойств. Определив 1–9 для каждой степени десяти, мы можем представить каждую цифру «транзакционной суммы» одной монетой или меньше, если есть нули.

Например, если у нас есть «транзакционная сумма» 55034, мы можем представить ее с помощью: 1 FiftyTau, 1 FiveTau, 1 Thirty и 1 Four. Итак, сумма состоит из 5 цифр, одна из которых равна нулю, и мы вносим в нее сдачу всего за 4 монеты.

У этого подхода также есть пара недостатков.

  1. Первое несколько второстепенное. Код для реализации перечисления с более чем 340 вариантами и сопоставленными именами огромен, и его сложно поддерживать. В итоге мы написали сценарий для создания файла исходного кода denominations.rs. Хотя это работало, это было неуклюжее и неудобное решение.

  2. Огромное количество разменных монет. В наших тестах среднее количество монет, необходимых для внесения изменений для случайно сгенерированных «транзакционных сумм» u128, составляло 38–39. И это для одного логического выхода. Помните, что у типичного переиздания будет два логических выхода, а может и больше. Таким образом, в среднем мы можем рассчитывать на 76+ выходных DBC на переиздание и сопоставимое количество входов. Это большая работа для монетного двора и сети, и это также ставит реализацию Blind Sig в серьезный недостаток эффективности по сравнению с реализацией Amount Hiding (но отслеживаемой).

Например, возьмем u128 :: MAX:
'' $ calc 2 ^ 128 340282366920938463463374607431768211456 ‘’

У нас есть вариант номинала для каждых [0…9] каждой степени десяти. В этом номере 40 цифр. Две цифры равны нулю, их можно игнорировать. Итак, нам нужно 38 «монет», чтобы представить это число. Например:
'' 3 * 10 ^ 38 4 * 10 ^ 37 2 * 10 ^ 35 8 * 10 ^ 34 ‘’
…и так далее…

Что, если мы будем использовать u64 вместо u128? Хорошо, эточуть лучше.
'' $ расчет 2 ^ 64 18446744073709551616 ‘’

Но это все равно 20 цифр. И теперь у нас есть только 19 степеней десяти, с которыми можно работать, поэтому наша потенциальная делимость более ограничена.

Теперь можно утверждать, что на практике пользователи предпочитают отправлять больше целых чисел с большим количеством нулей. Однако, похоже, это не так. @mav провел анализ всей цепочки блоков биткойнов и обнаружил, что для большинства сумм транзакций используется 8-значная точность с в основном случайными числами. Мы считаем, что это связано с тем, что люди по-прежнему в основном производят платежи на основе фиатных сумм (например, долларов США) и вычисляют эквивалентную сумму BTC, которая становится числом со многими значащими цифрами. Такое же поведение, казалось бы, применимо и к нашим DBC.

В любом случае система должна быть спроектирована таким образом, чтобы она работала адекватно / эффективно даже в худшем случае.

Поэтому нам нужно было найти лучший способ.

Наш второй (текущий) подход

Мы придумали более простое и мощное представление деноминаций. Мы кодируем показатель степени десяти в перечислении Denomination как целое число со знаком. Итак, вместо * огромного * перечисления с более чем 300 вариантами у нас есть следующее:

`` ‘’
тип паба PowerOfTen = i8;

pub enum Denomination {
Один (PowerOfTen),
Два (PowerOfTen),
Три (PowerOfTen),
Четыре (PowerOfTen),
Пять (PowerOfTen),
Шесть (PowerOfTen),
Семь (PowerOfTen),
Восемь (PowerOfTen),
Девять (PowerOfTen),
}
`` ‘’

Таким образом, с i8 мы можем представить 9 \ * 10 ^ -128…9 \ * 10 ^ 127. Помните, что u128, который уже считается * огромным *, дал нам только 10 ^ 38 с точки зрения делимости. Так что для * практических * целей это представление практически безгранично. (Мы могли бы легко пойти еще дальше, используя i16 или i32 вместо i8, но это кажется излишним.)

примечание: нижеследующее не является обсуждением планов денежно-кредитной политики SNT. Это чисто гипотетическое описание для иллюстративных целей.

Такой подход к номиналам не делает номинал «Один» равным наименьшей возможной сумме, как это было в нашем первом подходе. Вместо этого One может представлять нашу лучшую начальную догадку, например, 1 DBC = 1 USD. Мы могли бы стремиться к тому, чтобы «One» имел некоторую полезную покупательную способность с точки зрения реальных товаров, но не много. например, «Человеку» следует покупать банку газировки или, возможно, жевательную резинку, а не дом.

Также интересно подумать о том, какой может быть наша общая денежная масса. В качестве гипотетической отправной точки, допустим, мы придумываем сумму Genesis DBC, беря текущую рыночную капитализацию SNT и вычисляя сумму Genesis, так что 1 DBC = 1 доллар США. Я не подсчитал, какой будет сумма Genesis с помощью этого метода, но давайте представим, что это 50 миллионов. Итак, мы запускаем номинал One (1 * 10 ^ 0) = ~ 1 доллар США и Genesis DBC = 50 миллионов. Круто. На данный момент мы проигнорируем эффекты распределения / фарма, за исключением того, что они могут временно вызвать инфляцию / девальвацию валюты.

Итак, это дефляционная валюта (фиксированная денежная масса), и со временем каждая единица становится более ценной с точки зрения реальной мировой стоимости (иначе проект, вероятно, потерпит неудачу). По мере того, как это происходит, пользователи постепенно переходят на использование большего количества купюр 10 ^ -1. затем 10 ^ -2 и так далее. Мы можем разместить 128 таких 10-кратных расширений. Мы также можем разместить 3 миллиона SNT в одном dbc, например, используя номинал 3 \ * 10 ^ 6. Или намного, намного, намного больше, если мы, например, определим, что количество Genesis намного выше, вплоть до 9 \ * 10 ^ 127.


Именование

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

Шкала SI называет 10 ^ -24 … 10 ^ 24. Ну, не каждую степень десяти, но каждые 10 ^ 3, что достаточно хорошо. Когда мы вставляем их в код, мы можем сгенерировать:

'' 10 ^ -24-1 лет 10 ^ -23 - 10 лет 10 ^ -22 - 100 лет 10 ^ -21 - 1 зепто 10 ^ -20 - 10 зепто 10 ^ -19 - 100 зепто 10 ^ -18 - 1 атто 10 ^ -17-10 атто 10 ^ -16 - 100 атто 10 ^ -15-1 фемто 10 ^ -14-10 фемто 10 ^ -13 - 100 фемто 10 ^ -12 - 1 пик 10 ^ -11 - 10 пик 10 ^ -10 - 100 пико 10 ^ -9 - 1 нано 10 ^ -8 - 10 нано 10 ^ -7 - 100 нано 10 ^ -6 - 1 микро 10 ^ -5 - 10 микро 10 ^ -4 - 100 микро 10 ^ -3 - 1 милли 10 ^ -2 - 1 санти 10 ^ -1 - 1 деци 1 - 1 10 ^ 1 - 1 дека 10 ^ 2 - 1 га 10 ^ 3 - 1 кг 10 ^ 4 - 10 кг 10 ^ 5 - 100 кг 10 ^ 6 - 1 мега 10 ^ 7 - 10 мега 10 ^ 8 - 100 мега 10 ^ 9 - 1 гига 10 ^ 10 - 10 гига 10 ^ 11 - 100 гига 10 ^ 12 - 1 тера 10 ^ 13 - 10 тера 10 ^ 14 - 100 тера 10 ^ 15 -1 пета 10 ^ 16 - 10 пета 10 ^ 17 - 100 пета 10 ^ 18 - 1 exa 10 ^ 19 - 10 exa 10 ^ 20 - 100 exa 10 ^ 21 - 1 зетта 10 ^ 22 - 10 зетта 10 ^ 23 - 100 зетта 10 ^ 24 - 1 год 10 ^ 25 - 10 лет 10 ^ 26 - 100 лет ‘’

Если мы когда-нибудь достигнем наименований меньших, чем yocto, мы сможем придумать для них названия. Это на далекое будущее. На данный момент существует функция Amount :: to_si_string (), которая просто выводит экспоненциальное представление для безымянных значений.


Представление транзакционных сумм

Ранее мы использовали u128 для обозначения «транзакционных сумм». Но как мы поступаем с суммами, когда наше новое перечисление деноминации допускает значения, намного превышающие допустимые u128 (10 ^ 38), а также они могут быть отрицательными показателями, то есть десятыми, сотыми, тысячными и т. Д.?

Хорошо, здесь все становится немного сложнее.

Мы изменили sn_dbc :: Amount с псевдонима u128 на:

`` ‘’
тип паба PowerOfTen = i8;
Тип паба AmountCounter = u32;

pub struct Amount {
количество пабов: AmountCounter,
юнит паба: PowerOfTen,
}
`` ‘’

примечание: в будущем это может быть переименовано в TransactableAmount.

С помощью этой структуры мы можем представить денежные суммы как степень десяти (представляющую один номинал или * монету *) плюс счет, представляющий количество этих монет.

AmountCounter - это u32, поэтому мы можем представить более миллиарда единиц любого достоинства. В настоящее время мы ограничиваем его ровно 1 миллиардом (10 ^ 9).

Мы пришли к этому числу, подумав о типичных сделках сегодня. Физические лица регулярно производят платежи наличными на сумму до нескольких тысяч долларов, покупают дом за миллион долларов и т. Д. Но 100 миллионов tx - довольно редкое явление. И переводы на миллиард долларов очень редки - мы говорим об огромных корпорациях и правительствах. Мы хотели избежать принуждения людей менять свою (мысленную) единицу ценообразования для обычных транзакций. Но к тому времени, когда мы дойдем до разницы в 10 ^ 9 в единицах, мы окажемся в другой финансовой лиге.

Другой способ думать об этом заключается в том, что если Салли отправляет 1 миллиард долларов, ей, вероятно, все равно, если она не может использовать приращения в 1 доллар. Салли могла перевыпустить 1 миллиард + 10, но не могла перевыпустить 1 миллиард + 1 (или 2, 3, 4…). Салли, вероятно, сможет с этим смириться. Если мы установим предел, скажем, 100, и она сможет переиздать только 110, 120, но не 101, 102, это может стать большой проблемой для нее … и для большинства людей.

Теперь помните, что количество цифр в сумме определяет максимальное количество требуемых монет сдачи. Таким образом, используя 1 миллиард в качестве лимита, мы упали с 40 монет сдачи до 9 (максимум). Вполне нормально!

Мы могли бы отказаться от этого и дальше. 1 миллион = 10 ^ 6, или шесть монет. Это могло быть разумным выбором. 1 миллион - по-прежнему довольно высокий показатель для повседневных транзакций. Мы могли бы продолжать снижаться, с компромиссом, заключающимся в том, что люди должны начать указывать суммы / цены, используя более высокие единицы.

Таким образом, эта функция counter_limit () - это ручка, которую мы можем повернуть, чтобы сбалансировать эффективность системы и степень детализации «транзакционных сумм». Тестирование производительности может / поможет нам здесь, когда мы продвинемся немного дальше.


Расчет с суммой

Важно заранее отметить, что Amount по-прежнему использует только целочисленные вычисления. Никакие числа с плавающей запятой не используются.

Монетному двору необходимо проверить, что сумма (входы) == сумма (выходы). Мы реализовали несколько математических операторов: checked_sub (), checked_add () и checked_sum (). Они работают путем преобразования сумм в общую базовую единицу и последующего добавления или вычитания количества каждой из них. Результатом каждой операции является либо Amount, либо Error :: AmountIncompatible. Суммы несовместимы, если единицы находятся слишком далеко друг от друга, чтобы счетчик мог быть представлен в Amount :: counter_max () (1 миллиард).

Amount вообще не предоставляет обычные неотмеченные операторы Add, Sub, Sum, поэтому невозможно выполнять непроверенные операции. Также возвращаемое значение - это Результат, а не Вариант, как со встроенными типами.

Код Mint теперь вызывает Amount :: checked_sum () вместо sum (). Благодаря этому простому изменению Монетный двор теперь требует, чтобы все входы и выходы были совместимы, иначе переиздание не удастся.

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

Последствия использования входных данных

«Предел близости» 10 ^ 9 для Сумм применяется как к входам, так и к выходам в переиздании.

Однако входы и выходы суммируются отдельно. Таким образом, может случиться так, что отдельные входы и выходы не будут совместимы сами по себе, однако сумма входов и сумма выходов по-прежнему равны друг другу. Вот пример этого:

`` ‘’
входы:
9 * 10 ^ 8, 1 * 10 ^ 8, 9 * 10 ^ 0, 1 * 10 ^ 0
выходы:
1 * 10 ^ 9,1 * 10 ^ 1

$ вычислить 9 * 10 ^ 8 + 1 * 10 ^ 8 + 9 * 10 ^ 0 + 1 * 10 ^ 0
1000000010
$ вычислить 1 * 10 ^ 9 + 1 * 10 ^ 1
1000000010
`` ‘’
Так что переиздание прошло успешно.

Тем не менее, предел близости ограничивает то, что входы и выходы не могут быть слишком далеко друг от друга, особенно если мыустанавливают ограничение на количество входов и выходов. Таким образом, его можно рассматривать как (нечеткое) ограничение пыли в биткойнах, за исключением того, что он всегда относительно других сумм в перевыпуске, поэтому нигде нет фиксированного лимита, что приятно. :wink:

Этот предел близости должен побуждать людей совершать сделки с другими, используя наиболее часто используемые наименования. Потому что монетный двор не позволит мне заплатить вам за газировку одним DBC плюс 1 Pico DBC (10 ^ -12). В настоящее время мы не навязываем какое-либо максимальное количество входных DBC для переиздания, но это также нужно учитывать, что сделало бы невозможным оплату сотнями или тысячами крошечных монет вместо нескольких монет разумного размера.


Заключительные мысли

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

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

Хотя концепция TransactableAmount на первый взгляд может показаться необычной, она должна быть в основном прозрачной для пользователей. Первоначально все суммы транзакций менее 1 миллиарда DBC можно было просто выразить как целое число (с использованием базы 10 ^ 0). Только если кто-то превысит эту сумму или должен опуститься ниже 1 DBC, тогда нужно будет выбрать другую единицу для выражения суммы, и программное обеспечение кошелька может сделать это автоматически.

Глубокая делимость - хорошее свойство, позволяющее совершать крошечные микроплатежи, и то, что этот дизайн представляет собой своего рода счастливую случайность. 128 цифр делимости - это огромное количество, и мы могли бы пойти намного дальше с i16, если бы захотели. Для сравнения, биткойн имеет 8 десятичных знаков делимости, и большинство современных криптовалют аналогичны. 12 знаков после запятой считаются большими.

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

Любой, кто интересуется подробностями, может прочитать исходный код. amount.rs содержит длинный комментарий / объяснение.

код:


Полезные ссылки

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

Как проект с открытым исходным кодом, мы всегда ждем отзывов, комментариев и предложений сообщества - так что не стесняйтесь, присоединяйтесь и давайте вместе создадим безопасную сеть!