Esercitazione Avanzata di ErgoScript

Aggiornato il 8 Giugno 2022

Tempo di lettura stimato: 25 minuti

Sviluppatori Ergo
25 aprile 2019

Astratto #

Ergo è una piattaforma di smart contract basata sul modello UTXO di Bitcoin e su funzionalità simili a Ethereum che fornisce tramite un linguaggio chiamato ErgoScript. La sintassi di ErgoScript è un sottoinsieme di quella di Scala. In questo articolo, forniamo una panoramica di alto livello di ErgoScript utilizzando degli esempi. Utilizziamo ErgoScript per creare contratti intelligenti per diversi protocolli come un gioco XOR, un gioco sasso-carta-forbici,indirizzi reversibiliche hanno caratteristiche antifurto eErgoMix, un protocollo di miglioramento della privacy, che può essere considerato una variante non interattiva di CoinJoin.

Introduzione #

Una caratteristica fondamentale di ErgoScript è l’uso diProtocolli Sigma(Σ-protocolli scritti)[1] intercalati
con predicati sulla transazione e sullo stato della blockchain. ErgoScript attualmente supporta due di
questi protocolli definiti su un gruppoGdi primo ordineq, scritto qui in forma moltiplicativa. Il primo,
indicato comeproveDlog(u),è unprova di conoscenza del Logaritmo Discretodi qualche elemento di
gruppo arbitrarioturispetto ad un generatore fissog, dove chi spende ne dimostra la conoscenza Xtale che tu=gX.

Questo è derivato dalle firme di Schnorr [2]. Il secondo, indicato comedimostrareDTupla,è un
prova della conoscenza di Diffie-Hellman Tupleed è spiegato nella Sezione 3.3.
La struttura principale in ErgoScript è ascatola, che è più o meno come un UTXO di Bitcoin. Una transazione spende (distrugge) alcune caselle utilizzandole come input e crea nuove caselle come output. ErgoScript viene utilizzato per scrivere il filecondizione di spesaproteggere i fondi conservati in una scatola. Chi spende una scatola deve fornire una “prova” di soddisfare tale condizione. Le sezioni seguenti presentano gli smart contract che utilizzano ErgoScript. Maggiori dettagli su ErgoScript sono disponibili nel white paper [3] e il codice per gli esempi seguenti è disponibile su GitHub [4].


Esempi di base: Contratti di spesa migliorati #

Gli esempi seguenti utilizzano l’indirizzo P2SH ed evidenziano alcune limitazioni di Bitcoin.


Transazioni non confermate di breve durata: pagamento del caffè #

Alice sta pagando il caffè usando la criptovaluta. Effettua un pagamento ma la conferma della transazione
impiega molto tempo. Decide di pagare in contanti e di andarsene. Tuttavia, è preoccupata che il suo
pagamento originale alla fine verrà confermato e quindi lo perderà o dovrà chiedere un rimborso. In Bitcoin,
può provare a spendere il doppio della transazione, che non è sempre garantita, anche se utilizzatasostituzione
a pagamento. ErgoScript ha una soluzione migliore utilizzandopagamenti a tempoin modo che se la transazione non è confermata prima di una certa altezza, non è più valida. I pagamenti a tempo richiedono che i fondi di Alice siano archiviati in aindirizzo a tempo, che è il P2SH del seguente script: alice && ALTEZZA <= getVar[Int](1).get Quialiceè uncostante denominatache rappresenta la sua chiave pubblica. Eventuali fondi depositati a questo indirizzo possono essere spesi solo se l’operazione di spesa soddisfa quanto segue: 1. La variabile di contesto con l’id 1 della casella spesa deve contenere un numero intero, ad esempioio. 2. L’altezza durante l’estrazione dovrebbe essere inferiore o uguale aio.

Osservalo se la transazione non viene estratta prima dell’altezzaioquindi la transazione diventa non valida. Quando si paga in un bar, ad esempio, Alice può impostareiovicino all’altezzahal momento della trasmissione, ad esempio,io=h+10. Alice può comunque inviare pagamenti fuori orario effettuandoiomolto largo. Poiché le variabili di contesto fanno parte del messaggio nella costruzione della prova a conoscenza zero, un minatore non può cambiarla (per rendere valida questa transazione).

Contratti Hot-Wallet: indirizzi reversibili #

Creiamo un utile primitivo chiamatoindirizzi reversibili, progettato per conservare i fondi in un portafoglio
caldo. Tutti i fondi inviati a un indirizzo reversibile possono essere spesi solo in modo da consentire lo storno dei pagamenti per un certo tempo. L’idea è stata proposta per Bitcoin [5] (usando il monikerIndirizzi R) e richiede un hardfork. In ErgoScript, tuttavia, questo può essere fatto in modo nativo.
Per motivare questa funzione, prendi in considerazione la gestione del portafoglio caldo di un pool
minerario o di uno scambio. I fondi prelevati dai clienti provengono da questo portafoglio caldo. Essendo un hot-wallet, la sua chiave privata è suscettibile di compromessi. Un giorno scopri diverse transazioni non autorizzate dall’hot-wallet, che indicano una violazione. Vorresti che ci fosse un modo per annullare le transazioni e annullare i prelievi, ma purtroppo non è così. In generale non c’è modo di recuperare i fondi persi una volta che la transazione è stata estratta, anche se la violazione è stata scoperta in pochi minuti. Vorremmo che, in caso di tale compromissione, siamo in grado di salvare tutti i fondi archiviati in
questo portafoglio e spostarli a un altro indirizzo, a condizione che la violazione venga scoperta entro un
tempo specificato (ad esempio 24 ore) dal primo ritirare.

Per ottenere ciò, richiediamo che tutte le monete inviate dall’hot-wallet (sia legittime che dall’attaccante) abbiano un periodo di riflessione di 24 ore, durante le quali le scatole create possono essere spese solo da una chiave privata fidata che è stata selezionataprimaè avvenuto il compromesso. Questa chiave attendibile deve essere diversa dalla chiave privata hot-wallet e idealmente dovrebbe trovarsi in una cella frigorifera. Dopo 24 ore, queste scatole diventano “normali” e possono essere spese solo dal destinatario.
Questo viene fatto memorizzando i fondi hot-wallet in un tipo speciale di indirizzo indicato comereversibile.

Supponi chealiceè la chiave pubblica dell’hot-wallet ecarloè la chiave pubblica della parte fidata. Si noti che la parte fidata deve essere decisa al momento della generazione dell’indirizzo e non può essere modificata in seguito. Per utilizzare un’altra parte attendibile, è necessario generare un nuovo indirizzo. Permettere blocchiIn24h essere il numero stimato di blocchi in un periodo di 24 ore. Un indirizzo reversibile è un indirizzo P2SH il cui script codifica le seguenti condizioni:

  1. Questa casella di input può essere spesa solo daalice.
  2. Qualsiasi casella di output creata spendendo questa casella di input deve avere nel suo registroR5almeno
    un numeroblocchiIn24hpiù dell’altezza attuale.
  3. Qualsiasi casella di output creata spendendo questa casella di input deve essere protetta da uno script che richiede
  4. quanto segue:
  5. (a) Il suo registro R4 deve avere una chiave pubblica arbitraria chiamata bob.
  6. (b) Il suo registro R5 deve avere un numero intero arbitrario chiamato bobDeadline.
  7. (c) Può essere speso solo da carlo Se ALTEZZA≤bobDeadline.
  8. (d) Può essere speso solo da bob Se ALTEZZA>bobDeadline

Pertanto, tutti i fondi inviati da tali indirizzi hanno un blocco temporaneoblocchiIn24hblocchi. Questo può essere sostituito da qualsiasi altro valore desiderato ma deve essere deciso al momento della generazione dell’indirizzo. Permetterebobessere la chiave pubblica di un cliente che sta ritirando. Il mittente (alice)deve garantire quel registroR4della scatola creata contienebob.Nello scenario normale,bobsarà in grado di spendere la scatola dopo all’incircablocchiIn24hblocchi (con il numero esatto a secondabobDeadline). Se una transazione non autorizzata daaliceviene rilevato, viene attivata una “procedura di interruzione” tramite cantilena:tutti i fondi inviati daalicee nello stato bloccato sono sospetti e devono essere dirottati altrove.

Per creare un indirizzo reversibile, crea prima uno script,ritirareScript,con il seguente codice: val bob val bobDeadline = SELF.R5[Int].get (bob && ALTEZZA > bobDeadline) || (carol && ALTEZZA <= bobDeadline) = SELF.R4[SigmaProp].get // chiave pubblica del ritiro del cliente // altezza massima di bloccaggio Permetteretassa Propostaessere lo script di una scatola che paga la tassa di mining etassa massimaessere la commissione massima consentita in una transazione.

L’indirizzo reversibile è l’indirizzo P2SH del seguente script: val isChange = {(out:Box) => out.propositionBytes == SELF.propositionBytes} val isWithdraw = {(out:Box) => out.R5[Int].get >= HEIGHT + blocksIn24h && out.propositionBytes == ritiraScript } val isFee = {(out:Box) => out.propositionBytes == feeProposition} val isValid = {(out:Box) => isChange(out) || isWithdraw(out) || isFee(out)} val totalFee = OUTPUTS.fold(0L, { (x:Long, b:Box) => if (isFee(b)) x + b.value else x } ) alice && OUTPUTS.forall(isValid) && totalFee <= maxFee

Contratti Cold-Wallet: limitazione della capacità di spesa #

Supponiamo che un indirizzo sia protetto da 2 chiavi private, corrispondenti alle chiavi pubblichealiceebob. Per
sicurezza, vogliamo che le seguenti condizioni siano valide:

  1. Una chiave può spendere al massimo l’1% o 100 Ergs (a seconda di quale sia il valore più alto) in un giorno.
  2. Se entrambe le chiavi stanno spendendo, non ci sono restrizioni.

Permettere blocchi In 24h essere il numero di blocchi in 24 ore. Invece di cablare 1% e 100 Ergs, useremo le costanti denominateper centoeminSpendrispettivamente. L’indirizzo cold-wallet è l’indirizzo P2SH del seguente script: val storedStartHeight = SELF.R4[Int].get // blocco in cui è iniziato il periodo val creationHeight = SELF.creationInfo._1 // altezza di creazione val startHeight = min(creationHeight, storedStartHeight) val notExpired = HEIGHT – startHeight <= blocksIn24h // scaduto se sono trascorse 24 ore val min = SELF.R5[Long].get // min Saldo necessario in questo periodo val ours:Long = SELF.value – SELF.value * percentuale / 100 val keep = if (Ours > minSpend) ours else 0L // la ricarica dovrebbe mantenere min >= keep val nStart:Int = if (notExpired) start else HEIGHT val nMin:Long = if (notExpired) min else keep val out = USCITE(0) val valido = INPUTS.size == 1 && out.propositionBytes == SELF.propositionBytes && out.value >= nMin && out.R4[Int].get >= nStart && out.R5[Long].get == nMin}) (alice && bob) || ((alice || bob) && min >= mantieni && (nMin == 0 || valido)) La spesa da questo indirizzo viene effettuata in periodi di 24 ore o più in modo tale che il massimo spendibile sia una frazione fissa dell’importo all’inizio del periodo.

Lo facciamo richiedendo che la transazione di spesa abbia un output con un valore maggiore del minimo (che è memorizzato inR5) e rimborsare allo stesso indirizzo. L’inizio del periodo corrente è memorizzato inR4. Entrambi i registri vengono copiati nella nuova uscita entro lo stesso periodo e ottengono nuovi valori se il periodo corrente è scaduto.

Protocolli bipartitici #

Ci concentriamo su protocolli a due turni ea due parti. Al primo turno, la prima parte, Alice, avvia il
protocollo creando una scatola protetta da uno script che codifica le regole del protocollo. Nel secondo
round, la seconda parte, Bob, completa il protocollo spendendo la scatola di Alice di solito con una delle
sue e creando scatole aggiuntive che codificano lo stato finale del protocollo.
Tutti i protocolli qui consentono che il primo round sia offchain, nel senso che la creazione della scatola di Alice potrebbe essere posticipata fino al momento in cui Bob partecipa effettivamente al protocollo. Alice invece invia la sua transazione di creazione della scatola a Bob, che poi pubblicherà entrambe le transazioni in un secondo momento.

Il Gioco XOR #

Descriviamo un semplice gioco chiamato “Same or Different” o il gioco XOR. Alice e Bob inviano entrambi una moneta ciascuno e selezionano un po’ indipendentemente. Se i bit sono gli stessi, Alice riceve entrambe le monete, altrimenti Bob riceve entrambe le monete. Il gioco è composto da 3 passaggi.

  1. Alice si impegna in un bit segretouncome segue. Seleziona una stringa di bit casualeSe calcola il suo impegnoK=H(S‖un) (ovvero, hash dopo la concatenazione S insieme a un). Crea una scatola non spesa chiamata iluscita a metà partitacontenente la sua moneta e il suo impegno K. Questa casella è protetta da uno script chiamato thesceneggiatura di metà giocoindicato di seguito. Alice attende che un altro giocatore si unisca al suo gioco, che lo farà spendendo la sua produzione di metà partita e creando un’altra scatola che soddisfi le condizioni fornite nella sceneggiatura di metà partita.
  2. Bob si unisce al gioco di Alice selezionando un bit a casobe spendendo l’output di metà gioco di Alice per creare una nuova scatola chiamata theuscita del gioco completo. Questa nuova scatola contiene due monete e contieneb (in chiaro) insieme alla chiave pubblica di Bob nei registri. Nota che l’output del gioco completo deve soddisfare le condizioni fornite dallo script di metà gioco. In particolare, una delle condizioni richiede che l’output del gioco completo debba essere protetto dalsceneggiatura del gioco completo(indicato di seguito).
  3. Alice si apreKoffchain rivelandos, ae vince seun=b. Il vincitore spende l’output del gioco completo usando la sua chiave privata e fornendoSeuncome input per la sceneggiatura del gioco completo. Se Alice non si apreKentro una scadenza specificata, Bob vince automaticamente.

Lo script del gioco completo codifica le seguenti condizioni: I registriR4,R5eR6dovrebbero memorizzare il bit di Bobb, la chiave pubblica di Bob (memorizzata come aproveDlogproposta) e rispettivamente la scadenza per la vittoria automatica di Bob. Le variabili di contesto con id 0 e 1 (fornite al momento di spendere la scatola del gioco completo) contengonoSeunnecessario per aprire il commit di AliceK, che insieme alla chiave pubblica di Alicealice viene utilizzato per calcolare full Game ScriptHash,l’hash dello script seguente:
val s
val a
val b
val bob
val bobDeadline = SELF.R6[Int].get (bob
&& ALTEZZA > bobDeadline) ||
(blake2b256(s ++ Coll(a)) == k && (alice && a == b || bob && a != b))
= getVarColl[Byte].get // stringa di bit s =
getVarByte.get
= SELF.R4[Byte].get
= SELF.R5[SigmaProp].get
chiave pubblica di Bob
Le costanti di cui sopra vengono utilizzate per crearemezzo GameScriptcon il seguente codice:
val fuori
val b
val bobDeadline
val ValidBobInput = b == 0 || b == 1
validBobInput && blake2b256(out.propositionBytes) == fullGameScriptHash &&
OUTPUTS.size == 1 && bobDeadline >= HEIGHT+30 && out.value >= SELF.value * 2
= USCITE(0)
= out.R4[Byte].get
= out.R6[Int].get
Alice crea la sua scatola di metà gioco protetta dametà GameScript,che richiede che la transazione che
spende l’half-game box debba generare esattamente una casella di output con le seguenti proprietà:

  1. Il suo valore deve essere almeno il doppio di quello della scatola di metà gioco.
  2. Il suo registro R4 deve contenere un byte che sia 0 o 1. Questo codifica la scelta di Bobb.
  3. Il suo registro R6 deve contenere un numero intero che sia almeno 30 in più rispetto all’altezza alla quale viene generata la scatola. Questo corrisponderà all’altezza alla quale Bob vince automaticamente.
  4. Deve essere protetto da uno script il cui hash è ugualefullGameScriptHash.

Il gioco garantisce sicurezza ed equità come segue. Poiché la scelta di Alice è nascosta a Bob quando crea
l’output del gioco completo, non ha alcun vantaggio nella selezioneb. In secondo luogo, Alice ha la garanzia di perdere se si impegna su un valore diverso da 0 o 1 perché può vincere solo seun=b. Pertanto, la strategia razionale per Alice è quella di impegnarsi per un valore corretto. Infine, se Alice si rifiuta di accettare il suo impegno, Bob vincerà sicuramente dopo la scadenza del termine.

Gioco Sasso-Carta-Forbici #

Rispetto a Rock-Paper-Scissors (RPS), il gioco XOR è più semplice (ed efficiente) perché non c’è
condizione di pareggio e per questo nella pratica dovrebbe essere preferito. Tuttavia, è utile
considerare il gioco RPS come un esempio di protocolli più complessi.
Permetterea, b∈Z3essere le scelte di Alice e Bob, con la consapevolezza che 0, 1 e 2 rappresentano
rispettivamente sasso, carta e forbici. Seun=ballora il gioco è pareggio, altrimenti vince Alice se a-b∈ {1,-2}altrimenti vince Bob. Il gioco è simile a XOR, tranne per il fatto che Bob genera due output per gestire il caso di sorteggio (in cui ogni giocatore ottiene un output). L’impegno di AliceK=H(a||s) e chiave pubblica aliceviene utilizzato nella generazionefullGameScriptHash,l’hash del seguente script:
val s = getVarColl[Byte].get val a =
getVarByte.get val b =
SELF.R4[Byte].get
val bob = SELF.R5[SigmaProp].get
val bobDeadline = SELF.R6[Int].get
drawPubKey = SELF.R7[SigmaProp].get
val valid_a = (a == 0 || a == 1 || a == 2) && blake2b256(s ++ Coll(a)) == k
(bob && ALTEZZA > bobDeadline) || {valid_a &&
if (a == b) drawPubKey else {if ((a – b) == 1 || (a – b) == -2) alice else bob}}
Per avviare il gioco, Alice crea una scatola protetta dallo script riportato di seguito:
OUTPUTS.forall{(out:Box) =>
val b
val bobDeadline
bobDeadline >= ALTEZZA+30 && out.value >= SELF.value && (b
== 0 || b == 1 || b == 2) &&
blake2b256(out.propositionBytes) == fullGameScriptHash
} && OUTPUTS.size == 2 && OUTPUTS(0).R7[SigmaProp].get == alice
= out.R4[Byte].get
= out.R6[Int].get
Il codice di cui sopra garantisce tale registrazioneR7del primo output contiene la chiave pubblica di Alice
(per lo scenario di disegno). Bob deve assicurarsi cheR7del secondo output contiene la sua chiave pubblica.
Inoltre, deve assicurarloR5di entrambe le uscite contiene la sua chiave pubblica.

ErgoMix: CoinJoin non interattivo #

Le tecniche di miglioramento della privacy nelle blockchain generalmente rientrano in due
categorie. Il primo è nascondere gli importi trasferiti, come in Transazioni riservate [6]. Il secondo
sta oscurando le relazioni input-output come in ZeroCoin [7] e CoinJoin [8]. Alcune soluzioni come

MimbleWimble [9] e Z-Cash [10, 11] combinano entrambi gli approcci. Descriviamo ErgoMix, un altro
protocollo di miglioramento della privacy basato su quest’ultimo approccio. Il protocollo è motivato da
ZeroCoin e CoinJoin per superare alcuni dei loro limiti.
ErgoMix utilizza un pool diMezzo mixscatole, che sono scatole pronte per la miscelazione. Questo è chiamato il Piscina H. Per mescolare una scatola arbitrariaB, viene eseguita una delle seguenti operazioni:
1.Piscina:Aggiungi casellaBall’H-pool e attendi che qualcuno lo usi in una fase di mix.
2.Mescolare:Scegli una scatola qualsiasiUNdall’H-pool e un po’ segretob. TrascorrereA,Bper generarne due Completamente mistoscatoleo0,O1taleobeo1-bsono spendibili daUN’sabbiaBrispettivamente i proprietari. La privacy deriva dal fatto che le scatoleobeo1-bsono indistinguibili, quindi un estraneo non può indovinareb con probabilità migliore di 1/2. Quindi, la probabilità di indovinare la scatola originale dopo nmix è 1/2n. Una scatola viene mescolata più volte per raggiungere la privacy desiderata. La figura 1b spiega il protocollo. ErgoMix usa una primitiva chiamata aTupla di prova di Diffie-Hellman, spiegato di seguito. Permettereg, h, u, v essere elementi di un gruppo pubblico. Il prover dimostra la conoscenza diXtale chetu=gXev=hX.

  1. Il prover sceglier←RZq, calcola (t0, t1) = (gr, hr) e invia (t0, t1) al verificatore.
  2. Il verificatore scegliec←RZqe inviacper provare.
  3. Il prover inviaz=r+cxal verificatore, che accetta segz=t0·tucehz=t1·vc.
    Usiamo la variante non interattiva, dovec=H(t0‖t1‖m). Lo chiamiamodimostrareDHTuple(g, h, u, v).

Il Protocollo di Base #

Senza perdere la generalità, Alice si unirà e Bob si mescolerà. Permetteregessere il generatore diproveDlog.
1.Piscina:Per aggiungere una moneta all’H-pool, Alice sceglie a casoX∈Zqe crea una casella di outputUN
contenentetu=gXprotetto dallo script riportato di seguito. Aspetta che Bob si unisca, che lo farà
spendendoUNin una transazione che soddisfi le seguenti condizioni:
(a) Ha due usciteo0,O1contenente coppie (w0, w1), (w1, w0) rispettivamente perw0, w1∈G.
(b) Uno di (g, u, w0, w1),(g, u, w1, w0) è della forma (g, gX, gy, gxy), una tupla di Diffie-Hellman valida.
Questo è codificato comedimostrareDHTuple(g, u, w0, w1)∨dimostrareDHTuple(g, u, w1, w0).
(c) Il valore dio0,O1è uguale a quello diUN.
(d) Entrambio0,O1dovrebbe essere protetto dallo scriptτUN∨ τBindicato nel passaggio Mix di seguito.
2.Mescolare:Bob sceglie i segreti (di)∈Z2×Zqe spendeUNcon una propria casella per creare due
caselle di outputo0,O1di uguale valore tale cheobè spendibile solo da Alice eo1-bda solo Bob. Le
scatole sono indistinguibili nel senso che hanno script identici che operano su registri di dati
CDcontenente elementi diversi (ma correlati) daGcome spiegato di seguito.
(a) Registri (CD) diobeo1-bsono impostati su (gy, tuy) e (tuy, gy) rispettivamente.
(b) Ogni casella è protetta dalla propostaτUN∨ τB, doveτUNeτBsono come segue: τUN= “Dimostrare
la conoscenza diXtale chetu=gXed=cXattraversodimostrareDHTuple(g, c, u, d).” τB= “
Dimostrare la conoscenza diytale ched=gyattraversoproveDlog(d).”

Dopo il mix, Alice e Bob possono spendere le rispettive scatole usando i loro segreti. Alice può identificare la sua scatola come quella cond=cX. Codice ErgoScript:Primo calcolofullMixScriptHash,l’hash del seguente script:
val u = SELF.R4[GroupElement].get // copiato dalla transazione precedente val c
= SELF.R5[GroupElement].get
val d = SELF.R6[GroupElement].get
proveDlog(d) || dimostrareDHTuple(g, c, u, d)
Quindi crea uno script,mezzo MixScript,avente il seguente codice:
val u = SELF.R4[GroupElement].get val u0 =
OUTPUT(0).R4[GroupElement].get val c0 =
OUTPUT(0).R5[GroupElement].get // w0 val d0 =
OUTPUT(0). R6[GroupElement].get // w1 val u1 =
OUTPUT(1).R4[GroupElement].get val c1 =
OUTPUT(1).R5[GroupElement].get // w1 val d1 =
OUTPUT(1).R6[ GroupElement].get // w0
val bob = u0 == u && u1 == u && c0 == d1 && c1 == d0 &&
(proveDHTuple(g, u, c0, d0) || proveDHTuple(g, u, d0, c0))
val alice = proveDlog(u) // così Alice può spendere se nessuno si unisce per molto tempo val
fullMixBox = {(b:Box) => blake2b256(b.propositionBytes) == fullMixScriptHash} val fullMixTx =
OUTPUT(0). valore == SELF.value && OUTPUT(1).value == SELF.value &&
fullMixBox(OUTPUT(0)) && fullMixBox(OUTPUT(1))
fullMixTx && (bob || alice)
La scatola Half-Mix di Alice è protetta damezzo MixScriptindicato sopra, che Bob può spendere usando la condizionebob. Nel caso in cui nessuno trascorra la sua scatola per molto tempo, può farlo lei stessa usando la condizionealice,fintanto che lo spende in una transazione mista.

Analisi del Protocollo #

Sicurezza:Osservare che registri (CD) diobeo1-bcontenere (gy, gxy) e (gxy, gy) rispettivamente, il che implica cheobla condizione di spesa si riduce aproveDlog(gxy)∨dimostrareDHTuple(g, gy, gX, gxy) e o1-bsi riduce a proveDlog(gy)∨dimostrareDHTuple(g, gxy, gX, gy). Così, mentre Alice può spendereob
usandodimostrareDHTuplecon il suo segretoX, non può soddisfare la condizione di spesa dio1-b. Allo stesso modo, Bob può solo spendereo1-busandoproveDlogcon il suo segretoy. Bob deve generareo0,O1in questo modo perché deve dimostrare che una delle uscite contiene una tupla DH valida.
Per la privacy, osservare che due caselle identiche qualsiasi protette damezzo MixScriptavereindistinguibilità di chi spendeperché ognuno viene speso usando una dimostrazione Σ-OR che è a conoscenza zero [1]. Si può dimostrare che qualsiasi algoritmo che, dato (g, gX), distingue (gy, gxy) da (gxy, gy) può essere utilizzato per risolvere il Decisione Diffie Hellman(DDH) problema. Ne consegue che anche le nostre scatole, che sono di questa forma, sono indistinguibili se il problema DDH è difficile.

Figura 1: confronto tra CoinJoin ed ErgoMix

Confronto con CoinJoin:CoinJoin [8] è un protocollo di miglioramento della privacy, in cui più parti
forniscono input e creano output in un’unica transazione calcolata in modo interattivo in modo tale che
gli input e gli output originali non siano collegati. L’uso ottimale di CoinJoin è quando due input di uguale valore vengono uniti per generare due output di uguale valore e il processo viene ripetuto, come
illustrato nella Figura 1a. Ciò richiede che due parti firmino in modo interattivo una transazione offchain
e questa natura interattiva è lo svantaggio principale di CoinJoin, che ErgoMix mira a superare.

In ErgoMix, questa interazione è sostituita dalla partecipazione pubblica utilizzando la blockchain. Sebbene ciò aggiunga un’altra transazione, non richiede l’interazione tra le parti. Nota che le transazioni ErgoMix sono rilevabili, mentre le transazioni CoinJoin sono indistinguibili dalle transazioni ordinarie.
Confronto con ZeroCoin:ZeroCoin è un protocollo di miglioramento della privacy che utilizza un pool di missaggio. Una moneta ordinaria viene aggiunta al pool come impegnocad alcuni segreti (r, s), e viene successivamente speso in modo tale che il collegamento acnon è pubblicamente visibile. Il valorecdeve essere memorizzato permanentemente nel pool, poiché l’operazione di spesa non può rivelarlo. Invece, rivela il segretoS(ilnumero di serie) insieme a una prova a conoscenza zero cheSè stato utilizzato in un impegno dalla piscina. Il numero di serie viene memorizzato in modo permanente per evitare doppie spese.

Una conseguenza di ciò è che sia il pool (l’insieme degli impegni) che l’insieme dei numeri seriali spesi devono essere mantenuti in memoria per la verifica di ogni transazione. Un’altra conseguenza è che le dimensioni di questi due insiemi aumentano in modo monotono. Questo è lo svantaggio principale di ZeroCoin (anche ZCash [10]), che ErgoMix cerca di affrontare. In ErgoMix, una volta esaurita una scatola, nessuna informazione su di essa viene conservata in memoria e in particolare non vengono mantenuti set di dati di dimensioni monotonamente crescenti.


Piscina fuori catena:L’H-Pool può essere mantenuto completamente fuori catena, in modo che la scatola Half-Mix di Alice non debba essere presente sulla blockchain fino al momento in cui Bob decide di spenderlo. Alice invia la sua transazione non trasmessa direttamente a Bob che trasmetterà entrambe le transazioni in un secondo momento.

Miglioramenti futuri:Rispetto a CoinJoin, ErgoMix richiede un box aggiuntivo (il box Half-Mix) come illustrato nella Figura 1. Sarà meglio avere una variante che elimini questo box. Un modo per farlo sarebbe trovare un modo in modo che la fase di missaggio produca direttamente due box Half-Mix indistinguibili che possono essere utilizzati nella fase di missaggio o spesa esternamente.

Commissione di gestione in ErgoMix #

Simile a ZeroCoin e alla variante canonica di CoinJoin nella Figura 1a, ogni moneta in ErgoMix deve avere un valore fisso, che viene riportato alla fase successiva. Questo va bene in teoria, ma implica transazioni a zero commissioni, cosa che non è possibile in pratica. Di seguito discutiamo alcuni approcci per la gestione delle commissioni.
Supponiamo che la tassa sia stata pagatagettoni di miscelazione, che sono token1emesso da una terza parte e che la creazione di un output misto consuma uno di questi token. Una transazione mix (che ha due di questi output) consuma esattamente due token di mixaggio e, per mantenere la privacy, il saldo deve essere equamente distribuito tra i due output.
Di seguito sono riportate alcune strategie per garantire l’equità nel pagamento delle commissioni.
1. Perfetta correttezza:La scatola Half-Mix di Alice contieneiomescolando i gettoni e richiede che ogni casella di output contengaio -1 gettoni miscelazione. Così, Alice può mescolare la sua monetaiovolte.
Questa strategia tariffaria ottimale, tuttavia, presenta due inconvenienti. In primo luogo, ha indebolito la
privacy perché limita le monete che possono essere mescolate. In secondo luogo, influisce sull’usabilità perché potrebbero non esserci scatole con il numero di gettoni desiderato. La strategia di equità approssimativa, discussa di seguito, ha una migliore privacy e usabilità a scapito di una minore equità.


2. Equità approssimativa:Alice rilassa la sua condizione richiedendo che Bob contribuisca con almeno un
gettone nel mix. Tuttavia, richiede anche a Bob di averloinizialmenteiniziato con esattamente 1000 token
nel suo primo mix. Quindi, se Bob sta contribuendo, diciamo, 1 token nel mix corrente, allora Alice vuole
assicurarsi che sia effettivamente arrivato lì “nel modo più difficile”, iniziando con 1000 token e
perdendoli in mix sequenziali. Questo può essere fatto come segue:


In primo luogo, l’emittente di token deve limitare l’ingresso di token emettendoli solo in lotti di 1000 in
una casella protetta dallo script sottostante, che richiede che i token possano essere trasferiti (nel loro
insieme) solo se la transazione è una transazione mista o crea una scatola Half-Mix:

val halfBox = {(b:Box) => blake2b256(b.propositionBytes) == halfMixScriptHash} val
sameTokenHalfBox = {(b:Box) => halfBox(b) && b.tokens(0) == SELF.tokens(0 )} carol &&
(halfBox(INPUTS(0) || sameTokenHalfBox(OUTPUTS(0))) // carol è l’acquirente
Il valorehalfMixScriptHashè un hash dimezzo MixScript,che ha il seguente codice
aggiuntivo:out.R7[Coll[Byte]].get == blake2b256(SELF.propositionBytes),garantendo cosìR
7di ogni output contiene il suo hash. Il codice difullMixScriptè modificato:
val halfMixScriptHash = SELF.R7[Coll[Byte]].get
val halfBox = {(b:Box) => blake2b256(b.propositionBytes) == halfMixScriptHash} val
sameTokenHalfBox = {(b:Box) => halfBox(b) && b.tokens(0) == SELF.tokens(0 )} val noToken =
{(token:(Coll[Byte], Long)) => token._1 != SELF.tokens(0)._1}.

Ogni transazione può generare qualsiasi quantità di al massimo un token, il cui ID è il box-ID del primo input. Per altri token-ID, la somma delle quantità negli output deve essere inferiore o uguale alla somma delle quantità negli input.

val noTokenBox = {(b:Box) => b.tokens.forall(noToken)} val
noTokenTx = OUTPUTS.forall(noTokenBox)
(halfBox(INPUTS(0)) || sameTokenHalfBox(OUTPUTS(0)) || noTokenTx) && …

3.Il primo pagatore paga la commissione:Un altro miglioramento, principalmente in perfetta equità, è quello di avvantaggiare la
parte che è disposta ad aspettare più a lungo. Richiediamo quindi che la commissione per la transazione mix sia pagata dalla prima parte che spende un output. Possiamo identificare il primo spender come segue.


Una transazione mix deve generare esattamente 4 quantità di un token (con alcuni idX) distribuito
equamente tra 4 uscite. Due di queste sono le uscite mix standardo0,O1con la condizione di spesa
aggiuntiva che un output deve contenere una quantità di token diversa da zeroX. Le altre due
scatole,o2,O3, hanno le seguenti identiche condizioni di spesa:
(a) La somma delle quantità di gettoneXnegli ingressi e nelle uscite è rispettivamente 3 e 2.
(b) Un output contiene 2 quantità di tokenXprotetto dallo stesso script di questa casella.
Quindi è il secondo spender se e solo se c’è un input con due quantità di tokenX. La fase
di mix creerà una scatola aggiuntiva con due gettoni spendibili dal secondo spender.

Conclusione #

Questo articolo descrive i contratti intelligenti scritti in ErgoScript. Gli esempi si basano sui concetti
del white-paper ErgoScript [3]. I contratti più avanzati saranno discussi in un altro tutorial.

Riferimenti #

[1] Ivan Damgård. Sui protocolli Σ, 2010.http://www.cs.au.dk/~ivan/Sigma.pdf.
[2] Claus-Peter Schnorr. Generazione efficiente di firme tramite smart card.Giornale di crittografia,
4(3):161–174, 1991.
[3] Ergoscript, un linguaggio di scripting per criptovalute che supporta prove a conoscenza zero non
interattive.https://docs.ergoplatform.com/ErgoScript.pdf,03 2019.
[4] Fondazione Scorex. Interprete Sigmastato.https://github.com/ScorexFoundation/sigmastate-interpreter,2017.
[5] Aggiunta della funzione antifurto a bitcoin utilizzando un nuovo tipo di indirizzo.https://bitcointalk.org/index.php?topic=4440801.0,06 2018.
[6] Gregory Maxwell.valori_riservati.txt,2015. Transazioni riservate. https://people.xiph.org/~greg/
[7] Ian Miers, Christina Garman, Matthew Green e AD Rubin. Zerocoin: denaro elettronico distribuito da
anonimo da bitcoin. pagine 397–411, 05 2013.
[8] Coinjoin: privacy Bitcoin per il mondo reale.https://bitcointalk.org/?topic=279249,08 2013.

Cosa nè pensi?