version 1.0 ready for report M3

This commit is contained in:
Özgür Kesim 2022-10-17 19:02:36 +02:00
parent 8b67a01b4e
commit 8d7a5cb182
22 changed files with 399 additions and 60 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
*.pdf
*.aux
*.log
*.out
*.toc

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,32 +1,39 @@
\documentclass{scrarticle}
\documentclass{scrartcl}
\usepackage[a4paper]{geometry}
\usepackage{hyperref}
\usepackage{xcolor}
\usepackage[dvipsnames]{xcolor}
\hypersetup{
colorlinks = true,
allcolors = {black},
linkcolor = [rgb]{0.6 0.1 0.1},
urlcolor = [rgb]{0.1 0.1 0.7}
linkcolor = DarkOrchid,
urlcolor = DarkOrchid,
}
\usepackage{url}
\usepackage[font=footnotesize]{caption}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{pdfpages}
\usepackage{graphicx}
\usepackage{listings}
\usepackage{fontspec}
\setmonofont[Path = fonts/,
Extension = .ttf,
UprightFont = *-Regular,
ItalicFont = *-Italic,
BoldFont = *-Bold,
Scale=0.85]{RobotoMono}
\lstdefinelanguage{typescript}{
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break, interface},
keywordstyle=\color{purple}\bfseries,
ndkeywords={class, export, boolean, number, Amount, string, Timestamp, RelativeTime, EddsaPublicKey, BrandtVickreyAuction, BrandtVickreyAuctionMessage, BrandtVickreyAuctionWinner, EddsaSignature, HashCode, throw, implements, import, this},
ndkeywordstyle=\color{blue},
keywordstyle=\bfseries,
ndkeywords={class, export, boolean, number, Amount, string, Timestamp, RelativeTime, EddsaPublicKey, BrandtVickreyAuction, BrandtVickreyAuctionMessage, BrandtVickreyAuctionWinner, EddsaSignature, HashCode, throw, implements, import, this, BrandtVickreyReplayOutcome},
ndkeywordstyle=\bfseries,
identifierstyle=\color{black},
sensitive=false,
comment=[l]{//},
morecomment=[s]{/*}{*/},
commentstyle=\color{darkgray}\ttfamily,
stringstyle=\color{red}\ttfamily,
commentstyle=\itshape,
%stringstyle=\color{red},
morestring=[b]',
morestring=[b]"
}
@ -35,7 +42,7 @@
language=typescript,
%backgroundcolor=\color{lightgray},
extendedchars=true,
basicstyle=\footnotesize\ttfamily,
basicstyle=\footnotesize\color{NavyBlue}\ttfamily,
showstringspaces=false,
showspaces=false,
%numbers=left,
@ -44,7 +51,8 @@
tabsize=2,
breaklines=true,
showtabs=false,
captionpos=b
captionpos=b,
emphstyle=\bfseries
}
\begin{document}
@ -70,8 +78,8 @@ The AP³ project presents here the report for the milestone III for NGI Pointer.
The deliverables for this milestone are:
\begin{description}
\item[P2P payments] --
\item[Anonymous auction] --
\item[Anonymous auction] -- PoC for execution of anonymous sealed-bid auctions
\item[P2P payments] -- PoC UI on Android for P2P transactions in the Taler wallet
\end{description}
\end{abstract}
@ -79,48 +87,149 @@ The AP³ project presents here the report for the milestone III for NGI Pointer.
\hfill {\footnotesize Version: 1.0}
\thispagestyle{empty}
\newpage
\newpage
\tableofcontents
\newpage
\section{P2P payments}
\section{Anonymous auctions - PoC}
We departed from the design of a dedicated escrow service in GNU Taler (see
report for milestone 2) and identified a more general approach for conditional
payments---deposits with \textit{policies} attached to them. The design of
\textit{Deposit Policy Extensions} has been drafted in
\href{https://docs.taler.net/design-documents/028-deposit-policies.html}{design
document 28} and is still work-in-progress.
\subsection{Policy extensions framework for GNU Taler}
\label{extension}
The policy-based approach to deposit of coins allows GNU Taler to offer a
variety of condidtional payment types, including: Merchant refunds, escrowed
payments and Brandt-Vickrey auctions.
In Brandt-Vickrey auctions, bidders put coins into escrow with the exchange in
order to participate in an Brandt-Vickrey auction. The deposit confirmation is
proof to the seller for the escrow and contains a hash of the auction meta-data
and a deadline.
After successful execution of the auction, the seller provides a valid
transcript to the exchange from which the exchange learns which bidder(s) won
the auction for which price(s). It then transfers the amounts from the winners
coins to the seller. The coins' (rest) values can be refreshed by winning and
losing bidders.
\begin{quote}
\itshape Note: Support for Brandt-Vickrey type auctions has been
implemented as Proof-of-Concept policy-extension for this milestone.
\end{quote}
A policy can be in one of the following five states of fulfillment:
\begin{itemize}
\item[\itshape Ready:] The policy is funded and ready. The exchange is
waiting for a proof of fulfillment to arrive before the
deadline.
\item[\itshape Insufficient:] The policy lacks funding, that is
\verb|accumulated_total| $<$ \verb|commitment| (see
Fig.~\ref{schema}), but has otherwise been accepted. Funding
can be continued by calling \verb|/deposit| or
\verb|/batch-deposit| with more coins and the same policy
details.
\item[\itshape Success:] The policy is provably fulfilled. The amounts
for the payout, fees and refresh are transferred/can be
claimed. Note that a policy fulfillment handler can change the
values for the amounts for payout, fees and refresh.
\item[\itshape Timeout:] The policy has timed out. The amounts for
payout and refresh are transferred/can be claimed.
\item[\itshape Failure:] The policy is in an failure state. Payouts and
refreshes are blocked, timeouts are ignored.
\end{itemize}
The
\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/exchangedb/exchange-0001-part.sql\#L539-L602}%
{database-schema has been extended} to include the tables
\verb|policy_details|, which contains the details to a particular policy
associated with one or more deposits, and \verb|policy_fulfillments|, which
keeps proofs of fulfillment when they arrive, associated with one or more
policies, see Figure~\ref{schema}.
A new stored-procedure
\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/exchangedb/procedures.sql\#L2190-L2301}%
{\ttfamily insert\_or\_update\_policy\_details} has been added to handle policy
details, including the accumulation of the total amount that has been put into
deposit for an existing policy.
\begin{figure}
\centering
\includegraphics[width=0.95\textwidth]{pics/schema.png}
\caption{Tables and their relationships for policy extensions}
\label{schema}
\end{figure}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The
\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/include/taler\_extensions.h}{extensions-API}
has been extended to include the following function pointers:
\newpage
\section{Anonymous auctions}
\begin{description}
\item[\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/include/taler\_extensions.h\#L167-L185}%
{\ttfamily create\_policy\_details}] When a deposit request
contains a
\href{https://docs.taler.net/core/api-exchange.html#deposit}%
{policy object} that refers to this extension, this handler
will be called before the deposit transaction. The policy
extension is responsible to generate the necessary data to fill
the \verb|policy_details| table and provide a
\verb|policy_hash_code| as unique reference to these details.
\item[\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/include/taler\_extensions.h\#L187-L202}%
{\ttfamily policy\_post\_handler}] Handler for POST-requests to
the \verb|/extensions/policy_$name| endpoint, responsible for
validating incoming proofs of fulfilment of a policy with name
\verb|$name|.
\end{description}
\subsection{Transscript and Replay for libbrandt}
At the beginning of our project, the most recent implementation of
Brandt-Vickrey auctions was from Markus Teich
(\url{https://git.gnunet.org/libbrandt.git/}). In our own fork at
\url{https://git.kesim.org/oec/libbrandt}, we added the following functionality:
When we started our project, the most recent implementation of Brandt-Vickrey
auctions was from Markus Teich (\url{https://git.gnunet.org/libbrandt.git/}).
In our fork at \url{https://git.kesim.org/oec/libbrandt}, we added the
following functionality:
\begin{description}
\item[Transscript generation] The unit-test file
\href{https://git.kesim.org/oec/libbrandt/src/branch/transcript/test\_brandt.c}{test\_brandt.c}
has been extended to generate and print a transscript for each
auction, containing parameters of the auction---such as number
of bidders, prices and auction type---and the list of all
messages that the seller has received during the protocol
execution.
\item[Transscript generation] The existing unit-test file
\href{https://git.kesim.org/oec/libbrandt/src/branch/transcript/test\_brandt.c}{\ttfamily test\_brandt.c}
has been extended to generate and print a transcript in JSON
encoding for each auction, containing parameters of the
auction---such as number of bidders, prices and auction
type---and the list of all messages that the seller has
received during the protocol execution.
The definition of the transcript structure is given in appendix
\ref{transcript}.
\item[Replay of transscript] The new file
\href{https://git.kesim.org/oec/libbrandt/src/branch/transcript/replay.c}{replay.c}
reads a transcript from stdin, parses it and executes an
auction, replaying all messages from the transcript.
\label{replay}
\href{https://git.kesim.org/oec/libbrandt/src/branch/transcript/replay.c}{\ttfamily replay.c}
implements a standalone program that reads a transcript from
\verb|stdin|, parses it and executes an auction, replaying all
messages from the transcript.
On success, it prints a result in JSON form to stdout:
On success, it prints a result in
\href{https://docs.taler.net/design-documents/032-brandt-vickrey-auctions.html#transcripts}{JSON form}
to \verb|stdout|:
\begin{lstlisting}[language=typescript]
interface BrandtVickreyReplayOutcome {
// If the replay of the transcript was successfull, will contain the
// the list of winners and prices.
winner?: BrandtVickreyAuctionWinner[];
// If an error occured during replay, will contain an error message.
error?: string;
}
interface BrandtVickreyAuctionWinner {
// The index of the bidder into the
// `BrandtVickreyAuctionTranscript`.bidder array.
@ -137,48 +246,184 @@ interface BrandtVickreyAuctionWinner {
\end{description}
\subsection{Policy extensions framework for GNUN Taler}
General policy extension framework
\subsection{Brandt-Vickrey-auction extension for GNU Taler}
Extension \verb|policy_brandt_vickrey_auction| added
Based on the policy extensions framework for GNU Taler
(section~\ref{extension}), we implemented the
\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/extensions/policy\_brandt\_vickrey\_auction/policy\_brandt\_vickrey\_auction.c}%
{policy extension \ttfamily policy\_brandt\_vickrey\_auction} to handle
deposits of coins that are put into escrow for the participation in an auction
and the transfer or release of coins after successful proof of fulfillment,
which in this cases involves the replay of a transcript of the corresponding
Brandt-Vickrey auction. In particular, we implemented the following handlers:
\begin{description}
\item[\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/extensions/policy\_brandt\_vickrey\_auction/policy\_brandt\_vickrey\_auction.c\#L767-L821}{\ttfamily auction\_create\_policy\_details}]
This implements the {\ttfamily create\_policy\_details}
function of the policy extension interface and generates a
hash-code for incoming policy in JSON form, by calculating the
hash $H(h_a || p_b)$, where $h_a$ is the hash of the auction
meta data and $p_b$ is the public key of the bidder. Both
parameters are part of the policy structure provided during a
deposit.
\item[\href{https://git.kesim.org/taler/exchange/src/branch/auction\_brandt/src/extensions/policy\_brandt\_vickrey\_auction/policy\_brandt\_vickrey\_auction.c\#L694-L764}{\ttfamily auction\_policy\_post\_handler}]
This implements the {\ttfamily policy\_post\_handler} function
of teh policy extension interface. The POST-handler receives
the transcript and the list of policy-details of former
deposit-operations. It parses the transcript and calls the
external program (sec.~\ref{replay}) to replay the auction and
determine the winners and winning price.
\end{description}
\begin{itemize}
\item \verb|get_policy_details| generates hash-code for
a policy by building the hash $H(h_a || p_b)$,
where $h_a$ is the hash of the auction meta
data and $p_b$ is the public key of the bidder.
Both parameters are part of the policy
structure provided during a deposit.
\item the POST-handler receives the transcript and the
list of policy-details of former
deposit-operations. It parses the transcript
and calls an external program to replay the
auction and determine the winners and winning
price.
\end{itemize}
\subsection{Future work}
\subsubsection{libbrandt}
To a fully functional and maintainable solution, further development is needed
in the following areas:
use libsodium
\begin{description}
make it compatible with current version of GNUNET
\item[libbrandt upgrade] The code base of this library is from 2017 and doesn't
compile with the lates version of
\href{https://git.gnunet.org/gnunet.git}{GNUnet}. It also
uses \href{https://gnupg.org/software/libgcrypt/index.html}%
{\ttfamily libgcrypt} as cryptographic library, which is quite
slow compared to its modern alternative
\href{https://doc.libsodium.org/}{\ttfamily libsodium}, at
least with respect to the primitives used in libbrandt.
\subsubsection{policy framework}
For future work we plan to refactor libbrandt to use libsodium
and the current version of GNUnet.
add escrow policy and merge refund to it
\item[Brandt-Vickrey-Auction continuation] The current code is a
proof-of-concept. The following known problem exist:
\begin{itemize}
\item Signature verification of the transcript and its messages
is missing
\item Unit- and Integrationtests with the exchange are missing
\item A fully featured auction(er) system, including billboard
and client softare, is missing.
\end{itemize}
\subsubsection{brandt-vickrey-auction}
We will address at least the first two issues and plan to
address the lack of an auction system once we find volunteers
and funding for it.
verify signatures of transscript
\item[Policy framework continuation] Based on the current design of policy
extensions for deposit---aka conditional payments---we plan to add
following policy extensions to the GNU Taler exchange:
\begin{itemize}
\item{\itshape Merchant refunds:} Merchant can grant customers refundable
payments. In this case, the amount of the deposit is put into
escrow by the exchange for a certain period until which the
customer can claim a refund.
Right now, this policy is implicit and optional in the usual
deposit-flow. Future work on Taler will lift this into a
policy-extension.
\item{\itshape Escrowed payments:} A trustor puts coins into escrow
with the exchange. It can be claimed by a beneficiary until a
certain deadline, when the claim is signed by
both, the beneficiarys and the trustors keys.
This was the basic idea behind the design we presented in
Milestone 2. It will become a policy-extension in future work.
\end{itemize}
\end{description}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newpage
\section{P2P payments}
After the implementation of Peer-to-Peer payments on the exchange for milestone
2, we continued to implement the UI's for Android and Web extensions, both as
Proof-of-Concepts.
\subsection{Wallet Core}
The core functionality for the P2P payment in the wallet is located at {
\centering \url{https://git.taler.net/wallet-core.git}
}
\noindent under
\href{https://git.taler.net/wallet-core.git/tree/packages/taler-wallet-core/src}{/packages/taler-wallet-core/src},
in particular:
\begin{itemize}
\item Peer-to-peer payment operations:
\hspace{3em}\href{https://git.taler.net/wallet-core.git/tree/packages/taler-wallet-core/src/operations/pay-peer.ts}%
{\ttfamily operations/pay-peer.ts}
\item Database schema with p2p state in it:
\hspace{3em}\href{https://git.taler.net/wallet-core.git/tree/packages/taler-wallet-core/src/db.ts\#n2065}%
{{\ttfamily src/db.ts}, starting at line 2065}
\item Peer-to-peer transactions in the transaction list type declarations:
\hspace{3em}\href{https://git.taler.net/wallet-core.git/tree/packages/taler-util/src/transactions-types.ts#n251}%
{{\ttfamily src/transactions-types.ts}, starting at line 251}
\end{itemize}
\subsection{Android App}
\begin{figure}
\centering
\includegraphics[width=0.24\linewidth]{pics/photo_2022-10-17_11-52-30.jpg}
\includegraphics[width=0.24\linewidth]{pics/photo_2022-10-17_11-52-32.jpg}
\includegraphics[width=0.24\linewidth]{pics/photo_2022-10-17_11-52-33.jpg}
\includegraphics[width=0.24\linewidth]{pics/photo_2022-10-17_11-52-34.jpg}
\caption{Screenshots of the UI for P2P payments in the the Android
version of the GNU Taler wallet}
\label{android}
\end{figure}
The implementation of the UI for P2P on the Android app is located at {
\centering \url{https://git.taler.net/taler-android.git/}
} \noindent under
\href{https://git.taler.net/taler-android.git/tree/wallet/src/main/java/net/taler/wallet/peer}%
{/wallet/src/main/java/net/taler/wallet/peer}. The resulting UI is shown in
the screenshots in Fig.~\ref{android}.
\subsection{Web extensions UI}
WebExtension UI stuff related to peer-to-peer is in repository {
\centering \url{https://git.taler.net/wallet-core.git/}
} \noindent under
\href{https://git.taler.net/wallet-core.git/tree/packages/taler-wallet-webextension/src/cta/}%
{\ttfamily /packages/taler-wallet-webextension/src/cta/}, in directories
\href{https://git.taler.net/wallet-core.git/tree/packages/taler-wallet-webextension/src/cta/InvoiceCreate}%
{\ttfamily InvoiceCreate} and
\href{https://git.taler.net/wallet-core.git/tree/packages/taler-wallet-webextension/src/cta/InvoicePay}%
{\ttfamily InvoicePay}. The resulting UI is shown in the screenshots in Fig.~\ref{web}
\begin{figure}
\centering
\framebox{\includegraphics[width=0.225\textwidth]{pics/Selection_276.png}}
\framebox{\includegraphics[width=0.225\textwidth]{pics/Selection_277.png}}
\framebox{\includegraphics[width=0.225\textwidth]{pics/Selection_278.png}}
\framebox{\includegraphics[width=0.225\textwidth]{pics/Selection_279.png}}
\caption{Screenshots of the P2P payments in the Web extension}
\label{web}
\end{figure}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newpage
\part*{Appendix}
\addcontentsline{toc}{part}{Appendix}

BIN
m3/pics/Selection_276.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
m3/pics/Selection_277.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
m3/pics/Selection_278.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
m3/pics/Selection_279.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 MiB

After

Width:  |  Height:  |  Size: 4.9 MiB

View File

Before

Width:  |  Height:  |  Size: 5.1 MiB

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
m3/pics/schema.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

90
m3/pics/schema.svg Normal file
View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: deposit_policies Pages: 1 -->
<svg width="887pt" height="388pt"
viewBox="0.00 0.00 887.00 388.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 384)">
<title>deposit_policies</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-384 883,-384 883,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_deposits</title>
<polygon fill="none" stroke="black" points="8,-193 8,-372 263,-372 263,-193 8,-193"/>
<text text-anchor="start" x="102" y="-357.8" font-family="monospace" font-weight="bold" font-size="14.00">deposits</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_policy_details</title>
<polygon fill="none" stroke="black" points="304,-78 304,-349 575,-349 575,-78 304,-78"/>
<text text-anchor="start" x="381.5" y="-334.8" font-family="monospace" font-weight="bold" font-size="14.00">policy_details</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_policy_fulfillments</title>
<polygon fill="none" stroke="black" points="616,-8 616,-165 871,-165 871,-8 616,-8"/>
<text text-anchor="start" x="665" y="-150.8" font-family="monospace" font-weight="bold" font-size="14.00">policy_fulfillments</text>
</g>
<!-- deposits -->
<g id="node1" class="node">
<title>deposits</title>
<polygon fill="none" stroke="black" points="28,-213.5 28,-328.5 243,-328.5 243,-213.5 28,-213.5"/>
<text text-anchor="middle" x="135.5" y="-313.3" font-family="monospace" font-size="14.00">...</text>
<polyline fill="none" stroke="black" points="28,-305.5 243,-305.5 "/>
<text text-anchor="start" x="36" y="-290.3" font-family="monospace" font-size="14.00">policy_details_id (null)</text>
<polyline fill="none" stroke="black" points="28,-282.5 243,-282.5 "/>
<text text-anchor="middle" x="135.5" y="-267.3" font-family="monospace" font-size="14.00">...</text>
<polyline fill="none" stroke="black" points="28,-259.5 243,-259.5 "/>
<text text-anchor="start" x="36" y="-244.3" font-family="monospace" font-size="14.00">timestamp</text>
<polyline fill="none" stroke="black" points="28,-236.5 243,-236.5 "/>
<text text-anchor="middle" x="135.5" y="-221.3" font-family="monospace" font-size="14.00">...</text>
</g>
<!-- policy_details -->
<g id="node2" class="node">
<title>policy_details</title>
<polygon fill="none" stroke="black" points="324,-98.5 324,-305.5 555,-305.5 555,-98.5 324,-98.5"/>
<text text-anchor="start" x="332" y="-290.3" font-family="monospace" font-size="14.00">id</text>
<polyline fill="none" stroke="black" points="324,-282.5 555,-282.5 "/>
<text text-anchor="start" x="332" y="-267.3" font-family="monospace" font-size="14.00">policy_hash_code (unique)</text>
<polyline fill="none" stroke="black" points="324,-259.5 555,-259.5 "/>
<text text-anchor="start" x="332" y="-244.3" font-family="monospace" font-size="14.00">deadline</text>
<polyline fill="none" stroke="black" points="324,-236.5 555,-236.5 "/>
<text text-anchor="start" x="332" y="-221.3" font-family="monospace" font-size="14.00">commitment (amount)</text>
<polyline fill="none" stroke="black" points="324,-213.5 555,-213.5 "/>
<text text-anchor="start" x="332" y="-198.3" font-family="monospace" font-size="14.00">accumulated_total (amount)</text>
<polyline fill="none" stroke="black" points="324,-190.5 555,-190.5 "/>
<text text-anchor="start" x="332" y="-175.3" font-family="monospace" font-size="14.00">fee (amount)</text>
<polyline fill="none" stroke="black" points="324,-167.5 555,-167.5 "/>
<text text-anchor="start" x="332" y="-152.3" font-family="monospace" font-size="14.00">transferable (amount)</text>
<polyline fill="none" stroke="black" points="324,-144.5 555,-144.5 "/>
<text text-anchor="start" x="332" y="-129.3" font-family="monospace" font-size="14.00">fulfillment_state</text>
<polyline fill="none" stroke="black" points="324,-121.5 555,-121.5 "/>
<text text-anchor="start" x="332" y="-106.3" font-family="monospace" font-size="14.00">fulfillment_id (null)</text>
</g>
<!-- deposits&#45;&gt;policy_details -->
<g id="edge1" class="edge">
<title>deposits:ref&#45;&gt;policy_details:id</title>
<path fill="none" stroke="black" d="M244.5,-294C244.5,-294 291.16,-294 312.3,-294"/>
<polygon fill="black" stroke="black" points="312.5,-297.5 322.5,-294 312.5,-290.5 312.5,-297.5"/>
<text text-anchor="middle" x="283.5" y="-297.8" font-family="monospace" font-size="14.00">n:1</text>
</g>
<!-- policy_fulfillments -->
<g id="node3" class="node">
<title>policy_fulfillments</title>
<polygon fill="none" stroke="black" points="636,-29 636,-121 851,-121 851,-29 636,-29"/>
<text text-anchor="start" x="644" y="-105.8" font-family="monospace" font-size="14.00">id</text>
<polyline fill="none" stroke="black" points="636,-98 851,-98 "/>
<text text-anchor="start" x="644" y="-82.8" font-family="monospace" font-size="14.00">proof</text>
<polyline fill="none" stroke="black" points="636,-75 851,-75 "/>
<text text-anchor="start" x="644" y="-59.8" font-family="monospace" font-size="14.00">timestamp</text>
<polyline fill="none" stroke="black" points="636,-52 851,-52 "/>
<text text-anchor="start" x="644" y="-36.8" font-family="monospace" font-size="14.00">policy_hash_codes (blob)</text>
</g>
<!-- policy_details&#45;&gt;policy_fulfillments -->
<g id="edge2" class="edge">
<title>policy_details:fid&#45;&gt;policy_fulfillments:id</title>
<path fill="none" stroke="black" d="M556.5,-110C556.5,-110 603.16,-110 624.3,-110"/>
<polygon fill="black" stroke="black" points="624.5,-113.5 634.5,-110 624.5,-106.5 624.5,-113.5"/>
<text text-anchor="middle" x="595.5" y="-113.8" font-family="monospace" font-size="14.00">n:1</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB