From 0fc7dff0d64f4e9c5a49032441e22b3e86860a19 Mon Sep 17 00:00:00 2001 From: Ramin Soltanzadeh <36580473+rsoltanzadeh@users.noreply.github.com> Date: Thu, 31 Jan 2019 00:37:41 +0100 Subject: [PATCH 1/4] Create intro_SE.md --- doc/intro_SE.md | 388 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 doc/intro_SE.md diff --git a/doc/intro_SE.md b/doc/intro_SE.md new file mode 100644 index 000000000..6947420b5 --- /dev/null +++ b/doc/intro_SE.md @@ -0,0 +1,388 @@ +# Introduktion till MimbleWimble och Grin + +*Läs detta på andra språk: [English](intro.md), [简体中文](intro_ZH-CN.md), [Español](intro_ES.md), [Nederlands](intro_NL.md), [Русский](intro_RU.md), [日本語](intro_JP.md), [Deutsch](intro_DE.md).* + +MimbleWimble är ett blockkedjeformat och protokoll som erbjuder extremt bra +skalbarhet, integritet, och fungibilitet genom starka kryptografiska primitiver. +Den angriper brister som existerar i nästan alla nuvarande blockkedjeimplementationer. + +Grin är ett mjukvaruprojekt med öppen källkod som implementerar en MimbleWimble-blockkedja +och fyller igen luckorna för att skapa en fullständig blockkedja och kryptovaluta. + +Grin-projektets huvudsakliga mål och kännetecken är: + +* Integritet som standard. Detta möjliggör fullkomlig fungibilitet utan att +förhindra förmågan att selektivt uppdaga information efter behov. +* Växer mestadels med antal användare och minimalt med antal transaktioner (< 100 bytes transaktionskärna), +vilket resulterar i stora utrymmesbesparingar i jämförelse med andra blockkedjor. +* Stark och bevisad kryptografi. MimbleWimble förlitar sig endast på kryptografi med +elliptiska kurvor (ECC) vilket har beprövats i decennier. +* Simplistik design som gör det enkelt att granska och underhålla på lång sikt. +* Gemenskapsdriven, uppmuntrar mining och decentralisering. + +## Tungknytande för alla + +Detta dokument är riktat mot läsare med en bra förståelse för blockkedjor och grundläggande kryptografi. +Med det i åtanke försöker vi förklara den tekniska uppbyggnaden av MimbleWimble och hur det appliceras i Grin. +Vi hoppas att detta dokument är föreståeligt för de flesta tekniskt inriktade läsare. Vårt mål är att +uppmuntra er att bli intresserade i Grin och bidra på något möjligt sätt. + +För att uppnå detta mål kommer vi att introducera de huvudsakliga begrepp som krävs för en +bra förståelse för Grin som en MimbleWimble-implementation. Vi kommer att börja med en kort +beskrivning av några av elliptiska kurvornas relevanta egenskaper för att lägga grunden som Grin +är baserat på och därefter beskriva alla viktiga element i en MimbleWimble-blockkedjas +transaktioner och block. + +### Småbitar av elliptiska kurvor + +Vi börjar med en kort undervisning i kryptografi med elliptiska kurvor (ECC) där vi endast +går igenom de nödvändiga egenskaper för att förstå hur MimbleWimble fungerar utan att +gå djupt in på dess krångligheter. För läsare som vill fördjupa sig i detta finns andra +möjligheter att [lära sig mer](http://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/). + +En elliptisk kurva för kryptografiska är ändamål är enkelt sagt en stor mängd av punkter +som vi kallar för _C_. Dessa punkter kan adderas, subtraheras, eller multipliceras med heltal (även kallat skalärer). +Given ett heltal _k_ kan vi beräkna `k*H` med skalärmultiplikation, vilket också är en punkt på kurvan _C_. Given ett annat +heltal _j_ kan vi också beräkna `(k+j)*H`, vilket är lika med `k*H + j*H`. Addition och skalärmultiplikation på elliptiska +kurvor behåller sina kommutativa och associativa egenskaper från vanlig addition och multiplikation: + + (k+j)*H = k*H + j*H + +Inom ECC, om vi väljer ett väldigt stort tal _k_ som privat nyckel så anses `k*H` vara dess publika nyckel. Även om +man vet värdet av den publika nyckeln `k*H`, är det nästintill omöjligt att härleda `k` (sagt med andra ord, medan +multiplikation är trivialt är "division" med kurvpunkter extremt svårt). + +Den föregående formeln `(k+j)*H = k*H + j*H`, med _k_ och _j_ båda privata nycklar demonstrerar att en publik nyckel +erhållen av att ha adderat de två privata nycklarna är identisk med de två privata nycklarnas respektive +publika nycklar adderade (`k*H + j*H`). I Bitcoin-blockkedjan använder hierarkiska deterministiska plånböcker (HD wallets) +sig flitigt av denna princip. MimbleWimble och Grin-implementationer gör det också. + +### Transaktioner med MimbleWimble + +Transaktionernas struktur demonstrerar en av MimbleWimbles kritiska grundsatser: +starka garantier av integritet och konfidentialitet. + +Valideringen av MimbleWimble-transaktioner använder sig av två grundläggande egenskaper: + +* **Kontroll av nollsummor.** Summan av utmatningar minus inmatningar är alltid lika med noll, vilket bevisar—utan att +avslöja beloppen—att transaktionen inte skapade nya pengar. +* **Innehav av privata nycklar.** Som med de flesta andra kryptovalutar garanteras ägandet av transaktionsutmatningar +med innehavet av privata nycklar. Dock bevisas inte ägandet av dem genom en direkt signering av transaktionen. + +De följande styckena angående saldo, ägande, växel, och bevis klarlägger hur de två grundläggande egenskaperna uppnås. + +#### Saldo + +Bygger vi på ECC-egenskaperna vi förklarade ovan kan vi beslöja beloppen i en transaktion. + +Om _v_ är beloppet av en inmatning eller utmatning i en transaktion och _H_ en elliptisk kurva, kan vi enkelt bädda in +`v*H` i stället för _v_ i en transaktion. Detta fungerar eftersom vi fortfarande kan bekräfta att summan av utmatningarna är +lika med summan av inmatningarna i en transaktion med hjälp av ECC-operationer: + + v1 + v2 = v3 => v1*H + v2*H = v3*H + +Bekräftandet av denna egenskap på alla transaktioner låter protokollet bekräfta att en transaktion inte skapar pengar ur +tomma intet utan att veta vad beloppen är. Dock finns det ett begränsat antal av användbara belopp och man skulle kunna +prova varenda en för att gissa beloppet på din transaktion. Dessutom, om man känner till v1 (till exempel från en föregående +transaktion) och det resulterande `v1*H` avslöjar man alla utmatningar med beloppet v1 över hela blockkedjan. Av dessa +anledningar introducerar vi en till elliptisk kurva _G_ (i praktiken är _G_ endast en annan generatorpunkt på samma kurvgrupp +som _H_) och en privat nyckel _r_ som används som en *bländande faktor*. + +Ett inmatnings- eller utmatningsbelopp i en transaktion kan uttryckas som: + + r*G + v*H + +Där: + +* _r_ är en privat nyckel använd som en bländande faktor, _G_ är en elliptisk kurva, och deras +produkt `r*G` är den publika nyckeln för _r_ på _G_. +* _v_ är ett inmatnings- eller utmatningsbelopp och _H_ är en annan elliptisk kurva. + +Varken _v_ eller _r_ kan härledas på grund av ECC:s grundläggande egenskaper. `r*G + v*H` kallas för +ett _Pedersen Commitment_. + +Som ett exempel, låt oss anta att vi vill skapa en transaktion med två inmatningar och en utmatning. +Vi har (utan hänsyn till avgifter): + +* vi1 och vi2 som inmatningsbelopp. +* vo3 som utmatningsbelopp. + +Sådana att: + + vi1 + vi2 = vo3 + +Vi genererar en privat nyckel som en bländande faktor för varje inmatningsbelopp och ersätter alla belopp med +deras respektive Pedersen Commitment och ekvationen blir därmed: + + (ri1*G + vi1*H) + (ri2*G + vi2*H) = (ro3*G + vi3*H) + +Vilket som följd kräver att: + + ri1 + ri2 = ro3 + +Detta är MimbleWimbles första pelare: de beräkningar som är nödvändiga för att validera en transaktion +kan göras utan att veta några belopp. + +Denna idé härstammar faktiskt från Greg Maxwells +[Confidential Transactions](https://elementsproject.org/features/confidential-transactions/investigation), +som i sin tur härstammar från ett förslag av Adam Back för homomorfiska belopp applicerade på Bitcoin. + +#### Ägande + +I föregående stycke introducerade vi en privat nyckel som en bländande faktor för att dölja transaktionens belopp. +MimbleWimbles andra insikt är att denna privata nyckel kan användas för att bevisa ägande av beloppet. + +Alice skickar 3 mynt till dig och för att dölja beloppet väljer du 28 som din bländande faktor (notera att i praktiken +är den bländande faktorn ett extremt stort tal). Någonstans i blockkedjan dyker följande utmatning upp och ska endast +vara spenderbar av dig: + + X = 28*G + 3*H + +_X_ som är resultatet av additionen är synlig för alla. Beloppet 3 är endast känt av dig och Alice, och 28 är endast +känt av dig. + +För att skicka dessa 3 mynt igen kräver protokollet att 28 ska vara känt. För att demonstrera hur detta fungerar, låt +oss säga att du vill skicka samma 3 mynt till Carol. Du behöver skapa en simpel transaktion sådan att: + + Xi => Y + +Där _Xi_ är en inmatning som spenderar din _X_-utmatning och Y är Carols utmatning. Det finns inget sätt att skapa +en sådan transaktion utan att känna till din privata nyckel 28. Om Carol ska balansera denna transaktion behöver hon +både känna till det skickade beloppet och din privata nyckel så att: + + Y - Xi = (28*G + 3*H) - (28*G + 3*H) = 0*G + 0*H + +Genom att kontrollera att allt har nollställts kan vi återigen försäkra oss om att inga nya pengar har skapats. + +Vänta! Stopp! Nu känner du till den privata nyckeln i Carols utmatning (vilket i detta fall måste vara samma som ditt +för att balansera in- och utmatningarna) så du skulle kunna stjäla tillbaka pengarna från Carol! + +För att lösa detta problem använder Carol en privat nyckel som hon väljer själv. Låt oss säga att hon väljer 113. +Det som hamnar i blockkedjan är: + + Y - Xi = (113*G + 3*H) - (28*G + 3*H) = 85*G + 0*H + +Nu summeras transaktionen inte längre till noll och vi har ett _överskottsbelopp_ på _G_ (85), vilket är resultatet +av summeringen av alla bländande faktorer. Men eftersom `85*G` är en giltig publik nyckel på elliptiska kurvan _G_ vet vi +att in- och utmatningarna har subtraheras till noll och transaktionen är därmed giltig. + +Så allt protokollet behöver göra är att kontrollera att (`Y - Xi`) är en giltig publik nyckel på _G_ och att de två parter +som utför transaktionen tillsammans kan producera den privata nyckeln (85 i exemplet ovan). Det enklaste sättet att göra +det är att kräva en signatur med överskottsbeloppet (85), vilket bekräftar att: + +* De parter som utför transaktionen känner till den privata nyckeln, och +* Summan av utmatningarna minus inmatningarna i transaktionen är noll (eftersom överskottsbeloppet måste vara en publik nyckel). + +Denna signatur som tillsammans med lite annan information (som exempelvis mining-avgifter) bifogas till transaktionen kallas +för _transaktionskärna_ och kontrolleras av alla validerare. + +#### Några finare punkter + +Detta stycke detaljerar byggandet av transaktioner genom att diskutera hur växel införs och kravet för "range proofs" +så att alla belopp är bevisade att vara icke-negativa. Inget av detta är absolut nödvändigt för att förstå MimbleWimble +och Grin, så om du har bråttom känn dig fri att hoppa direkt till [Sammanställningen av allt](#sammanställningen-av-allt). + +#### Växel + +Låt oss säga att du endast vill skicka 2 mynt till Carol av de 3 mynt du mottog från Alice. För att göra detta behöver du +skicka det återstående myntet tillbaka till dig själv som växel. Du genererar en annan privat nyckel (t ex 12) som en +bländande faktor för att skydda ditt växel-utmatningsbelopp. Carol använder sin egen privata nyckel som tidigare. + + Växel-utmatning: 12*G + 1*H + Carols utmatning: 113*G + 2*H + +Det som hamnar i blockkedjan är något väldigt likt det vi hade tidigare, och signaturen är återigen skapat med +överskottsbeloppet, 97 i detta exempel. + + (12*G + 1*H) + (113*G + 2*H) - (28*G + 3*H) = 97*G + 0*H + +#### Range Proofs + +I alla beräkningar ovan förlitar vi oss på att alla belopp är positiva. Introduktionen av negativa belopp skulle vara +extremt problematiskt då man skulle kunna skapa nya pengar i varje transaktion. + +Till exempel skulle man kunna skapa en transaktion med inmatningen 2 och utmatningar 5 och -3 och fortfarande +ha en balanserad transaktion. Detta kan inte upptäcklas enkelt eftersom punkten `x*H` ser ut som vilken annan punkt +som helst på kurvan även om _x_ är negativt. + +För att lösa detta problem använder MimbleWimble sig av ett kryptografiskt koncept som kallas "range proofs" (som också härstammar +från Confidential Transactions): ett bevis på att ett tal befinner sig inom ett visst intervall utan att avsölja talet. +Vi kommer inte att förklara range proofs; du behöver endast veta att för varje `r*G + v*H` kan vi skapa ett bevis som visar +att _v_ är större än noll och inte orsakar overflow. + +Det är även viktigt att notera att både värdet 113 och värdet 28 måste vara kända för att kunna skapa ett giltigt range proof. +Anledningen till detta och en mer utförlig beskrivning av range proofs är förklarat i +[range proof-pappret](https://eprint.iacr.org/2017/1066.pdf). + +#### Sammanställningen av allt + +En MimbleWimble-transaktion inkluderar följande: + +* En mängd inmatningar som refererar till och spenderar en mängd föregående utmatningar. +* En mängd nya utmatningar som inkluderar: + * Ett belopp och en bländande faktor (vilket bara är en ny privat nyckel) multiplicerade på en kurva och adderade + till att bli `r*G + v*H`. + * Ett range proof som visar att v är icke-negativt. +* En tydlig transaktionsavgift i klartext. +* En signatur vars privata nyckel beräknas genom att ta överskottsbeloppet (summan av alla utmatningar och +avgiften minus inmatningarna). + +### Block och kedjetillstånd + +Vi förklarade ovan hur MimbleWimble-transaktioner kan erbjuda starka anonymitetsgarantier samtidigt som de +upprätthåller egenskaperna för en giltig blockkedja, d.v.s en transaktion skapar inte pengar och ägandebevis är +fastställt med privata nycklar. + +MimbleWimble-blockformatet bygger på detta genom att introducera ett till koncept: _genomskärning_. Med detta +får en MimbleWimble-kedja: + +* Extremt bra skalbarhet då den stora majoriteten av transaktionsinformation kan elimineras på lång sikt utan att +kompromissa säkerhet. +* Ytterligare anonymitet genom att blanda och ta bort transaktionsinformation. +* Förmågan att effektivt synkronisera sig med resten av nätverket för nya noder. + +#### Transaktionsaggregation + +Kom igåg att en transaktion består av följande: + +* En mängd inmatningar som refererar till och spenderar en mängd föregående utmatningar +* En mängd nya utmatningar (Pedersen commitments) +* En transaktionskärna som består av: + * överskottsbelopp + * transaktionssignatur + +En transaktion signeras och signaturen inkluderas i en transaktionskärna. Signaturen genereras genom att använda +överskottsbeloppet som en publik nyckel för att bevisa att beloppen summeras till 0: + + (42*G + 1*H) + (99*G + 2*H) - (113*G + 3*H) = 28*G + 0*H + +Den publika nyckeln i detta exempel är `28*G`. + +Vi kan säga att följande är sant för alla giltiga transaktioner (vi ignorerar avgifter för enkelhetens skull): + + summa(utmatningar) - summa(inmatningar) = överskottsbelopp + +Detsamma gäller för blocken själva när vi inser att ett block helt enkelt är en mängd aggregerade inmatningar, utmatningar, och +transaktionskärnor. Vi kan summera transaktionsutmatningarna, subtrahera summan av transaktionsinmatningarna, och jämföra +det resulterande Pedersen commitment med summan av överskottsbeloppen: + + summa(utmatningar) - summa(inmatningar) = summa(överskottsbelopp) + + +Något förenklat, (återigen ignorerar vi transaktionsavgifter) kan vi säga att MimbleWimble-block kan betraktas precis som +MimbleWimble-transaktioner. + +##### Kärn-offset + +Det finns ett subtilt problem med MimbleWimble-block och transaktioner som beskrivet ovan. Det är möjligt (och i vissa fall +trivialt) att rekonstruera de konstituerande transaktionerna i ett block. Detta är naturligtvis dåligt för integriteten. +Detta är "delmängdsproblemet": given en mängd inmatningar, utmatningar, och transaktionskärnor kommer någon delmängd av detta +kunna kombineras för att rekonstruera en giltig transaktion. + +Till exempel, vi har följande två transaktioner: + + (inmatning1, inmatning2) -> (utmatning1), (kärna1) + (inmatning3) -> (utmatning2), (kärna2) + +Vi kan aggregera dem till följande block: + + (inmatning1, inmatning2, inmatning3) -> (utmatning1, utmatning2), (kärna1, kärna2) + +Det är trivialt att testa alla möjliga kombinationer och återskapa en av transaktionerna (där summan lyckas bli noll). + + (inmatning1, inmatning2) -> (utmatning1), (kärna1) + +Vi vet också att allt som kvarstår kan användas för att rekonstruera den andra giltiga transaktionen: + + (inmatning3) -> (utmatning2), (kärna2) + +För att mildra detta inkluderar vi ett _kärn-offset_ med varje överskottsbelopp. Detta är en bländande faktor som måste +tilläggas överskottsbeloppet för att verifiera att det summeras till noll: + + summa(utmatningar) - summa(inmatningar) = överskottsbelopp + kärn-offset + +Vi "separerar" nyckeln `k` till `k1 + k2` under transaktionsbyggandet. För ett överskottsbelopp `(k1+k2)*G` publicerar vi +`k1*G` (överskottet) och `k2` (offset) och signerar transaktionen med `k1*G` som tidigare. Under block-konstruktionen +kan vi enkelt summera alla `k2`-offset för att generera ett aggregat-offset för alla transaktioner i blocket. `k2`-offsetet +för en individuell transaktion är omöjlig att få fram. + +#### Genomskärning + +Blocks låter miners sätta ihop flera transaktioner till en enstaka mängd som läggs till på kedjan. I följande +block-representationer som innerhåller tre transaktioner visar vi endast in- och utmatningarna. Inmatningar refererar till +föregående utmatningar som de spenderar. Föregående utmatningar markeras med _x_. + + I1(x1) --- O1 + |- O2 + + I2(x2) --- O3 + I3(O2) -| + + I4(O3) --- O4 + |- O5 + +Vi lägger märke till följande två egenskaper: + +* Inom detta block är vissa utmatningar spenderade direkt av inkluderade inmatningar (I3 spenderar O2, och I4 spenderar O3). +* Transaktionernas struktur spelar faktiskt ingen roll. Eftersom alla transaktioner individuellt summeras till noll +måste summan av alla transaktionsinmatningar och utmatningar summera till noll. + +Liknande en transaktion, allt som behöver kontrolleras i ett block är att ägandebevis (vilket kommer från transaktionskärnorna) +och att blocket i helhet inte skapade pengar ur tomma intet. Således kan matchande inmatningar och utmatningar elimineras, då +deras sammansatta påverkan är noll. Detta leder till följande, mycket mer kompakta block: + + I1(x1) | O1 + I2(x2) | O4 + | O5 + +Notera att all transaktionsstruktur har eliminerats och att ordningen av in- och utmatningar inte längre spelar någon roll. +Summan av alla in- och utmatningar garanteras fortfarande vara noll. + +Ett block består av: + +* En block-header. +* En lista av alla inmatningar som kvarstår efter genomskärning. +* En lista av alla utmatningar som kvarstår efter genomskärning. +* Ett enstaka kärn-offset som skyddar hela blocket. +* Transaktionskärnor för varje transaktion som innehåller: + * Publika nyckeln `r*G` erhållen genom summation av alla commitments. + * Signaturerna genererade genom överskottsbeloppet. + * Mining-avgiften + +Med denna struktur erbjuder ett MimbleWimble-block extremt bra integritetsgarantier: + +* Mellanliggande transaktioner är endast representerade av sina transaktionskärnor. +* Alla utmatningar ser likadana ut: väldigt stora tal som inte går att skilja åt på något meningsfullt sätt. +Om någon vill exkludera en specifik utmatning är de tvungna att exkludera alla. +* All transaktionsstruktur har borttagits vilket gör det omöjligt att se vilka in- och utmatningar som passar ihop. + +Men ändå kan allting valideras! + +#### Genomskärning hela vägen + +Vi går tillbaka till blocket i föregående exempel. Utmatningarna x1 och x2 som spenderades av I1 och I2 måste ha +dykt upp tidigare i blockkedjan. Efter att detta block adderas till blockkedjan kan de utmatningarna tillsammans med +I1 och I2 alla tas bort från blockkedjan eftersom de nu är mellanliggande transaktioner. + +Vi slutleder att kedjetillståndet kan (bortsett från block-headers) vid varje tidspunkt sammanfattas med endast dessa tre ting: + +1. Den totala mängden mynt skapade genom mining. +2. Den kompletta mängden av oförbrukade utmatningar (UTXO). +3. Transaktionskärnorna för varje transaktion. + +Det första kan härledas genom att endast observera block-höjden. + +Både mängden av oförbrukade utmatningar och transaktionskärnorna är extremt kompakta. Detta har två följder: + +* En nod i en MimbleWimble-blockkedja får en väldigt liten kedja att behöva ta vara på. +* När en ny nod ansluter sig till närverket krävs det väldigt lite information för att den ska bygga kedjan. + +Dessutom kan man inte manipulera mängden av de oförbrukade utmatningarna. Tar man bort ett element ändras summan av +de bländande faktorerna och in- och utmatningarna matchar inte längre varandra. + +### Slutsats + +I detta dokument gick vi igenom de grundläggande principerna för en MimbleWimble-blockkedja. Genom att använda egenskaperna +för addition i kryptografi med elliptiska kurvor kan vi skapa fullständigt förmörkade transaktioner som ändå kan valideras. +Genom att generalisera dessa egenskaper till block kan vi eliminera en stor mängd blockkedjeinformation vilket medför +väldigt bra skalbarhet. From 422db826674d0ae1794d3df8d20d6801abf72ce1 Mon Sep 17 00:00:00 2001 From: Antioch Peverell Date: Thu, 31 Jan 2019 09:23:19 +0000 Subject: [PATCH 2/4] no is_mainnet() to keep tests close to mainnet (#2496) --- core/src/global.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/src/global.rs b/core/src/global.rs index 013341920..7cdfb5869 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -267,17 +267,14 @@ pub fn is_production_mode() -> bool { } /// Are we in floonet? +/// Note: We do not have a corresponding is_mainnet() as we want any tests to be as close +/// as possible to "mainnet" configuration as possible. +/// We want to avoid missing any mainnet only code paths. pub fn is_floonet() -> bool { let param_ref = CHAIN_TYPE.read(); ChainTypes::Floonet == *param_ref } -/// Are we for real? -pub fn is_mainnet() -> bool { - let param_ref = CHAIN_TYPE.read(); - ChainTypes::Mainnet == *param_ref -} - /// Helper function to get a nonce known to create a valid POW on /// the genesis block, to prevent it taking ages. Should be fine for now /// as the genesis block POW solution turns out to be the same for every new From 0c851c5140661e80ccdaec3899ea313118284c65 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Thu, 31 Jan 2019 11:55:34 +0000 Subject: [PATCH 3/4] Save slate participant messages in database (#2441) * Save and display optional slate messages * rustfmt * fixes and test updates * rustfmt * output json * rustfmt * add better serialisation of slate message, rename to secp_ser, add tests for serialization functions * rustfmt * max length for message enforced by API * rustfmt --- Cargo.lock | 1 + core/Cargo.toml | 3 + core/src/libtx/mod.rs | 2 +- .../libtx/{serialization.rs => secp_ser.rs} | 55 ++++++++++++++ core/src/libtx/slate.rs | 47 +++++++++++- src/bin/cmd/wallet_tests.rs | 18 ++++- wallet/src/command.rs | 8 +- wallet/src/display.rs | 76 ++++++++++++++++++- wallet/src/libwallet/api.rs | 24 +++++- wallet/src/libwallet/internal/selection.rs | 4 + wallet/src/libwallet/internal/tx.rs | 23 +++++- wallet/src/libwallet/types.rs | 4 + wallet/tests/file.rs | 34 ++++++++- 13 files changed, 288 insertions(+), 11 deletions(-) rename core/src/libtx/{serialization.rs => secp_ser.rs} (78%) diff --git a/Cargo.lock b/Cargo.lock index ccb25d079..70d7e744a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,6 +788,7 @@ dependencies = [ "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index f317bebe1..19f49665d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -30,3 +30,6 @@ chrono = "0.4.4" grin_keychain = { path = "../keychain", version = "1.0.0" } grin_util = { path = "../util", version = "1.0.0" } + +[dev-dependencies] +serde_json = "1" diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index 319d85438..a9c802b03 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -26,7 +26,7 @@ pub mod build; mod error; pub mod proof; pub mod reward; -pub mod serialization; +pub mod secp_ser; pub mod slate; use crate::consensus; diff --git a/core/src/libtx/serialization.rs b/core/src/libtx/secp_ser.rs similarity index 78% rename from core/src/libtx/serialization.rs rename to core/src/libtx/secp_ser.rs index 969be3640..13a4ce14b 100644 --- a/core/src/libtx/serialization.rs +++ b/core/src/libtx/secp_ser.rs @@ -173,3 +173,58 @@ where { serializer.serialize_str(&to_hex(bytes.as_ref().to_vec())) } + +// Test serialization methods of components that are being used +#[cfg(test)] +mod test { + use super::*; + use crate::libtx::aggsig; + use crate::util::secp::key::{PublicKey, SecretKey}; + use crate::util::secp::{Message, Signature}; + use crate::util::static_secp_instance; + + use serde_json; + + use rand::{thread_rng, Rng}; + + #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] + struct SerTest { + #[serde(with = "pubkey_serde")] + pub pub_key: PublicKey, + #[serde(with = "option_sig_serde")] + pub opt_sig: Option, + #[serde(with = "sig_serde")] + pub sig: Signature, + } + + impl SerTest { + pub fn random() -> SerTest { + let static_secp = static_secp_instance(); + let secp = static_secp.lock(); + let sk = SecretKey::new(&secp, &mut thread_rng()); + let mut msg = [0u8; 32]; + thread_rng().fill(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + let sig = aggsig::sign_single(&secp, &msg, &sk, None).unwrap(); + SerTest { + pub_key: PublicKey::from_secret_key(&secp, &sk).unwrap(), + opt_sig: Some(sig.clone()), + sig: sig.clone(), + } + } + } + + #[test] + fn ser_secp_primitives() { + for _ in 0..10 { + let s = SerTest::random(); + println!("Before Serialization: {:?}", s); + let serialized = serde_json::to_string_pretty(&s).unwrap(); + println!("JSON: {}", serialized); + let deserialized: SerTest = serde_json::from_str(&serialized).unwrap(); + println!("After Serialization: {:?}", deserialized); + println!(); + assert_eq!(s, deserialized); + } + } +} diff --git a/core/src/libtx/slate.rs b/core/src/libtx/slate.rs index c1ab36c60..2fc2fe3b7 100644 --- a/core/src/libtx/slate.rs +++ b/core/src/libtx/slate.rs @@ -22,7 +22,7 @@ use crate::core::transaction::{kernel_features, kernel_sig_msg, Transaction, Wei use crate::core::verifier_cache::LruVerifierCache; use crate::keychain::{BlindSum, BlindingFactor, Keychain}; use crate::libtx::error::{Error, ErrorKind}; -use crate::libtx::{aggsig, build, tx_fee}; +use crate::libtx::{aggsig, build, secp_ser, tx_fee}; use crate::util::secp; use crate::util::secp::key::{PublicKey, SecretKey}; use crate::util::secp::Signature; @@ -66,6 +66,33 @@ impl ParticipantData { } } +/// Public message data (for serialising and storage) +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ParticipantMessageData { + /// id of the particpant in the tx + pub id: u64, + /// Public key + #[serde(with = "secp_ser::pubkey_serde")] + pub public_key: PublicKey, + /// Message, + pub message: Option, + /// Signature + #[serde(with = "secp_ser::option_sig_serde")] + pub message_sig: Option, +} + +impl ParticipantMessageData { + /// extract relevant message data from participant data + pub fn from_participant_data(p: &ParticipantData) -> ParticipantMessageData { + ParticipantMessageData { + id: p.id, + public_key: p.public_blind_excess, + message: p.message.clone(), + message_sig: p.message_sig.clone(), + } + } +} + /// A 'Slate' is passed around to all parties to build up all of the public /// transaction data needed to create a finalized transaction. Callers can pass /// the slate around by whatever means they choose, (but we can provide some @@ -94,6 +121,13 @@ pub struct Slate { pub participant_data: Vec, } +/// Helper just to facilitate serialization +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ParticipantMessages { + /// included messages + pub messages: Vec, +} + impl Slate { /// Create a new slate pub fn blank(num_participants: usize) -> Slate { @@ -274,10 +308,19 @@ impl Slate { message: message, message_sig: message_sig, }); - Ok(()) } + /// helper to return all participant messages + pub fn participant_messages(&self) -> ParticipantMessages { + let mut ret = ParticipantMessages { messages: vec![] }; + for ref m in self.participant_data.iter() { + ret.messages + .push(ParticipantMessageData::from_participant_data(m)); + } + ret + } + /// Somebody involved needs to generate an offset with their private key /// For now, we'll have the transaction initiator be responsible for it /// Return offset private key for the participant to use later in the diff --git a/src/bin/cmd/wallet_tests.rs b/src/bin/cmd/wallet_tests.rs index f9b301fe4..6aa6eff7d 100644 --- a/src/bin/cmd/wallet_tests.rs +++ b/src/bin/cmd/wallet_tests.rs @@ -253,6 +253,16 @@ mod wallet_tests { let mut bh = 10u64; let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize); + let very_long_message = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\ + This part should all be truncated"; + // Update info and check let arg_vec = vec!["grin", "wallet", "-p", "password", "-a", "mining", "info"]; execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; @@ -273,7 +283,7 @@ mod wallet_tests { "-d", &file_name, "-g", - "Love, Yeast", + very_long_message, "10", ]; execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; @@ -491,6 +501,12 @@ mod wallet_tests { let arg_vec = vec!["grin", "wallet", "-p", "password", "-a", "mining", "txs"]; execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; + // message output (mostly spit out for a visual in test logs) + let arg_vec = vec![ + "grin", "wallet", "-p", "password", "-a", "mining", "txs", "-i", "10", + ]; + execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; + // txs and outputs (mostly spit out for a visual in test logs) let arg_vec = vec![ "grin", "wallet", "-p", "password", "-a", "mining", "outputs", diff --git a/wallet/src/command.rs b/wallet/src/command.rs index c45925dba..35e51959f 100644 --- a/wallet/src/command.rs +++ b/wallet/src/command.rs @@ -396,15 +396,19 @@ pub fn txs( &g_args.account, height, validated, - txs, + &txs, include_status, dark_scheme, )?; // if given a particular transaction id, also get and display associated - // inputs/outputs + // inputs/outputs and messages if args.id.is_some() { let (_, outputs) = api.retrieve_outputs(true, false, args.id)?; display::outputs(&g_args.account, height, validated, outputs, dark_scheme)?; + // should only be one here, but just in case + for tx in txs { + display::tx_messages(&tx, dark_scheme)?; + } }; Ok(()) })?; diff --git a/wallet/src/display.rs b/wallet/src/display.rs index b66965110..73d0bf2d5 100644 --- a/wallet/src/display.rs +++ b/wallet/src/display.rs @@ -123,7 +123,7 @@ pub fn txs( account: &str, cur_height: u64, validated: bool, - txs: Vec, + txs: &Vec, include_status: bool, dark_background_color_scheme: bool, ) -> Result<(), Error> { @@ -357,3 +357,77 @@ pub fn accounts(acct_mappings: Vec) { table.printstd(); println!(); } + +/// Display transaction log messages +pub fn tx_messages(tx: &TxLogEntry, dark_background_color_scheme: bool) -> Result<(), Error> { + let title = format!("Transaction Messages - Transaction '{}'", tx.id,); + println!(); + let mut t = term::stdout().unwrap(); + t.fg(term::color::MAGENTA).unwrap(); + writeln!(t, "{}", title).unwrap(); + t.reset().unwrap(); + + let msgs = match tx.messages.clone() { + None => { + writeln!(t, "{}", "None").unwrap(); + t.reset().unwrap(); + return Ok(()); + } + Some(m) => m.clone(), + }; + + if msgs.messages.is_empty() { + writeln!(t, "{}", "None").unwrap(); + t.reset().unwrap(); + return Ok(()); + } + + let mut table = table!(); + + table.set_titles(row![ + bMG->"Participant Id", + bMG->"Message", + bMG->"Public Key", + bMG->"Signature", + ]); + + let secp = util::static_secp_instance(); + let secp_lock = secp.lock(); + + for m in msgs.messages { + let id = format!("{}", m.id); + let public_key = format!( + "{}", + util::to_hex(m.public_key.serialize_vec(&secp_lock, true).to_vec()) + ); + let message = match m.message { + Some(m) => format!("{}", m), + None => "None".to_owned(), + }; + let message_sig = match m.message_sig { + Some(s) => format!("{}", util::to_hex(s.serialize_der(&secp_lock))), + None => "None".to_owned(), + }; + if dark_background_color_scheme { + table.add_row(row![ + bFC->id, + bFC->message, + bFC->public_key, + bFB->message_sig, + ]); + } else { + table.add_row(row![ + bFD->id, + bFb->message, + bFD->public_key, + bFB->message_sig, + ]); + } + } + + table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP); + table.printstd(); + println!(); + + Ok(()) +} diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index 9d247a148..337582fe0 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -48,6 +48,8 @@ use crate::libwallet::{Error, ErrorKind}; use crate::util; use crate::util::secp::{pedersen, ContextFlag, Secp256k1}; +const USER_MESSAGE_MAX_LEN: usize = 256; + /// Functions intended for use by the owner (e.g. master seed holder) of the wallet. pub struct APIOwner where @@ -551,7 +553,8 @@ where /// ParticipantData within the slate. This message will include a signature created with the /// sender's private keys, and will be publically verifiable. Note this message is for /// the convenience of the participants during the exchange; it is not included in the final - /// transaction sent to the chain. Validation of this message is optional. + /// transaction sent to the chain. The message will be truncated to 256 characters. + /// Validation of this message is optional. /// /// # Returns /// * a result containing: @@ -640,6 +643,14 @@ where None => w.parent_key_id(), }; + let message = match message { + Some(mut m) => { + m.truncate(USER_MESSAGE_MAX_LEN); + Some(m) + } + None => None, + }; + let (slate, context, lock_fn) = tx::create_send_tx( &mut *w, amount, @@ -685,12 +696,12 @@ where let context = w.get_private_context(slate.id.as_bytes())?; tx::complete_tx(&mut *w, slate, &context)?; tx::update_stored_tx(&mut *w, slate)?; + tx::update_message(&mut *w, slate)?; { let mut batch = w.batch()?; batch.delete_private_context(slate.id.as_bytes())?; batch.commit()?; } - w.close()?; Ok(()) } @@ -873,6 +884,15 @@ where return Err(ErrorKind::TransactionAlreadyReceived(slate.id.to_string()).into()); } } + + let message = match message { + Some(mut m) => { + m.truncate(USER_MESSAGE_MAX_LEN); + Some(m) + } + None => None, + }; + let res = tx::receive_tx(&mut *w, slate, &parent_key_id, message); w.close()?; diff --git a/wallet/src/libwallet/internal/selection.rs b/wallet/src/libwallet/internal/selection.rs index fcc6ed4ca..560aa616e 100644 --- a/wallet/src/libwallet/internal/selection.rs +++ b/wallet/src/libwallet/internal/selection.rs @@ -100,6 +100,7 @@ where let lock_inputs = context.get_inputs().clone(); let _lock_outputs = context.get_outputs().clone(); + let messages = Some(slate.participant_messages()); // Return a closure to acquire wallet lock and lock the coins being spent // so we avoid accidental double spend attempt. @@ -122,6 +123,7 @@ where } t.amount_debited = amount_debited; + t.messages = messages; // write the output representing our change for (change_amount, id, _) in &change_amounts_derivations { @@ -195,6 +197,7 @@ where ); context.add_output(&key_id, &None); + let messages = Some(slate.participant_messages()); // Create closure that adds the output to recipient's wallet // (up to the caller to decide when to do) @@ -206,6 +209,7 @@ where t.tx_slate_id = Some(slate_id); t.amount_credited = amount; t.num_outputs = 1; + t.messages = messages; batch.save(OutputData { root_key_id: parent_key_id.clone(), key_id: key_id_inner.clone(), diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index c68dd3084..2bfb42244 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -39,7 +39,6 @@ where // create an output using the amount in the slate let (_, mut context, receiver_create_fn) = selection::build_recipient_output_with_slate(wallet, slate, parent_key_id.clone())?; - // fill public keys let _ = slate.fill_round_1( wallet.keychain(), @@ -55,6 +54,8 @@ where // Save output in wallet let _ = receiver_create_fn(wallet); + update_message(wallet, slate)?; + Ok(()) } @@ -224,6 +225,26 @@ where Ok(()) } +/// Update the transaction participant messages +pub fn update_message(wallet: &mut T, slate: &Slate) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?; + if tx_vec.is_empty() { + return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()))?; + } + let mut batch = wallet.batch()?; + for mut tx in tx_vec.into_iter() { + tx.messages = Some(slate.participant_messages()); + let parent_key = tx.parent_key_id.clone(); + batch.save_tx_log_entry(tx, &parent_key)?; + } + batch.commit()?; + Ok(()) +} #[cfg(test)] mod test { use crate::core::libtx::build; diff --git a/wallet/src/libwallet/types.rs b/wallet/src/libwallet/types.rs index cf2a35278..0b8523c3c 100644 --- a/wallet/src/libwallet/types.rs +++ b/wallet/src/libwallet/types.rs @@ -25,6 +25,7 @@ use crate::util::secp::key::{PublicKey, SecretKey}; use crate::util::secp::{self, pedersen, Secp256k1}; use chrono::prelude::*; use failure::ResultExt; +use grin_core::libtx::slate::ParticipantMessages; use serde; use serde_json; use std::collections::HashMap; @@ -610,6 +611,8 @@ pub struct TxLogEntry { pub amount_debited: u64, /// Fee pub fee: Option, + /// Message data, stored as json + pub messages: Option, /// Location of the store transaction, (reference or resending) pub stored_tx: Option, } @@ -643,6 +646,7 @@ impl TxLogEntry { num_inputs: 0, num_outputs: 0, fee: None, + messages: None, stored_tx: None, } } diff --git a/wallet/tests/file.rs b/wallet/tests/file.rs index c4202f730..925f7cca4 100644 --- a/wallet/tests/file.rs +++ b/wallet/tests/file.rs @@ -27,6 +27,8 @@ use std::fs; use std::thread; use std::time::Duration; +use serde_json; + fn clean_output_dir(test_dir: &str) { let _ = fs::remove_dir_all(test_dir); } @@ -137,7 +139,7 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> { // wallet 2 receives file, completes, sends file back wallet::controller::foreign_single_use(wallet2.clone(), |api| { - api.receive_tx(&mut slate, None, Some(sender2_message))?; + api.receive_tx(&mut slate, None, Some(sender2_message.clone()))?; adapter.send_tx_async(&receive_file, &mut slate)?; Ok(()) })?; @@ -174,6 +176,36 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> { Ok(()) })?; + // Check messages, all participants should have both + wallet::controller::owner_single_use(wallet1.clone(), |api| { + let (_, tx) = api.retrieve_txs(true, None, Some(slate.id))?; + assert_eq!( + tx[0].clone().messages.unwrap().messages[0].message, + Some(message.to_owned()) + ); + assert_eq!( + tx[0].clone().messages.unwrap().messages[1].message, + Some(sender2_message.to_owned()) + ); + + let msg_json = serde_json::to_string_pretty(&tx[0].clone().messages.unwrap()).unwrap(); + println!("{}", msg_json); + Ok(()) + })?; + + wallet::controller::owner_single_use(wallet2.clone(), |api| { + let (_, tx) = api.retrieve_txs(true, None, Some(slate.id))?; + assert_eq!( + tx[0].clone().messages.unwrap().messages[0].message, + Some(message.to_owned()) + ); + assert_eq!( + tx[0].clone().messages.unwrap().messages[1].message, + Some(sender2_message) + ); + Ok(()) + })?; + // let logging finish thread::sleep(Duration::from_millis(200)); Ok(()) From 062d41766ec67d482da910197d86f8ba32dc1f8f Mon Sep 17 00:00:00 2001 From: hashmap Date: Thu, 31 Jan 2019 13:00:44 +0100 Subject: [PATCH 4/4] Refresh coinbase key if the existing one was removed (#2493) * Refresh coinbase key if the existing was removed It happens if `wallet check` runs in parallel. Alternative solution is not to remove it during the check. Fixes #2412 --- wallet/src/libwallet/error.rs | 2 +- wallet/src/libwallet/internal/updater.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/wallet/src/libwallet/error.rs b/wallet/src/libwallet/error.rs index be108cddb..867e7b63e 100644 --- a/wallet/src/libwallet/error.rs +++ b/wallet/src/libwallet/error.rs @@ -104,7 +104,7 @@ pub enum ErrorKind { CallbackImpl(&'static str), /// Wallet backend error - #[fail(display = "Wallet store error")] + #[fail(display = "Wallet store error: {}", _0)] Backend(String), /// Callback implementation error conversion diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index 678fa9632..b878b7344 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -473,7 +473,10 @@ where let parent_key_id = wallet.parent_key_id(); let key_id = match key_id { - Some(key_id) => keys::retrieve_existing_key(wallet, key_id, None)?.0, + Some(key_id) => match keys::retrieve_existing_key(wallet, key_id, None) { + Ok(k) => k.0, + Err(_) => keys::next_available_key(wallet)?, + }, None => keys::next_available_key(wallet)?, };