Локальная система обмена торговлей на базе Ergo
22 апреля 2019 г.

Локальная система обмена торговлей (LETS) — это местная ассоциация взаимного кредита, в которой участники могут создавать общие
кредитные деньги индивидуально, при этом все сделки в системе записываются в общий реестр.
В качестве примера предположим, что Алиса с нулевым балансом хочет купить литр сырого молока у Боба.
Сначала они согласовывают цену, например, предположим, что цена составляет около 2 евро (так как Алиса и Боб
живут в Ирландии). После записи сделки в реестр баланс Алисы становится -2 (минус
два) евро, а баланс Боба становится 2 евро. Затем Боб может потратить свои 2 евро, например, на
домашнее пиво от Чарли. Часто такие системы накладывают ограничения на отрицательные балансы, а иногда
даже на положительные, чтобы способствовать обмену в сообществе.
Исторически такие системы становились популярными в кризисные времена. Первая система была создана Майклом Линтоном в
канадском городе, застрявшем в депрессии, в 1981 году. Локальные системы обмена торговлей были чрезвычайно популярны в период
великой депрессии в Аргентине с 1998 по 2002 год. Большинство групп LETS насчитывают от 50 до 250 участников, с бумажными кредитными записками и
реестром, поддерживаемым основным комитетом. Однако бумажные валюты LETS показали некоторые проблемы, такие как
поддельные записки, возможное недобросовестное поведение управляющих системы и так далее. Поэтому LETS на основе блокчейна могут быть превосходнее
старых систем. Более подробную информацию о LETS можно найти в
книге "Экология денег" (автор Ричард Даутайте) и
Wikipedia.
В этой статье мы показываем, как LETS могут быть реализованы на базе Ergo. Насколько нам известно, это
первая реализация такого рода общественной валюты на базе блокчейна.
Наша эталонная реализация
проста и состоит из двух контрактов, а именно, контракта управления и контракта обмена.
Мы пропускаем предварительные сведения об Ergo, поэтому, пожалуйста, прочитайте
статью ICO и
учебники по ErgoScript (базовый и
расширенный) для начинающих.
Тем не менее, мы собираемся ввести несколько новых терминов в следующих предложениях.
Если токен выпускается с количеством, равным одному, мы называем его одиночным токеном. Аналогично,
коробка, содержащая одиночный токен, называется одиночной коробкой.
Контракт управления контролирует одиночную коробку, которая содержит участников системы LETS.
Контракт позволяет добавлять новых участников с темпом один участник за одну транзакцию. Коробка
не хранит участников, а только небольшой дайджест аутентифицированной структуры данных, построенной на основе
каталога участников. Участник ассоциируется с одиночным токеном, выпущенным в транзакции, которая
добавляет участника в каталог. Транзакция создает новую коробку участника, которая содержит
одиночный токен участника. Коробка участника защищена контрактом обмена. Также, новая
созданная коробка участника имеет начальный баланс, записанный в регистре R4, и баланс
равен нулю в нашем примере. Транзакция, создающая нового участника, должна предоставить доказательство корректности для
преобразования каталога.
Коробка контракта управления обычно контролируется комитетом, и комитет может эволюционировать со временем. Чтобы поддержать
это, мы позволяем логике комитета находиться в регистре R5.
Например, предположим, что новый член комитета был добавлен вместе с новым участником LETS,
входная коробка контракта управления требует 2 из 3 подписей, а выходная коробка требует 3 из 4 подписей.
В этом случае содержимое регистра R5 во входной и выходной коробках будет отличаться.
Код контракта управления на ErgoScript с комментариями приведен ниже. Обратите внимание, что
"userContractHash" относится к хешу контракта обмена.
val selfOut = OUTPUTS(0)
// Скрипт управления
val managementScript = selfOut.R5[SigmaProp].get
// Шаблон скрипта управления дублирует себя, и скрипт управления удовлетворен
val scriptCorrect = (selfOut.propositionBytes == SELF.propositionBytes) && managementScript
// Транзакция расходования создает коробки для каталога, пользователя, комиссии.
val outsSizeCorrect = OUTPUTS.size == 3
// Проверяет, что токен метки управления дублирует себя
val outTokenCorrect = (selfOut.tokens.size == 1) && (selfOut.tokens(0)._1 == letsToken)
// Проверяет, что новый токен выпущен, и его количество корректно
// OUTPUTS(0) токены уже проверены через outtokenCorrect
val issuedTokenId = INPUTS(0).id
val userOut = OUTPUTS(1)
val correctTokenAmounts =
(userOut.tokens.size == 1 &&
userOut.tokens(0)._1 == issuedTokenId &&
userOut.tokens(0)._2 == 1 &&
OUTPUTS(2).tokens.size == 0 &&
outTokenCorrect)
// Проверяет, что новый пользователь был создан с нулевым балансом
val zeroUserBalance = userOut.R4[Long].get == 0
val properUserScript = blake2b256(userOut.propositionBytes) == userContractHash
// Проверяет, что новый идентификатор токена был добавлен в каталог
val selfTree = SELF.R4[AvlTree].get
val toAdd: Coll[(Coll[Byte], Coll[Byte])] = Coll((issuedTokenId, Coll[Byte]()))
val proof = getVar[Coll[Byte]](1).get
val modifiedTree = selfTree.insert(toAdd, proof).get
val expectedTree = selfOut.R4[AvlTree].get
val treeCorrect = modifiedTree == expectedTree
correctTokenAmounts && scriptCorrect && treeCorrect && zeroUserBalance && properUserScript
correctTokenAmounts && scriptCorrect && treeCorrect && zeroUserBalance && properUserScript correctTokenAmounts && scriptCorrect && treeCorrect && zeroUserBalance && properUserScript
Скрипт контракта обмена довольно прост и приведен ниже вместе с комментариями, описывающими его логику. В
контракте предполагается, что транзакция расходования для коробки контракта обмена получает как минимум два входа,
и первые два входа должны быть защищены скриптом контракта обмена и содержать токены участников LETS. Чтобы проверить,
что одиночные токены участников во входах действительно принадлежат системе LETS, транзакция расходования предоставляет коробку контракта управления
в качестве первого входа только для чтения, а также должна предоставить доказательство того, что токены участников действительно принадлежат
каталогу, аутентифицированному через регистр R4 коробки контракта управления. "letsToken" в скрипте относится к
одиночному токену коробки управления.
// Минимальный баланс, разрешенный для трейдера LETS
val minBalance = -20000
val lookupProof = getVar[Coll[Byte]](1).get
// Коробка только для чтения, которая содержит каталог участников LETS
val treeHolderBox = CONTEXT.dataInputs(0)
val properLetsToken = treeHolderBox.tokens(0)._1 == letsToken
val membersTree = treeHolderBox.R4[AvlTree].get
// Транзакция расходования берет две коробки участников LETS, желающих совершить сделку,
// и возвращает коробки с измененными балансами.
val participant0 = INPUTS(0)
val participant1 = INPUTS(1)
val participantOut0 = OUTPUTS(0)
val participantOut1 = OUTPUTS(1)
//Проверяет, что участники действительно принадлежат LETS
val token0 = participant0.tokens(0)._1
val token1 = participant1.tokens(0)._1
val memberTokens = Coll(token0, token1)
val membersExist = membersTree.getMany(memberTokens, lookupProof).forall({ (o: Option[Coll[Byte]]) => o.isDefined })
// Проверяет, что изменения баланса участников LETS во время сделки корректны
val initialBalance0 = participant0.R4[Long].get
val initialBalance1 = participant1.R4[Long].get
val finishBalance0 = participantOut0.R4[Long].get
val finishBalance1 = participantOut1.R4[Long].get
val diff0 = finishBalance0 - initialBalance0
val diff1 = finishBalance1 - initialBalance1
val diffCorrect = diff0 == -diff1
val balancesCorrect = (finishBalance0 > minBalance) && (finishBalance1 > minBalance) && diffCorrect
// Проверяет, что коробки участников сохраняют свои скрипты.
// todo: здесь можно сделать оптимизацию
val script0Saved = participantOut0.propositionBytes == participant0.propositionBytes
val script1Saved = participantOut1.propositionBytes == participant1.propositionBytes
val scriptsSaved = script0Saved && script1Saved
// Защита коробки, специфичная для участника
val selfPubKey = SELF.R5[SigmaProp].get
selfPubKey && properLetsToken && membersExist && diffCorrect && scriptsSaved
Обратите внимание, что оба контракта могут быть изменены различными способами для получения новых систем с различными свойствами. Так что, надеемся,
когда-нибудь эта статья будет продолжена!
Share post
13 августа 2025 г.
9 июля 2025 г.
12 мая 2025 г.






