Un Sistema di Scambio Locale Senza Fiducia
29 maggio 2019

Un Sistema di Scambio Locale (LETS) è mirato a sviluppare l'economia locale ed è solitamente utilizzato da persone di una località vicina tra loro. Per una breve panoramica del LETS, vedere questo link, che descrive anche un'implementazione di ErgoScript di un LETS gestito da un comitato. Chiamiamo un tale sistema gestito o con permesso, poiché dipende da un comitato di membri fidati per iscrivere nuovi membri nel LETS.
Qui descriviamo un LETS senza fiducia, cioè uno in cui non è necessario un comitato di gestione per l'iscrizione.
Panoramica
Il LETS coinvolge diverse parti che concordano di utilizzare una forma di "moneta locale", solitamente ancorata alla valuta principale del paese a un tasso di 1:1. Supponiamo che il nostro LETS sia basato in un paese europeo dove la valuta è l'Euro, e lo scambio avviene in "Euro locali", che sono considerati equivalenti agli Euro nazionali.
Ogni utente nel LETS ha un conto, che contiene il saldo LETS di quell'utente (in Euro Locali). All'iscrizione, ogni utente ha un saldo di zero. Il saldo è memorizzato in un registro (possibilmente decentralizzato). Una caratteristica interessante del LETS è che un utente con saldo zero può anche "prelevare" denaro, ma solo per pagare un altro utente LETS. In qualsiasi momento, la somma dei saldi LETS di tutti gli utenti è zero.
Ad esempio, Alice con saldo zero desidera acquistare un litro di latte per 2 Euro da Bob, che è anche un membro del LETS con saldo zero. Trasferisce 2 Euro dal suo conto a quello di Bob, facendo sì che il suo saldo diventi -2 e quello di Bob +2. Bob può quindi trasferire parte o tutto il suo saldo a un altro utente LETS in cambio di beni o servizi.
LETS Senza Fiducia
Poiché desideriamo un LETS senza fiducia, non possiamo dipendere da alcun gruppo fidato di persone per ammettere utenti. Si noti che avremo comunque un comitato per svolgere alcuni compiti come impostare i parametri del LETS (moneta locale, numero massimo di membri, ecc.) e consumare eventuali costi di iscrizione.
Assumeremo solo un oracolo di prezzo fidato che fornisce il tasso attuale di euro a ergs identificato da un qualche id globale (rateTokenID) e una scatola singleton contenente esattamente un token con questo id. Una scatola singleton, descritta qui, è una scatola contenente un token singleton, cioè un token con solo una quantità esistente. Questa scatola contiene anche il tasso di ergs a euro in un dato periodo di tempo. Il tasso viene aggiornato spendendo questa scatola e creando un'altra scatola singleton con il nuovo tasso.
In qualsiasi istante, il nostro LETS è unicamente definito da una scatola token globale che contiene alcuni token di adesione con id letsTokenID. Questa scatola definisce i parametri del LETS come la posizione, l'unità di valuta, rateTokenID, ecc. La scatola token è inizialmente avviata con, diciamo, 10000 token di adesione. Gli utenti possono spendere questa scatola e creare le proprie scatole LETS individuali come output della transazione, in modo tale che ciascun output abbia esattamente un token di adesione e i token di adesione rimanenti siano messi in una nuova scatola token creata.
Una scatola LETS rappresenta un membro del LETS e deve essere utilizzata in ogni transazione. Per semplicità, questo articolo limita tutte le transazioni LETS a coinvolgere esattamente due membri, uno che è il mittente e l'altro il ricevente, in modo tale che il mittente trasferisca un certo importo positivo della valuta LETS (euro locali) al ricevente. Tale transazione consuma le scatole dei membri e le ricrea come output con il saldo aggiornato.
La Variante Base
Per prevenire spam e attacchi DDoS, richiediamo almeno un certo numero minimo di ergs (minErgsToJoin) da essere bloccati nella scatola del nuovo membro creata. Gli ergs saranno bloccati fino a quando non saranno stati estratti almeno minWithdrawTime blocchi. Una scatola è autorizzata ad avere un saldo LETS negativo fino all'importo che può essere coperto dagli ergs bloccati (utilizzando il tasso al momento del commercio).
// una tokenBox memorizza i token di adesione e ha questo script
val tokenBox = OUTPUTS(0) // il primo output deve essere anche una tokenBox
// il primo output contiene i token LETS rimanenti
def isLets(b:Box) = { // restituisce true se b è una scatola LETS
// Una scatola LETS deve avere esattamente 1 token di adesione in tokens(0)
b.tokens(0)._1 == letsTokenID && b.tokens(0)._2 == 1 &&
blake2b256(b.propositionBytes) == memberBoxScriptHash &&
SELF.R4[Long].get == 0 && // inizia la scatola con saldo LETS zero
b.value >= minErgsToJoin && // la scatola deve contenere alcuni ergs minimi
b.R6[Long].get <= HEIGHT // memorizza l'altezza di creazione in R6
}
// quanti scatole lets create nella tx
val numLetsBoxes = OUTPUTS.filter({(b:Box) => isLets(b)}).size
// Nella transazione seguente è preservato per la scatola token ...
tokenBox.tokens(0)._1 == SELF.tokens(0)._1 && // id token
tokenBox.tokens(0)._2 == SELF.tokens(0)._2 - numLetsBoxes && // quantità
tokenBox.propositionBytes == SELF.propositionBytes // script
La scatola di un membro LETS è protetta dallo script qui sotto, il cui hash memberBoxScriptHash è utilizzato sopra. Lo script richiede esattamente una coppia (mittente, ricevente) per transazione.
val validRateOracle = CONTEXT.dataInputs(0).tokens(0)._1 == rateTokenID
val rate = CONTEXT.dataInputs(0).R4[Int].get
val inBalance = SELF.R4[Long].get // saldo LETS dell'input corrente
val pubKey = SELF.R5[SigmaProp].get // proprietario dell'input corrente
val createdAt = SELF.R6[Long].get // altezza alla quale l'input corrente è stato estratto
val index = getVar[Int](0).get // indice dell'output corrispondente
val out = OUTPUTS(index)
val outBalance = out.R4[Long].get // saldo LETS dell'output
// Una scatola LETS è quella che ha lo stesso script della scatola corrente
val isMemberBox = {(b:Box) => b.propositionBytes == SELF.propositionBytes}
val letsInputs = INPUTS.filter(isMemberBox) // tutte le scatole di input LETS
val letsOutputs = OUTPUTS.filter(isMemberBox) // tutte le scatole di output LETS
// L'input corrente appartiene al ricevente se il suo saldo LETS aumenta
// Potrebbero esserci alcuni ergs nella scatola di input del ricevente. Dobbiamo assicurarci che
// la scatola di output del ricevente contenga anche la stessa quantità di ergs dell'input
val receiver = outBalance > inBalance && out.value == SELF.value
val getBalance = {(b:Box) => b.R4[Long].get} // restituisce il saldo LETS di una scatola
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})
// la scatola del mittente può contenere un importo inferiore di ergs (il mittente può prelevare ergs forniti
// che qualsiasi saldo LETS negativo del mittente nell'output è coperto da ergs sufficienti)
val correctErgs = out.value >= -outBalance * rate && (
out.value >= SELF.value || SELF.R6[Long].get + minWithdrawTime > HEIGHT
)
// per il ricevente, non tocchiamo il saldo degli ergs,
// poiché un ricevente non è attivamente coinvolto nella transazione
inBalance != outBalance && // deve verificarsi qualche transazione; il saldo deve cambiare
SELF.tokens(0)._1 == letsTokenID && // l'input corrente ha il token corretto
out.tokens(0)._1 == letsTokenID && // l'output corrispondente ha il token corretto
validRateOracle && // l'oracolo che fornisce il tasso ha il corretto "token di tasso"
letsBalIn == letsBalOut && // il saldo totale LETS è preservato nella transazione
letsInputs.size == 2 && letsOutputs.size == 2 && // solo due input LETS, output
out.propositionBytes == SELF.propositionBytes && // out è una scatola LETS ...
out.R5[SigmaProp].get == pubKey && // ... con la giusta chiave pubblica
out.R6[Long].get == SELF.R6[Long].get && // ... e altezza di creazione
(receiver || // o l'input corrente appartiene al ricevente ...
(pubKey && correctErgs) // ... o out ha ergs corretti e la tx ha la firma
)
La transazione che spende una scatola con lo script sopra richiede:
- La somma del saldo LETS degli input e degli output è preservata
- Ci sono due input LETS e due output LETS
- Le chiavi pubbliche (memorizzate in R5) sono preservate nell'output corrispondente
- L'altezza di creazione (memorizzata in R6) deve essere preservata nell'output corrispondente
Diciamo che una certa chiave pubblica è il ricevente se il saldo LETS del suo output è superiore a quello del suo input.
L'ultima condizione richiede che o le scatole di input e output appartengano al ricevente (in modo che gli ergs siano preservati), oppure, nel caso appartengano al mittente, venga fornita una firma e l'output sia supportato dal numero richiesto di ergs se il suo saldo LETS è negativo. Inoltre, richiede che il saldo di ergs del mittente non possa essere ridotto fino a quando non siano stati estratti almeno minWithdrawTime blocchi dopo che gli ergs sono stati bloccati.
Rispetto al LETS gestito, il sistema sopra ha le seguenti differenze:
- Nessun record di adesione: A differenza del LETS gestito, non memorizziamo alcuna informazione di adesione qui.
- Scatole multiple: Una persona può creare più scatole di adesione, il che è consentito. Richiediamo solo che qualsiasi saldo negativo sia supportato dal numero corrispondente di ergs bloccati in essa.
LETS-1: Somma Zero, Collaterale
Questa è la variante base, che chiamiamo LETS-1. Ha le seguenti caratteristiche:
- Quota di Iscrizione Bloccata nel Tempo: Per prevenire attacchi di spam, un membro deve pagare una certa quota minima in ergs al momento dell'iscrizione. Questa quota è rimborsabile ma solo dopo un numero predefinito di blocchi.
- Somma Zero: La somma dei saldi LETS di tutte le scatole dei membri è zero. Le scatole dei membri possono avere un saldo negativo purché sia entro un certo limite.
- Collaterale: Per l'output del mittente, gli ergs sono utilizzati come collaterale per coprire il saldo LETS negativo al tasso di cambio attuale.
Le seguenti sono alcune variazioni di LETS-1.
LETS-2: Somma Zero, Nessun collaterale
Questa è una leggera variazione di LETS-1 come segue:
- Quota di iscrizione non rimborsabile: Simile a LETS-1, è necessaria una quota di iscrizione per prevenire attacchi di spam. Tuttavia, a differenza di LETS-1, questa quota non è rimborsabile e deve essere inviata a un comitato di gestione predefinito.
- Somma Zero: Come in LETS-1.
LETS-3: Somma Positiva, Collaterale
Le due varianti sopra richiedono che il saldo totale LETS sia sempre zero. Qui consideriamo un valore positivo per questa somma. In particolare, questa variante ha le seguenti proprietà:
- Quota di Iscrizione Bloccata nel Tempo: Come in LETS-1.
- Somma Positiva: Il saldo LETS di ogni membro deve essere sempre non negativo. Questo garantisce che la somma dei saldi LETS di tutte le scatole dei membri sia positiva. Il saldo LETS iniziale è impostato su un valore positivo basato sulla quota di iscrizione al tasso attuale, limitato a un certo valore massimo.
- Collaterale: Qualsiasi riduzione nel saldo di ergs del mittente deve essere accompagnata da una riduzione del corrispondente saldo LETS al tasso di cambio attuale.
Possiamo anche consentire di aumentare il saldo LETS durante una transazione aggiungendo l'importo equivalente di ergs.
LETS-4: Somma Positiva, Nessun collaterale
Questo è simile a LETS-3 ma con alcune piccole variazioni:
- Quota di Iscrizione Non Rimborsabile: Come in LETS-2
- Somma Positiva: Come in LETS-3
La seguente tabella riassume le varianti:
| Somma Zero | Somma Positiva | |
|---|---|---|
| Collaterale | LETS-1 | LETS-3 |
| Nessun collaterale | LETS-2 | LETS-4 |
Abbiamo considerato transazioni LETS che coinvolgono una singola coppia mittente-ricevente. Modelli più avanzati possono consentire più mittenti e riceventi, e non devono essere in coppie.
Share post
13 agosto 2025
9 luglio 2025
12 maggio 2025
7 agosto 2022




















