Beztrustowy lokalny system wymiany handlowej
29 maja 2019

Lokalny system wymiany handlowej (LETS) ma na celu rozwój lokalnej gospodarki i jest zazwyczaj używany przez ludzi z danej okolicy. Aby uzyskać krótki przegląd LETS, zobacz ten link, który opisuje również implementację ErgoScript zarządzanego przez komitet LETS. Taki system nazywamy zarządzanym lub uprawnionym, ponieważ zależy od komitetu zaufanych członków, aby przyjmować nowych członków do LETS.
Tutaj opisujemy beztrustowy LETS, tzn. taki, w którym nie jest potrzebny komitet zarządzający do przyjęcia.
Przegląd
LETS obejmuje kilka stron, które zgadzają się na używanie jakiejś formy "lokalnej waluty", zazwyczaj powiązanej z główną walutą kraju w stosunku 1:1. Załóżmy, że nasz LETS ma siedzibę w europejskim kraju, gdzie walutą są euro, a wymiana odbywa się w "lokalnych euro", które są uważane za równoważne krajowym euro.
Każdy użytkownik w LETS ma konto, które zawiera saldo LETS tego użytkownika (w lokalnych euro). Po dołączeniu każdy użytkownik ma saldo zerowe. Saldo jest przechowywane w (możliwie zdecentralizowanej) księdze. Ciekawą cechą LETS jest to, że użytkownik z zerowym saldem może również "wypłacić" pieniądze, ale tylko na zapłatę innemu użytkownikowi LETS. W każdej chwili suma sald LETS wszystkich użytkowników wynosi zero.
Na przykład, Alicja z zerowym saldem chce kupić litr mleka za 2 euro od Boba, który również jest członkiem LETS z zerowym saldem. Przenosi 2 euro ze swojego konta na konto Boba, co sprawia, że jej saldo wynosi -2, a Boba +2. Bob może następnie przenieść część lub całość swojego salda do innego użytkownika LETS w zamian za towary lub usługi.
Beztrustowy LETS
Ponieważ chcemy beztrustowego LETS, nie możemy polegać na żadnej zaufanej grupie ludzi, aby przyjmować użytkowników. Należy zauważyć, że nadal będziemy mieć komitet do wykonywania niektórych zadań, takich jak ustalanie parametrów LETS (lokalna waluta, maksymalna liczba członków itp.) oraz pobieranie jakiejkolwiek opłaty za dołączenie.
Założymy tylko zaufanego orakla cenowego, który podaje aktualny kurs euro do ergów zidentyfikowany przez jakiś globalny identyfikator (rateTokenID) oraz singleton box zawierający dokładnie jeden token z tym identyfikatorem. Singleton box, opisany tutaj, to box zawierający singleton token, tzn. token z tylko jedną ilością w istnieniu. Ten box zawiera również kurs ergów do euro w danym okresie czasu. Kurs jest aktualizowany przez wydanie tego boxa i stworzenie innego singleton boxa z nowym kursem.
W każdej chwili nasz LETS jest unikalnie definiowany przez globalny token box, który zawiera jakieś membership tokens z identyfikatorem letsTokenID. Ten box definiuje parametry LETS, takie jak lokalizacja, jednostka walutowa, rateTokenID itp. Token box jest początkowo uruchamiany z, powiedzmy, 10000 tokenami członkowskimi. Użytkownicy mogą wydać ten box i stworzyć swoje indywidualne LETS boxes jako wyjścia transakcji, tak aby każde takie wyjście miało dokładnie jeden token członkowski, a pozostałe tokeny członkowskie zostały umieszczone w nowo utworzonym token boxie.
LETS box reprezentuje członka LETS i musi być używany w każdej transakcji. Dla uproszczenia, ten artykuł ogranicza wszystkie transakcje LETS do dokładnie dwóch członków, z których jeden jest nadawcą, a drugi odbiorcą, tak aby nadawca przekazywał pewną dodatnią kwotę waluty LETS (lokalnych euro) do odbiorcy. Taka transakcja konsumuje boxy członków i odtwarza je jako wyjście z zaktualizowanym saldem.
Podstawowa wersja
Aby zapobiec spamowi i atakom DDoS, wymagamy, aby w nowo utworzonym boxie członka zablokowana była co najmniej pewna minimalna liczba ergów (minErgsToJoin). Ergi będą zablokowane, aż co najmniej minWithdrawTime liczba bloków zostanie wydobyta. Box może mieć ujemne saldo LETS do kwoty, która może być pokryta przez zablokowane ergi (używając kursu w momencie transakcji).
// a tokenBox stores the membership tokens and has this script
val tokenBox = OUTPUTS(0) // the first output must also be a tokenBox
// first output contains remaining LETS tokens
def isLets(b:Box) = { // returns true if b is a LETS box
// A LETS box must have exactly 1 membership token in tokens(0)
b.tokens(0)._1 == letsTokenID && b.tokens(0)._2 == 1 &&
blake2b256(b.propositionBytes) == memberBoxScriptHash &&
SELF.R4[Long].get == 0 && // start the box with zero LETS balance
b.value >= minErgsToJoin && // the box must contain some minimum ergs
b.R6[Long].get <= HEIGHT // store the creation height in R6
}
// how many lets boxes creared in the tx
val numLetsBoxes = OUTPUTS.filter({(b:Box) => isLets(b)}).size
// In the transaction following is preserved for the token box ...
tokenBox.tokens(0)._1 == SELF.tokens(0)._1 && // token id
tokenBox.tokens(0)._2 == SELF.tokens(0)._2 - numLetsBoxes && // quantity
tokenBox.propositionBytes == SELF.propositionBytes // script
Box członka LETS jest chroniony przez poniższy skrypt, którego hash memberBoxScriptHash jest używany powyżej. Skrypt wymaga dokładnie jednej pary (nadawca, odbiorca) na transakcję.
val validRateOracle = CONTEXT.dataInputs(0).tokens(0)._1 == rateTokenID
val rate = CONTEXT.dataInputs(0).R4[Int].get
val inBalance = SELF.R4[Long].get // LETS balance of current input
val pubKey = SELF.R5[SigmaProp].get // owner of the current input
val createdAt = SELF.R6[Long].get // height at which current input was mined
val index = getVar[Int](0).get // index of the corresponding output
val out = OUTPUTS(index)
val outBalance = out.R4[Long].get // LETS balance of the output
// A LETS box is one that has the same script as the current box
val isMemberBox = {(b:Box) => b.propositionBytes == SELF.propositionBytes}
val letsInputs = INPUTS.filter(isMemberBox) // all LETS input boxes
val letsOutputs = OUTPUTS.filter(isMemberBox) // all LETS output boxes
// The current input belongs to the receiver if its LETS balance increases
// There may be some ergs in receiver's input box. We need to ensure that
// the receiver's output box also contains the same amount of ergs as input
val receiver = outBalance > inBalance && out.value == SELF.value
val getBalance = {(b:Box) => b.R4[Long].get} // returns LETS balance of a box
val letsBalIn = letsInputs.map(getBalance).fold(0L, {(l:Long, r:Long) => l + r})
val letsBalOut = letsOutputs.map(getBalance).fold(0L, {(l:Long, r:Long) => l + r})
// sender box can contain less amount of ergs (sender may withdraw ergs provided
// that any negative LETS balance of sender in out is backed by sufficient ergs)
val correctErgs = out.value >= -outBalance * rate && (
out.value >= SELF.value || SELF.R6[Long].get + minWithdrawTime > HEIGHT
)
// for the receiver, we don't touch the erg balance,
// since a receiver is not actively involved in the transaction
inBalance != outBalance && // some transaction should occur; balance must change
SELF.tokens(0)._1 == letsTokenID && // the current input has the right token
out.tokens(0)._1 == letsTokenID && // corresponding output has the right token
validRateOracle && // oracle providing rate has the correct "rate token"
letsBalIn == letsBalOut && // total LETS balance is preserved in the transaction
letsInputs.size == 2 && letsOutputs.size == 2 && // only two LETS inputs, outputs
out.propositionBytes == SELF.propositionBytes && // out is a LETS box ...
out.R5[SigmaProp].get == pubKey && // ... with the right pub key
out.R6[Long].get == SELF.R6[Long].get && // ... and creation height
(receiver || // either current input belongs to receiver ...
(pubKey && correctErgs) // ... or out has correct ergs and tx has signature
)
Transakcja wydająca box z powyższym skryptem wymaga:
- Suma sald LETS wejść i wyjść jest zachowana
- Istnieją dwa LETS wejścia i dwa LETS wyjścia
- Klucze publiczne (przechowywane w R5) są zachowane w odpowiednim wyjściu
- Wysokość utworzenia (przechowywana w R6) jest zachowana w odpowiednim wyjściu
Mówimy, że jakiś klucz publiczny jest odbiorcą, jeśli saldo LETS jego wyjścia jest wyższe niż saldo jego wejścia.
Ostatni warunek wymaga, aby albo boxy wejściowe i wyjściowe należały do odbiorcy (tak aby ergi były zachowane), albo, w przypadku gdy należą do nadawcy, dostarczony został podpis, a wyjście było zabezpieczone wymaganą liczbą ergów, jeśli jego saldo LETS jest ujemne. Ponadto wymaga, aby saldo ergów nadawcy nie mogło być zmniejszone, dopóki co najmniej minWithdrawTime liczba bloków nie zostanie wydobyta po zablokowaniu ergów.
W porównaniu do zarządzanego LETS, powyższy system ma następujące różnice:
- Brak rejestru członkostwa: W przeciwieństwie do zarządzanego LETS, nie przechowujemy tutaj żadnych informacji o członkostwie.
- Wiele boxów: Osoba może tworzyć wiele boxów członkowskich, co jest dozwolone. Wymagamy tylko, aby jakiekolwiek ujemne saldo było zabezpieczone odpowiednią liczbą ergów zablokowanych w nim.
LETS-1: Suma zerowa, zabezpieczenie
Powyższe to podstawowa wersja, którą nazywamy LETS-1. Ma ona następujące cechy:
- Czasowo zablokowana opłata za dołączenie: Aby zapobiec atakom spamowym, członek musi uiścić pewną minimalną opłatę w ergach w momencie dołączenia. Ta opłata jest zwrotna, ale tylko po określonej liczbie bloków.
- Suma zerowa: Suma sald LETS wszystkich boxów członkowskich wynosi zero. Boxy członkowskie mogą mieć ujemne saldo, o ile mieści się ono w określonym limicie.
- Zabezpieczenie: Dla wyjścia nadawcy ergi są używane jako zabezpieczenie do pokrycia ujemnego salda LETS po aktualnym kursie wymiany.
Poniżej przedstawiono niektóre warianty LETS-1.
LETS-2: Suma zerowa, bez zabezpieczenia
To jest niewielka modyfikacja LETS-1, jak następuje:
- Niezwrotna opłata za dołączenie: Podobnie jak w LETS-1, wymagana jest opłata za dołączenie, aby zapobiec atakom spamowym. Jednak w przeciwieństwie do LETS-1, ta opłata jest niezwrotna i musi być wysłana do jakiegoś zdefiniowanego komitetu zarządzającego.
- Suma zerowa: Jak w LETS-1.
LETS-3: Suma dodatnia, zabezpieczenie
Powyższe dwa warianty wymagają, aby całkowite saldo LETS zawsze wynosiło zero. Tutaj rozważamy dodatnią wartość tej sumy. W szczególności ten wariant ma następujące właściwości:
- Czasowo zablokowana opłata za dołączenie: Jak w LETS-1.
- Dodatnia suma: Saldo LETS każdego członka musi być zawsze nieujemne. Zapewnia to, że suma sald LETS wszystkich boxów członkowskich jest dodatnia. Początkowe saldo LETS jest ustalane na dodatnią wartość na podstawie opłaty za dołączenie po aktualnym kursie, ograniczonej do pewnej maksymalnej wartości.
- Zabezpieczenie: Każde zmniejszenie salda ergów nadawcy musi być połączone ze zmniejszeniem odpowiadającego salda LETS po aktualnym kursie wymiany.
Możemy również pozwolić na doładowanie salda LETS podczas transakcji, dodając równoważną ilość ergów.
LETS-4: Suma dodatnia, bez zabezpieczenia
To jest podobne do LETS-3, ale z pewnymi małymi modyfikacjami:
- Niezwrotna opłata za dołączenie: Jak w LETS-2
- Dodatnia suma: Jak w LETS-3
Poniższa tabela podsumowuje warianty:
| Suma zerowa | Suma dodatnia | |
|---|---|---|
| Zabezpieczenie | LETS-1 | LETS-3 |
| Bez zabezpieczenia | LETS-2 | LETS-4 |
Rozważaliśmy transakcje LETS z udziałem pojedynczej pary nadawca-odbiorca. Bardziej zaawansowane modele mogą pozwalać na wielu nadawców i odbiorców, i nie muszą być w parach.
Share post
13 sierpnia 2025
12 maja 2025






