diff options
author | Antoine A <> | 2022-02-03 15:57:29 +0100 |
---|---|---|
committer | Antoine A <> | 2022-02-03 15:57:29 +0100 |
commit | 0abc24d093a9208333932c38b377e9e78bb2deaa (patch) | |
tree | c19063508f27539d9a849879fc5dacbc12022e18 | |
parent | cfb5da39f036a68aaeec0a0d3187718fc399b045 (diff) | |
download | depolymerization-0abc24d093a9208333932c38b377e9e78bb2deaa.tar.gz depolymerization-0abc24d093a9208333932c38b377e9e78bb2deaa.tar.bz2 depolymerization-0abc24d093a9208333932c38b377e9e78bb2deaa.zip |
presentation: progress
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | docs/media/fee.png (renamed from docs/fee.png) | bin | 25200 -> 25200 bytes | |||
-rw-r--r-- | docs/media/news0.png | bin | 0 -> 45301 bytes | |||
-rw-r--r-- | docs/media/news1.png | bin | 0 -> 367250 bytes | |||
-rw-r--r-- | docs/media/news2.png | bin | 0 -> 36734 bytes | |||
-rw-r--r-- | docs/media/taler.png (renamed from docs/taler.png) | bin | 24424 -> 24424 bytes | |||
-rw-r--r-- | docs/presentation.tex | 376 |
7 files changed, 221 insertions, 157 deletions
@@ -4,4 +4,4 @@ log /docs/* !/docs/*.docx !/docs/*.tex -!/docs/*.png
\ No newline at end of file +!/docs/media
\ No newline at end of file diff --git a/docs/fee.png b/docs/media/fee.png Binary files differindex 283c286..283c286 100644 --- a/docs/fee.png +++ b/docs/media/fee.png diff --git a/docs/media/news0.png b/docs/media/news0.png Binary files differnew file mode 100644 index 0000000..d1f5ab7 --- /dev/null +++ b/docs/media/news0.png diff --git a/docs/media/news1.png b/docs/media/news1.png Binary files differnew file mode 100644 index 0000000..c567581 --- /dev/null +++ b/docs/media/news1.png diff --git a/docs/media/news2.png b/docs/media/news2.png Binary files differnew file mode 100644 index 0000000..f33c1ba --- /dev/null +++ b/docs/media/news2.png diff --git a/docs/taler.png b/docs/media/taler.png Binary files differindex 503ae20..503ae20 100644 --- a/docs/taler.png +++ b/docs/media/taler.png diff --git a/docs/presentation.tex b/docs/presentation.tex index 0d6cb8b..2e9cd3b 100644 --- a/docs/presentation.tex +++ b/docs/presentation.tex @@ -12,7 +12,7 @@ \author{Antoine d'Aligny} \institute{Bern University of Applied Sciences} \date{\today} -\titlegraphic{\includegraphics[width=2.5cm]{taler.png}} +\titlegraphic{\includegraphics[width=2.5cm]{media/taler.png}} \begin{document} @@ -20,137 +20,209 @@ \titlepage \end{frame} -\begin{frame}{Outline} - \tableofcontents -\end{frame} - -\section{Introduction} - -\begin{frame}{Introduction} +\begin{frame}{Blockchain based cryptocurrencies} + \begin{tikzpicture}[remember picture,overlay] + \node (N1)[above right=5mm and 25mm of current page.center] {\includegraphics[width=34mm]{media/news1.png}}; + \node (N0)[below=-3mm of N1] {\includegraphics[width=34mm]{media/news0.png}}; + \node (N2)[below left=-26mm and -2.5mm of N1] {\includegraphics[width=34mm]{media/news2.png}}; + \end{tikzpicture} \begin{block}{Biggest cryptocurrencies} \begin{itemize} \item \textbf{BTC} Bitcoin \item \textbf{ETH} Ethereum \end{itemize} \end{block} - - \begin{block}{Common blockchain limitation} + \begin{block}{Common blockchain limitations} \begin{itemize} \item \textbf{Delay} block and confirmation delay \item \textbf{Cost} transaction fees \item \textbf{Scalability} limited amount of transaction per second \item \textbf{Ecological impact} computation redundancy \item \textbf{Privacy} + \item \textbf{Regulatory risk} \end{itemize} \end{block} \end{frame} -\section{Taler} - \begin{frame}{Taler}{Architecture} - \begin{center} + \begin{columns} + \column{0.5\paperwidth} \begin{tikzpicture}[ rect/.style={circle, draw=black}, - sym/.style={->, shorten >= 2pt, shorten <= 2pt} + sym/.style={-stealth, shorten >= 2pt, shorten <= 2pt} ] + % Taler payment system \node[rect](1) {Exchange}; - \node[rect, below left=1.5cm and 0.7cm of 1](2) {Customer}; - \node[rect, below right=1.5cm and 0.7cm of 1](3) {Merchant}; + \node[rect,below left=1.5cm and 0.7cm of 1](2) {Customer}; + \node[rect,below right=1.5cm and 0.7cm of 1](3) {Merchant}; \draw[sym] (1) -- node [midway, above, sloped] {\tiny Withdraw coins} (2); \draw[sym] (2) -- node [midway, above, sloped] {\tiny Spend coins} (3); \draw[sym] (3) -- node [midway, above, sloped] {\tiny Deposit coins} (1); + % Settlement layer \node[left=2cm of 1](E1){}; \node[right=2cm of 1](E2){}; \draw[sym] (E1) -- node [midway, above] {\tiny Deposit money} (1); \draw[sym] (1) -- node [midway, above] {\tiny Withdraw money} (E2); - + % Auditor \node[above= of 1](A){Auditor}; \draw[sym] (A) -- node [midway, right] {\tiny Verify} (1); - \end{tikzpicture} - \end{center} -\end{frame} -\begin{frame}{Taler}{Coins - WIP} - \begin{itemize} - \item Blind signatures - \item Currency agnostic - \item Change signature every year, Forget previous transactions (Pruning) - \item Compatibility ? - \end{itemize} + % Separator + \node[below=1mm of E1] (S1S) {}; + \node[below=1mm of E2] (S1E) {}; + \node[above=6mm of E1] (S2S) {}; + \node[above=6mm of E2] (S2E) {}; + + \draw[dotted] (S1S) -- (S1E); + \draw[dotted] (S2S) -- (S2E); + + \node[below right=-2mm and -1.5mm of S2S] {\tiny{\emph{Settlement Layer}}}; + \node[below right=-2mm and -1.5mm of S1S] {\tiny{\emph{Taler payment system}}}; + \end{tikzpicture} + \column{0.47\paperwidth} + \begin{block}{Settlement layer} + \begin{itemize} + \item Blockchain? + \end{itemize} + \end{block} + \begin{block}{Taler payment system} + \begin{itemize} + \item Blind signatures (privacy) + \item Rotate keys every year. Forget previous transactions (Pruning) + \end{itemize} + \end{block} + + \end{columns} \end{frame} -\begin{frame}{Taler}{Off-chain transactions} +\begin{frame}{Taler}{Blockchain settlement layer} \begin{center} \begin{tikzpicture}[ rect/.style={rectangle, draw=black, minimum width=28mm}, - sym/.style={<->, shorten >= 2pt, shorten <= 2pt}, + sym/.style={stealth-stealth, shorten >= 2pt, shorten <= 2pt}, block/.style={rectangle,draw=black,fill=black!10,minimum size=7mm}, ] %% Architecture - \node(Tt){Taler}; - \node[rect, below=0cm of Tt](Tc){Exchange}; - \node[rect, fit={(Tt) (Tc)}](T){}; + \node[rect,below=0cm of Tt](Tc){Exchange}; + \node[rect,fit={(Tt) (Tc)}](T){}; - \node[rect, below=7mm of Tc](D) {Depolymerization}; + \node[rect,below=7mm of Tc](D) {Depolymerization}; - \node[rect, below=7mm of D](Bc){Node}; + \node[rect,below=7mm of D](Bc){Node}; \node[below=0cm of Bc](Bt){Blockchain}; - \node[rect, fit={(Bt) (Bc)}](B){}; + \node[rect,fit={(Bt) (Bc)}](B){}; \draw[sym] (T) -- (D); \draw[sym] (D) -- (B); %% Blockchain - \node[block,right=7mm of B] (1){}; + \node[block,right=8mm of B] (1){}; \node[block,right=4mm of 1] (2){}; \node[block,right=4mm of 2] (3){}; \node[block,right=4mm of 3] (4){}; \node[block,right=4mm of 4] (5){}; \node[block,right=4mm of 5] (6){}; - \draw[->] (1) -- (2); - \draw[->] (2) -- (3); - \draw[->] (3) -- (4); - \draw[->] (4) -- (5); - \draw[->] (5) -- (6); + \draw[-stealth] (1) -- (2); + \draw[-stealth] (2) -- (3); + \draw[-stealth] (3) -- (4); + \draw[-stealth] (4) -- (5); + \draw[-stealth] (5) -- (6); - %% Taler - \node[block, right=18mm of T] (off){Off-chain transactions}; + \node[left=4mm of 1] (S){}; + \node[right=4mm of 6] (E){}; + \draw[-stealth] (S) -- (1); + \draw[-stealth] (6) -- (E); - % Depolymerization - \node[right=11mm of D] {\small{Deposit}}; - \node[right=49mm of D] {\small{Withdraw}}; - \draw[dashed,->] (1.north) |- (off.west); - \draw[dashed,->] (off.east) -| (6.north); + %% Taler + \node[block, below right=-7.5mm and 20.5mm of T] (off){Off-chain transactions}; + \node[above=-0.5mm of off] {\includegraphics[height=7mm]{media/taler.png}}; + + %% Depolymerization + \node[right=12mm of D] {\small{Deposit}}; + \node[right=50mm of D] {\small{Withdraw}}; + \draw[dashed,-stealth] (1.north) |- (off.west); + \draw[dashed,-stealth] (off.east) -| (6.north); \end{tikzpicture} \end{center} \end{frame} -\section{Depolymerization} +\begin{frame}{Challenges} + \begin{block}{Taler Metadata} + \begin{itemize} + \item Metadata are required to link a wallet to its deposits and withdraws + \item Putting metadata in blockchain transactions can be tricky + \item The whole on-chain transaction history can be retrieved from + the blockchain. \textbf{Easily auditable} + \end{itemize} + \end{block} + \begin{block}{Blockchain based cryptocurrencies} + \begin{itemize} + \item Reorganisation resilient + \item Adaptive confirmation + \item Resolve stuck transactions + \end{itemize} + \end{block} +\end{frame} -\begin{frame}{Depolymerization}{Metadata} - \begin{itemize} - \item Metadata are stored in depolymerizer on-chain transactions - \item The whole on-chain transaction history can be retrieved from the blockchain - \item \textbf{Easily auditable} - \end{itemize} +\begin{frame}{Challenges}{Chain reorganisation} + \begin{center} + \begin{tikzpicture}[ + block/.style={rectangle,draw=black,fill=black!10,minimum size=7mm}, + ar/.style={-stealth} + ] + % Common + \node[block](1){}; + \node[block,right=5mm of 1](2){$D_0$}; + \node[block,right=5mm of 2](3){}; + \draw[ar] (1) -- (2); + \draw[ar] (2) -- (3); + + % Current + \node [block,right=5mm of 3](4){}; + \node[block,right=5mm of 4](5){}; + \node[block,right=5mm of 5](6){$D_1$}; + \draw[ar] (3) -- (4); + \draw[ar] (4) -- (5); + \draw[ar] (5) -- (6); + + % Fork + \node [block,above=7mm of 4](4p){}; + \node[block,right=5mm of 4p](5p){$D_2$}; + \node[block,right=5mm of 5p](6p){}; + \node[block,right=5mm of 6p](7p){}; + \draw[ar] (3.east) -- (4p.west); + \draw[ar] (4p) -- (5p); + \draw[ar] (5p) -- (6p); + \draw[ar] (6p) -- (7p); + + % Indication + \node [right=5mm of 7p]{\emph{fork}}; + \node [right=17mm of 6]{\emph{active}}; + \end{tikzpicture} + \end{center} + A fork is when concurrent blockchain states coexist. Nodes will follow + the longest chain, replacing recent blocks if necessary. That is a + blockchain reorganisation. Taler expects deposit transactions to be + consistent. If a deposit transaction disappears from the blockchain, + depolymerizer is comprised. \end{frame} \begin{frame}{Depolymerization}{Architecture} \begin{center} \begin{tikzpicture}[ rect/.style={rectangle, draw=black, minimum height=6mm, minimum width=28mm}, - sym/.style={<->, shorten >= 2pt, shorten <= 2pt} + sym/.style={stealth-stealth, shorten >= 2pt, shorten <= 2pt} ] \node[rect](1) {Taler Exchange}; - \node[rect,below=of 1](2) {wire\_gateway}; + \node[rect,below=of 1](2) {Wire Gateway}; \node[rect,right=of 2](3) {PostgreSQL}; - \node[rect,right=of 3](4) {\#\#\#\_wire}; - \node[rect,above=of 4](5) {Full Node}; + \node[rect,right=of 3](4) {DLT Wire}; + \node[rect,above=of 4](5) {DLT Full Node}; \draw[sym] (1) -- node [midway,right] {\tiny HTTP} (2); \draw[sym] (2) -- node [midway,above] {\tiny SQL} (3); @@ -158,16 +230,45 @@ \draw[sym] (4) -- node [midway,left ] {\tiny RPC} (5); + \node[above= 2mm of 1]{\small{\emph{Wire Gateway API}}}; + \node[above= 2mm of 5]{\small{\emph{DLT specific}}}; + \node[above=22mm of 3](T) {}; + \draw[dotted] (3) -- (T); \end{tikzpicture} \end{center} - Two processes sharing a common database for state and communication. - wire\_gateway is currency agnostic and there is a specific wire binary for - every supported currency. + \begin{itemize} + \item Common database to store transactions state and communicate + with notifications + \item Wire Gateway for Taler API compatibility + \item Specific wire for DLT compatibility + \end{itemize} \end{frame} -\section{btc-wire} +\begin{frame}{Depolymerization}{Metadata} + \begin{block}{Bitcoin - Withdraw} + \begin{itemize} + \item Transaction from code + \item Only 32B + URI + \item \textbf{OP\_RETURN} + \end{itemize} + \end{block} + \begin{block}{Bitcoin - Deposit} + \begin{itemize} + \item Transaction from common wallet software + \item Only 32B + \item \textbf{Fake Segwit Addresses} + \end{itemize} + \end{block} + \begin{block}{Ethereum - Withdraw and Deposit} + \begin{itemize} + \item Smart contract is the recommend way + \item Expensive and error prone (bigger attack surface) + \item \textbf{Custom contract input format} + \end{itemize} + \end{block} +\end{frame} -\begin{frame}{btc-wire}{Architecture} +\begin{frame}{Depolymerization}{Architecture} \begin{center} \begin{tikzpicture}[ rect/.style={rectangle, draw=black, minimum height=6mm, minimum width=38mm}, @@ -177,15 +278,15 @@ \node(wat) {Watcher}; \node[rect, below=1mm of wat](wa1) {Wait for new block}; \node[rect, below=4mm of wa1](wa2) {Notify new block}; - \draw[->] (wa1) -- (wa2); - \draw[->] (wa2) .. controls ([xshift=-0.3cm] wa2.west) and ([xshift=-0.3cm] wa1.west) .. (wa1); + \draw[-stealth] (wa1) -- (wa2); + \draw[-stealth] (wa2) .. controls ([xshift=-0.3cm] wa2.west) and ([xshift=-0.3cm] wa1.west) .. (wa1); % Analysis loop \node[below=5mm of wa2] (at) {Analysis}; \node[rect, below=1mm of at](a1) {Wait for notification}; \node[rect, below=4mm of a1](a2) {Analyse}; - \draw[->] (a1) -- (a2); - \draw[->] (a2) .. controls ([xshift=-0.3cm] a2.west) and ([xshift=-0.3cm] a1.west) .. (a1); + \draw[-stealth] (a1) -- (a2); + \draw[-stealth] (a2) .. controls ([xshift=-0.3cm] a2.west) and ([xshift=-0.3cm] a1.west) .. (a1); % Worker loop \node[rect, below right= -2mm and 1cm of wa1](wo1) {Wait for notification}; @@ -193,134 +294,98 @@ \node[rect, below=4mm of wo2](wo3) {Send}; \node[rect, below=4mm of wo3](wo4) {Bounce}; \node[above=1mm of wo1]{Worker}; - \draw[->] (wo1) -- (wo2); - \draw[->] (wo2) -- (wo3); - \draw[->] (wo3) -- (wo4); - \draw[->] (wo4) .. controls ([xshift=-0.4cm] wo4.west) and ([xshift=-0.4cm] wo1.west) .. (wo1); + \draw[-stealth] (wo1) -- (wo2); + \draw[-stealth] (wo2) -- (wo3); + \draw[-stealth] (wo3) -- (wo4); + \draw[-stealth] (wo4) .. controls ([xshift=-0.4cm] wo4.west) and ([xshift=-0.4cm] wo1.west) .. (wo1); \end{tikzpicture} \end{center} \centering Three concurrent loops \end{frame} -\begin{frame}{btc-wire}{Metadata} - \begin{block}{Withdraw} - \begin{itemize} - \item Transaction from code - \item Only 32B + URI - \item \textbf{OP\_RETURN} - \end{itemize} - \end{block} - \begin{block}{Deposit} - \begin{itemize} - \item Transaction from common wallet software - \item Only 32B - \item \textbf{Fake Segwit Addresses} - \end{itemize} - \end{block} -\end{frame} - - -\begin{frame}{btc-wire}{Security features} - \begin{itemize} - \item Reorg resilient - \item Adaptive confirmation - \item Resolve stuck transaction - \end{itemize} -\end{frame} - -\begin{frame}{btc-wire}{Reorg resilient} +\begin{frame}{Reorganisation resilient} \begin{center} \begin{tikzpicture}[ block/.style={rectangle,draw=black,fill=black!10,minimum size=7mm}, conf/.style={draw=black!60!green,fill=black!60!green!10}, err/.style={draw=black!60!red,fill=black!60!red!10}, + ar/.style={-stealth} ] % Common - \only<1>{ - \node [block](1){}; - \node [block,right=5mm of 1](2){$D_0$}; - \node [block,right=5mm of 2](3){}; - } - \only<2->{ - \node [block,conf](1){}; - \node [block,conf,right=5mm of 1](2){$D_0$}; - \node [block,conf,right=5mm of 2](3){}; - } - \draw[->] (1) -- (2); - \draw[->] (2) -- (3); + \node[block,conf](1){}; + \node[block,conf,right=5mm of 1](2){$D_0$}; + \node[block,conf,right=5mm of 2](3){}; + \draw[ar] (1) -- (2); + \draw[ar] (2) -- (3); % Current - \only<1-2>{ + \only<1>{ \node [block,right=5mm of 3](4){}; } - \only<3->{ - \node [block,conf,right=5mm of 3](4){\only<4>{$D_3$}}; + \only<2->{ + \node [block,conf,right=5mm of 3](4){\only<3>{$D_3$}}; } - \node [block,right=5mm of 4](5){}; - \node [block,right=5mm of 5](6){$D_1$}; - \draw[->] (3) -- (4); - \draw[->] (4) -- (5); - \draw[->] (5) -- (6); + \node[block,right=5mm of 4](5){}; + \node[block,right=5mm of 5](6){$D_1$}; + \draw[ar] (3) -- (4); + \draw[ar] (4) -- (5); + \draw[ar] (5) -- (6); % Fork - \only<-3>{ + \only<-2>{ \node [block,above=7mm of 4](4p){}; } - \only<4>{ + \only<3>{ \node [block,err,above=7mm of 4](4p){$D_3'$}; } - \node [block,right=5mm of 4p](5p){$D_2$}; - \node [block,right=5mm of 5p](6p){}; - \node [block,right=5mm of 6p](7p){}; - \draw[->] (3.east) -- (4p.west); - \draw[->] (4p) -- (5p); - \draw[->] (5p) -- (6p); - \draw[->] (6p) -- (7p); + \node[block,right=5mm of 4p](5p){$D_2$}; + \node[block,right=5mm of 5p](6p){}; + \node[block,right=5mm of 6p](7p){}; + \draw[ar] (3.east) -- (4p.west); + \draw[ar] (4p) -- (5p); + \draw[ar] (5p) -- (6p); + \draw[ar] (6p) -- (7p); % Indication \node [right=5mm of 7p]{\emph{fork}}; \node [right=17mm of 6]{\emph{active}}; \end{tikzpicture} \end{center} - \only<1>{A fork is when concurrent blockchain states coexist. Nodes will - follow the longest chain, replacing recent blocks if necessary. That is a - blockchain reorganization. Taler expects deposit transactions to be - consistent. If a deposit transaction disappears from the blockchain btc-wire - is comprised.} - \only<2>{As small forks are common, we apply a confirmation delay to - handle the most common disturbances and attacks.} - \only<3>{If a reorganization longer than the confirmation delay happens, + \only<1>{As small reorganisations are common, we apply a confirmation delay + to handle the most common disturbances and attacks.} + \only<2>{If a reorganisation longer than the confirmation delay happens, but it does not remove deposits, btc-wire is safe.} - \only<4>{If it removed a confirmed deposit a powerful attacker may have + \only<3>{If it removed a confirmed deposit a powerful attacker may have created a conflicting transaction. btc-wire suspends operation until lost deposits reappear.} \end{frame} -\begin{frame}{btc-wire}{Adaptive confirmation} +\begin{frame}{Adaptive confirmation} \begin{center} \begin{tikzpicture}[ block/.style={rectangle,draw=black,fill=black!10,minimum size=7mm}, conf/.style={draw=black!60!green,fill=black!60!green!10}, conft/.style={text=black!60!green}, confl/.style={draw=black!60!green}, + ar/.style={-stealth} ] % Common \node(0){}; \node[block,conf,right=5mm of 0](1){}; \node[block,conf,right=5mm of 1](2){}; - \draw[->] (0) -- (1); - \draw[->] (1) -- (2); + \draw[ar] (0) -- (1); + \draw[ar] (1) -- (2); % Current \node[block,conf,right=5mm of 2](3){}; \node[block,right=5mm of 3](4){}; \node[block,right=5mm of 4](5){}; \node[block,right=5mm of 5](6){}; - \draw[->] (2) -- (3); - \draw[->] (3) -- (4); - \draw[->] (4) -- (5); - \draw[->] (5) -- (6); + \draw[ar] (2) -- (3); + \draw[ar] (3) -- (4); + \draw[ar] (4) -- (5); + \draw[ar] (5) -- (6); % Fork \node[block,above=7mm of 3](3p){}; @@ -328,11 +393,11 @@ \node[block,right=5mm of 4p](5p){}; \node[block,right=5mm of 5p](6p){}; \node[block,right=5mm of 6p](7p){}; - \draw[->] (2.east) -- (3p.west); - \draw[->] (3p) -- (4p); - \draw[->] (4p) -- (5p); - \draw[->] (5p) -- (6p); - \draw[->] (6p) -- (7p); + \draw[ar] (2.east) -- (3p.west); + \draw[ar] (3p) -- (4p); + \draw[ar] (4p) -- (5p); + \draw[ar] (5p) -- (6p); + \draw[ar] (6p) -- (7p); % Indication \node[right=5mm of 7p]{\emph{fork}}; @@ -351,12 +416,11 @@ \end{tikzpicture} \end{center} If we experience a reorganisation once, its plausible for another one of the - same size to happen again. btc-wire learns from reorganizations by updating + same size to happen again. btc-wire learns from reorganisations by updating its confirmation time. \end{frame} - -\begin{frame}{btc-wire}{Handle stuck transactions} +\begin{frame}{Handle stuck transactions} \begin{center} \begin{tikzpicture}[ dot/.style={circle,fill,inner sep=1pt,} @@ -364,7 +428,7 @@ % TODO caption with source (Ychart) - \node (I) {\includegraphics[width=\textwidth]{fee.png}}; + \node (I) {\includegraphics[width=\textwidth]{media/fee.png}}; \only<2->{ \node [below left=-2.5mm and -1.5cm of I] (Tx) {\small Tx}; \node [dot,above=8.4mm of Tx](D) {}; @@ -415,12 +479,12 @@ \begin{frame}{Conclusion} \begin{itemize} \item [$-$] Trust exchange operator or auditors - \item [$+$] Fast and cheap, no blockchain + \item [$+$] Fast and cheap \item [$+$] Realtime, ms latency - \item [$+$] Scalability, close to 100k txs per second + \item [$+$] Linear scalability \item [$+$] Ecological \item [$+$] Privacy when it can, transparency when it must (avoid tax evasion and money laundering) - \item [$+$] Currency agnostic (payment system) + \item [$+$] Compatibility with blockchains \end{itemize} \end{frame} \end{document}
\ No newline at end of file |