
DeFi whitepaper, 13 augustus 2020
Eén van de duidelijke uitdagingen waarvoor DLT-platforms vandaag de dag staan is schaalbaarheid. De snelle uitbreiding van DeFi-apps op Ethereum heeft het platform naar zijn grenzen gebracht. Terwijl Ethereum zijn 2.0 upgrade najaagt om de knelpunten te helpen verlichten tegen 2022 zijn andere technologieën in beeld gekomen die nieuwe technieken voorstellen om een grotere doorvoer van transacties te bereiken.
Het posten van hoge doorvoeraantallen alleen echter faalt om de volledige schaalbaarheidsvereisten van DeFi te omvatten. Als een DeFi DLT-platform een functioneel en levensvatbaar alternatief wil bieden voor een wereldwijd netwerk van traditionele financiële systemen dan moet dit in staat zijn om een enorm aantal van DeFi-apps tegelijk te ondersteunen terwijl iedere app met een hoge doorvoer wordt gedraaid, en dit zonder in te leveren op decentralisatie. Maar zelfs dit is niet genoeg.
Waarschijnlijk de belangrijkste eigenschap van DeFi is de interoperationaliteit van apps en activa, vaak compositionaliteit genoemd. De mogelijkheid om een enkele transactie samen te stellen met gebruik van meerdere autonome smart contracts vergt veel van de DeFi-innovatie en de opwinding op Ethereum vandaag de dag. Met de mogelijkheid om vrij over iedere set van DeFi-apps te compositioneren wordt het mogelijk om een service te bieden die bijvoorbeeld onmiddellijk de beste beursprijs biedt voor een verhandeling over meerdere geautomatiseerde marktmakers, of het gebruik mogelijk maakt van een publiek geworven liquiditeitspool om direct voordeel te halen uit een arbitrage-mogelijkheid. Het is cruciaal dat deze complexe operaties over apps allemaal gebeuren in een enkele atomische stap, wat wil zeggen dat ofwel de gehele transactie over alle apps valide en tegelijk beslist is, ofwel dat de gehele transactie veilig faalt. Dit is ongelofelijk krachtig en is de basis voor hoe DeFi de inefficiënties van traditionele financiële systemen oplost, deze vervangend door snelle, aanpasbare en interoperationele DeFi financiële apps.
Ondanks het cruciale belang van compositionaliteit willen de meeste DLT-oplossingen schaalbaarheid doen toenemen door compositionaliteit significant te reduceren. Typische benaderingen zijn het schalen van aparte apps en transacties over shards, waar ze sneller kunnen draaien maar geen directe, atomische toegang tot elkander hebben. Hier betekent meer sharding mnider interoperationaliteit, schaalbaarheid en compositionaliteit in een direct conflict met elkaar brengend. Hoewel dit een acceptabele deal kan zijn voor eenvoudige tokenoverschrijvingen of applicaties die niet vrij en atomisch compositioneel hoeven te zijn maakt dit ware DeFi op schaal essentieel onmogelijk.
Radix neemt een andere benadering, zijn volledige technologiestapel ontwerpend voor onbeperkte schaalbaarheid zonder in te leveren op veiligheid, decentralisatie of compositionaliteit.
Typische sharding voor schaalbaarheid
Typische schaalbaarheidsoplossingen betrekken een vorm van sharding. Of sharding nu wordt geïmplementeerd met gebruik van een hub-and-sidechain-architectuur (zoals Cosmos of Polkadot) of door het breken van een block in stukjes voor onafhankelijke verwerking door verschillende nodes (zoals Near), het idee is hetzelfde: verschillende apps en transacties worden toegewezen aan een aantal aparte shards waar ze parallel doorheen de consensus kunnen worden gedraaid.
Dit parallellisme bereikt een grotere doorvoer maar het compromis is dat communicatie tussen shards moeilijk wordt gemaakt. Verschillende shards kunnen geizen worden als aparte blockchains (soms is dat zelfs letterlijk wat ze zijn), maar waar er een methode is om berichten daartussen te versturen. Maar als iedere shard onafhankelijk consensus uitvoert is het onmogelijk om een transactie over meerdere shards atomisch te verwerken. Op de een of andere manier moet shard-overschrijdende coördinatie gedaan worden over meerdere blocks op de verschillende shards, wat vaak kwitanties omvat, of andere manieren van het bieden van conditionele cryptografische toewijdingen tussen onafhankelijke consensusprocessen. Dit maakt deze transacties traag, foutgevoelig en moeilijk te implementeren in smart contract-code. Wat het nog erger maakt is dat het toewijzen van apps en activa aan bepaalde shards gewoonlijk statisch is (zoals Ethereum 2.0), of een significante netwerk-overhed vereist om aan te passen.
We realiseerden ons vroeg dat Radix moest beginnen bij de eerste principe’s om deze spanning tussen schaalbaarheid en compositionaliteit op te lossen. Ten eerstemoesten we in plaats van het gebruiken van een statische set shards een bijna onbeperkt aantal shards ondersteunen om zoveel mogelijk parallellisme te bereiken voor een DeFi-platform op wereldwijde schaal. Ten tweede hadden we een consensusprotocol nodig dat in staat was om dynamisch consensus uit te voeren op atomische transacties (inclusief contract-operaties), synchroon over enkel de relevante shards zonder de rest van het netwerk stil te leggen. En ten derde hadden we een applicatielaag nodig die ins taat was om efficiënt gebruik te maken van deze onbeperkte “shard-ruimte” en multi-shard-consensus.
Cerberus-consensus
Een kernstuk van de Radix-oplossing is ons unieke consensusalgoritme, Cerberus. Cerberus is ontworpen rond een concept dat we pre-sharding noemen, waarbij in plaats van te proberen om sharding aan een monlitische ledger toe te voegen we starten met het splitsen van de ledger in een shard-ruimte van een aantal shards die zo groot is dat die bijna onbeperkt is[5]. We kunnen deze shards gebruiken om alles te representeren wat we willen, en Cerberus kan een veilige consensus vlechten over een arbitrair aantal benodigde shards. Ons Cerberus whitepaper bespreekt dit algoritme in detail. Maar kort gezegd combineert Cerberus die kerninzichten:
Ten eerste bewegen we van het typische concept van globale volgorde naar dat van bijzondere volgorde. Bijna alle DLT’s gebruiken globale volgorde waarin alle transacties op dezelfde tijdlijn moeten worden geplaatst. Sommige vormen van sharding creëren in principe meerdere globaal geordende tijdlijnen maar behouden een vaste globale volgorde binnen elk daarvan. Cerberus brengt dit concept zelfs verder met de aanname dat elke transactie precies kan specificeren welke shards relevant zijn (en dus geordend moeten worden) voor een specifieke transactie. Dit vereist een gespecialiseerde applicatielaag die kan specificeren hoe shards worden gebruikt en hoe ze gerelateerd zijn aan elkaar, wat we later zullen bespreken.

Ten tweede; nu dat we weten welke shards moeten worden ingesloten voor een transactie ontwerpen we een neiuwe vorm van BFT-stijl-consensus, genaamd braiding [vlechten]. Een typische BFT-stijl-consensus gebruikt 2 of 3 fasen van getekende toewijdingen tussen nodes om vertrouwd een transactie te finaliseren. Cerberus’ braided consensus draait een enkele 3-fase-BFT-instance (genaamd een 3-chain) binnen elke shard, maar vlecht deze instances samen met toewijdingen aangeboden door de leiders van de andere gerelateerde shards. Het resultaat is een oprijzende 3-braid-consensus die er voor zorgt dat alle relevante shards atomisch kunnen committeren aan de multi-shard-transactie.

Ten derde ontwerpen we het protocol zo dat dynamische 3-braid-consensus-rpocessen parallel kunnen draaien. Iedere shard met zijn lokale BFT-instance kan volledig onafhankelijk lopen, net als iedere oprijzende multi-shard-instance (zoals nodig voor een bepaalde transactie) die op dat moment tot geen andere is gerelateerd.

We combineren deze inzichten om Cerberus te creëren, een nieuw consensusalgoritme dat specifiek is ontworpen voor grote, diverse netwerken van gelijklopende applicaties en transacties. Cerberus biedt lineaire schaalbaarheid via parallellisme; er kan meer netwerkvraag worden bediend door steeds meer aantalen van economisch geprikkelde node-draaiers. Bij het groeien van het Cerberus-gebaseerde netwerk wordt nooit ingeleverd op compositionaliteit omdat directe consensus tussen shards naadloos gebeurt als reactie op iedere transactie.
Radix Engine applicatielaag
De applicatielaag van een DLT biedt de interface tussen de smart contract-code van een ontwikkelaar en de onderliggende ledger, en dus vereist Cerberus een speciale applicatielaag. Voor Radix is dit de Radix Engine, en het belangrijke deel voor schaalbaarheid is de onderste laag van de Engine: Radix VM.
Radix VM biedt de partiële volgorde die Cerberus vereist om consensus te vlechten op basis van individuele transacties — inclusief transacties die worden aangedreven door logica van een component-actie. Dit doet deze door transacties te definiëren als een gerelateerde set van veranderingen aan de staat die over shards zijn gecodeerd. Bijvoorbeeld het eigenaarschap van twee verschillende tokens (twee stukjes staat) kan gelokaliseerd zijn op twee verschillende shards, en dus moet een atomische transactie die het eigenaarschap van beide tokens overschrijft correct partieel die transactie bevelen, afhankelijk van andere gelijktijdige transacties op die twee specifieke shards. Dat wil zeggen dat we er zeker van moeten zijn dat verandering van eigenaar van elk token niet conflicteert met andere verzoeken om het eigenaarschap van die tokens te veranderen. Alle andere verzoeken aan het netwerk doen er op dat moment niet
Radix VM moet ook een ander substantieel probleem voor de DeFi-schaalbaarheid oplossen: gelijktijdigheid. Zelfs wanneer Cerberus ons de uitstekende mogelijkheid biedt om transacties van ongerelateerde dingen te parallelliseren zijn veel DeFi- (en andere -) applicaties bedoeld om veel gelijktijdige transacties uit te voeren voor sterk gerelateerde dingen. DeFi-pools van tokenreserves (zoals die welke Uniswap aandrijven) zijn hiervan een uitstekend voorbeeld, waar iedere transactie wordt berekend met gebruik van de staat van de tokenreserves die zelf veranderd worden door iedere transactie. Veel verzoeken die concurreren om de pool te gebruiken kunnen eenvoudigweg niet geparallelliseerd worden.
Dit laat zien dat er twee fundamenteel verschillende manieren zijn van het bereiken van een hoge doorvoer van transacties op ons netwerk — één waar parallellisatie mogelijk is (veel ongerelateerde transacties) en één waar serieschakeling nodig is (veel sterk gerelateerde transacties). Deze verschillende manieren zijn goed bestudeerd en we hebben twee verschillende instrumenten nodig om ze op te lossen: optimistische verzoeken en pessimistische verzoeken.
Optimistische verzoeken specificeren vooraf de volledige details van een staat, gebaseerd op wat geloofd wordt dat de huidige staat is. Zolang er niets conflicterend verandert aan de huidige staat tegen de tijd dat het netwerk de transactie verwerkt gaat de transactie door. Maar er is altijd de kans dat ons optimisme wordt gestraft en onze transactie verworpen, indien een van onze assumpties niet langer valide is, ons zo forcerend om het opnieuw te proberen. Voor de eenvoudigste transacties op een DLT-netwerk is dit erg onwaarschijnlijk, en Cerberus kan op natuurlijke wijze extreem effectief parallelliseren, maar voor zoiets als een populaire DeFi-pool zullen transacties constant conflicteren, falen en opnieuw geprobeerd moeten worden.
Pessimistische verzoeken betekent dat een transactie een benodigd deel van de huidige staat vooraf vastzet zodat we erop kunnen vertrouwen dat ieder goed geformuleerd verzoek niet zal falen vanwege een onvoorziene verandering van staat. Dit betekent dat verzoeken die een bepaald stuk van de staat updaten volledig worden seriegeschakeld. Wanneer veel verzoeken dezelfde staat willen updaten (zoals bij een DeFi-pool) is het groeperen van al die staatdelen en serieschakeling toepassen de optimale oplossing.
Dus wat we echt zouden willen is een hybride oplossing: Eenvoudige transacties zouden optimistische verzoeken moeten gebruiken en zoveel mogelijk geparallelliseerd moeten worden over vele Cerberus-shards om doorvoer te maximaliseren. Complexe transacties die veel gerelateerde staat omvatten (zoals een DeFi-pool met hoog volume) zouden pessimistische verzoeken moeten gebruiken zodat ze seriegeschakeld kunnen worden om zo snel mogelijk updates te maken van de staat die verzameld is op enkele (of één) shards. De Radix Engine maakt deze hybride oplossing mogelijk via twee elementen: actiegebaseerde verzoeken en dynamische lokalisering.
Zoals we beschreven nemen alle Radix Engine-transacties de vorm aan van verzoeken aan on-ledger componentenacties. Acties definiëren hoe een bepaald verzoek een uitvoer creëert (wat veranderingen aan de staat, over hun relevante shards) gebaseerd op een invoer (een bepaalde staat, over zijn relevante shards). Actie-gebaseerde verzoeken maken zowel optimistisch als pessimistisch gebruik mogelijk.
Laten we het voorbeeld nemen van onze bovenstaande token-inwisselingscomponent, waar een gebruiker tokenA kan versturen en tokenB kan ontvangen voor de huidige koers. Onze inwisselingsactie was dit:
Inwisselactie: “Iedere ontvangst van TokenA zal een verstuur creëren van TokenB door de vergelijking TokenA * [huidige gedefinieerde B/A-koers]”
Stel dat ik 10 TokenA bezit en 5 daarvan wil versturen naar de token-inwisselingscomponent om daarvoor in de plaats het juiste aantal van TokenB te ontvangen. We kunnen de inwisselactie op twee manieren gebruiken om dit te doen:
Optimistisch verzoek: Met gebruik van de gespecificeerde “inwissel”-actie stuur ik deze specifieke 5 TokenA van mijn account naar de reserve-account; ik zie de huidige koers in de token-inwisselingscomponent (aA → 2.7B) en dus worden deze specifieke 13.5 TokenB van de reserve-account naar mij terug gestuurd.
In dit geval specificeert mijn verzoek de volledige transactie, inclusief zowel de invoer-staat (de specifieke tokens) als de uitvoer-staat-verandering (twee overschrijvingen). De transactie is valide zolang 1) ik nog steds die specifieke 5 TokenB heb, 2) ik correct het aantal van TokenB heb berekend, en 3) de reserve nog steeds die gespecificeerde 13.5 TokenB heeft. Hier valideert de actie eigenlijk alleen dat ik alles correct heb gespecificeerd. Zoniet dan faalt deze.
Pessimistisch verzoek: Met gebruik van de gespecificeerde “inwissel”-actie stuur ik iedere 5 TokenA van mijn account naar de reserve-account.
In dit geval biedt het verzoek alleen het minimum wat de actie nodig heeft. Ik kan zien dat de inwisselingsactie van de token-inwisselingscomponent verteld moet worden waar een voldoende hoeveelheid van TokenA te vinden, de gewenste te verzenden hoeveelheid, en dat dit moet leiden tot een corresponderende hoeveelheid van TokenB die naar me terug komt. Maar ik laat eht aan de actie om de uiteindelijke transactie en uitvoer te construeren (en valideren). De transactie is valide zolang 1) ik nog steeds enig 5 TokenA heb, en 2) de reserve nog steeds enig 13.5 TokenB heeft.
Dit kan gecompiceerd lijken maar in zowel de optimistische als pessimistische gevallen kan cliëntsoftware automatisch deze verzoeken construeren, gebaseerd op dat de gebruiker eenvoudigweg zegt; “ik wil 5 van mijn TokenA sturen naar de inwisselingsactie van de de token-inwisselingscomponent”. De rest wordt volledig bepaald door de actie-definitie (in in het optimistische geval de huidige ledger-staat die de cliënt kent). Radix zal dergelijke cliëntsoftware bieden als onderdeel van zijn bibliotheken vor gemakkelijke integratie.
Een verzoek aan meerdere acties kan samengenomen worden in een enkel verzoek (optimistisch of pessimistisch), de opgesomde invoeren verzamelend en een opgesomde uitvoer producerend. Cerberus hoeft alleen maar kennis te hebben van de shards waarop de invoer wordt gevonden, waar de uitvoer te laten, en de te gebruiken acties.

Links zijn drie acties gespecificeerd door een cliënt, samen met de vereiste invoer-substaat over drie shards — alsook de resulterende uitvoer-substaat, inclusief die shards plus een nieuwe, geïdentificeerd door één van de acties. Rechts de resulterende transactie op de relevante shards.
Met dit actie-gebaseerde model dat ons de mogelijkheid geeft om naar behoefte zowel optimistische als pessimistische verzoeken te creëren heeft onze volledige schaalbaarheidsoplossing alleen Radix VM nodig om wat intelligente beslissingen te maken over hoe de staat wordt opgeslagen over shards. We willen ongerelateerde staten scheiden (voor optimistisch, geparallelliseerd gebruik), maar hoog gerelateerde staten groeperen (voor pessimistisch, seriegeschakeld gebruik). Hoe de vershillende stukjes van de staat naar shards in kaart te brengen?
We zouden een erg eenvoudig model kunnen gebruiken waar we Bitcoin UTXO-achtige staten naar individuele shards in kaart brengen: Hier zou iedere shard een vaste levenscyclus hebben, actief wordend bij een UTXO invoer, en dan voor altijd latent als leen UTXO uitvoer (als de invoer naar een andere UTXO-shard). Deze benadering zou heel goed naar optimistisch gebruik in kaart brengen, shards sterk korrelig en ongerelateerd makend voor eenvoudige dingen als tokenoverschrijvingen.
Echter deze benadering komt neer op situaties waar we in zijn algemeenheid pessimistische transacties willen. Een complexe DeFi-app zou potentieel een extreem grote hoeveelheid van token-UTXO-shards kunnen aanraken, al die shards forcerend te sluiten tijdens elke consensusronde. We willen zulke sterk gerelateerde staat liever groeperen in één shard (of een minimum aantal shards) om de consensus te vereenvoudigen en doorvoer van seriegeschakelde operatie te maximaliseren.
Gelukkig maakt het feit dat we een bijna onbeperkte shardruimte en een consensus die in staat is om dynamisch over die shardruimte te werken het mogelijk voor Radix VM om een dynamische lokalisering uit te voeren. Lokalisering betekent het groeperen van een complexe set van staten in een enkele shard. Radix VM is in staat om dynamisch de expressie van staat waar nodig van een meer gedistribueerde, gekorrelde, geparallelliseerde vorm te verschuiven naar een gelokaliseerde, collectieve seriegeschakelde vorm. Dit betekent dat er geen vaste in kaart brenging is van enig particulier stukje van de staat (zoals de eigenaarschap van een token, of een huidige koers) naar particuliere shards. Radix VM is in staat om die in kaart brenging arbitrair te creëren en veranderen, zoals nodig is bij maximale doorvoer.
In zijn geheel maken de massale beschikbare parallellisme van de Radix Engine gecombineerd met zijn vermogen om dynamisch de prestaties van individuele transacties te maximaliseren het mogelijk om eindelijk het soort van multi-modus schaalbaarheid te leveren die nodig is voor Defi. Meer dan het eenvoudigweg leveren van een hoog doorvoeraantal in speciale omstandigheden is Radix ontworpen voor massaal applicatiegebruik in de echte wereld. In plaats van het vastzetten van schaalbaarheid door compositionaliteit af te breken levert Radix beide zonder compromis, wat het het enige laag-1-protocol maakt dat is ontworpen om een onderling sterk verbonden, veelvragend DeFi-ecosysteem te schalen.
Noten
[5] In concept is dit vergelijkbaar met een typische extreem grote ruimte van publieke sleutels zodat zelfs vrij, willekeurig gebruik een praktisch insigificante hoeveelheid van de beschikbare ruimte gebruikt.
Website (https://radixdlt.com)
Medium (https://medium.com/@radixdlt)
Telegram (https://t.me/radix_dlt)
Twitter (https://twitter.com/RadixDLT)