From 2ecc2556c693985c14a722654c64fdea5082a341 Mon Sep 17 00:00:00 2001 From: Jonas Hietala Date: Mon, 25 Jan 2021 21:48:45 +0100 Subject: [PATCH] Elixir words --- drafts/bitpal_vision.markdown | 95 -------------------------- drafts/coinparty_retro.markdown | 107 ++++++++++++++++-------------- graphviz/payment-flow.dot | 8 +++ images/coinparty/payment-flow.svg | 73 ++++++++++++++++++++ 4 files changed, 140 insertions(+), 143 deletions(-) delete mode 100644 drafts/bitpal_vision.markdown create mode 100644 graphviz/payment-flow.dot create mode 100644 images/coinparty/payment-flow.svg diff --git a/drafts/bitpal_vision.markdown b/drafts/bitpal_vision.markdown deleted file mode 100644 index c9318ce1..00000000 --- a/drafts/bitpal_vision.markdown +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: "The BitPal vision" -tags: BitPal, Cryptocurrency ---- - -The Coinparty hackathon is over and our submission BitPal was well received, getting a bunch of votes by the community and even earning an honorable mention in the allstars section (sadly missing out on the $8,000 first price!) - -Even though we did make something during the hackathon, that was only the first shaky steps towards what I think a cryptocurrency payment processor should be. And that's what I want to describe with this post. - - -# You should be in control - -The big thing with cryptocurrencies is that *you* should be in full control over your payments and your money. Therefore a payment processor should have these properties: - -* Open-source -* No fees -* No KYC/AML -* Full control over payment verifications (run your own node if you want to) -* Non-custodial (private keys never touch the server) - - -# Configurable - -It's also important that a payment processor can be configured to suit your needs. - -When accepting a cryptocurrency payment you as the merchant can choose how much security you want to have before the payment should be accepted. For smaller payments you might be fine with 0-conf, for larger you might want to wait for one confirmation and for very large payments you might want to wait for 6 or more confirmations. - -There are also different privacy and security levels for how you verify payments: - -1. **Depend on a third-party API** - - This might be fine if you process smaller payments and use a relatively trusted source. - -2. **Rely on SPV security** - - With SPV security to cheat you the cheater must produce valid proof-of-work, which is a very expensive ordeal. - -3. **Use a full node to verify everything yourself** - - Using a full node gives you the maximum level of privacy and security possible. - -I think a payment processor should be able to provide all the different levels. - -The server could provide a public REST api that you could connect to. When you outgrow it you can easily spin up a server of your own and use that access point instead. You could start with SPV security in order to save on server resources and then upgrade to a full node when you feel the need to. - - -# Reliable - -Reliability and high uptime should be a core focus for a payment processor as downtime means payments don't go through and you'll lose customers (and money!). - -This is an area that Erlang (and therefore Elixir) [shines in](https://stackoverflow.com/questions/8426897/erlangs-99-9999999-nine-nines-reliability). Some of the features that Erlang/OTP provides are: - -1. **Hot code reloading** - - You can upgrade a system without stopping anything, vastly cutting down the "down for maintenance" interruptions. - -2. **Compartmentalization** - - Erlang processes (lightweight threads) are isolated from each other and if one part of the system crashes, the rest lives on. Combined with message passing that allows for reliable concurrency and the OTP framework this gives Erlang a very solid base for creating fault-tolerant systems. - -3. **Clustering** - - Message passing between processes works transparently between separate servers, as if they were all running on the same machine. This makes it very easy to split up the service into multiple servers. For example you could run your full node on one server and the payment processor on another or you could mirror the servers and use one as a backup if one goes down or you could run several different full node clients (like Flowee and BCHN) and combine their results. - - -# Extendible - -I think it should be easy to extend the payment processor with the functionality you need. - -With a plugin-focused architecture it should support different cryptocurrencies. The popular ones should be supported by default, and it should be easy to add new ones. This also includes adding things like SLP-tokens, shifting services or maybe Detoken for volatility control. - -I'll note that I wouldn't want to officially support plugins that rely on third-parties, as there's always a risk they'll suddenly add KYC requirements or decide to exit-scam, but it should be possible for third-parties to create them. - - -# Advanced payment protocols - -A payment isn't just sending coins from A to B, but they can include more complex interactions. For instance a Flipstarter pledge or recurring payments (depending on it's implementation). - -If it's something that a merchant would like to accept, then I think a payment processor should be able to support it. - - -# Documentation & integrations - -What sets a software library or service apart isn't always it's features, but how well documented it is and how easy it is to get started. - -That's why it's absolutely essential that a cryptocurrency payment processor is well documented and is easy to start with---no matter the language or framework or server setup you use. You shouldn't have to be an expert programmer to infer the right command-line arguments to use or have an intimate understanding of cryptocurrencies to get started. It should be approachable and simple. - -Examples of how to get started in popular languages (PHP/Ruby/Python/JavaScript/...), frameworks (Laravel/Rails/Django/Node/...) and e-commerce stores (WooCommerce/Shopify/OpenCart/...) is essential and it should be as easy as including some libs or copy-pasting a small code snippet. - - -# How do we get there? - -The problem with this model is there's ... - - diff --git a/drafts/coinparty_retro.markdown b/drafts/coinparty_retro.markdown index 4538d518..f7c200e2 100644 --- a/drafts/coinparty_retro.markdown +++ b/drafts/coinparty_retro.markdown @@ -3,12 +3,11 @@ title: "Jumping into Elixir during a one-week hackathon" tags: Coinparty, Cryptocurrency --- -# The hackathon idea +# The cards stacked against us +I wanted to use Elixir to create a -# Not much experience - * Both of us had other commitments so we couldn't focus on this full-time. * I had some experience with Elixir and Phoenix, but had never made anything real with them. * While we both had a high level understanding of cryptocurrencies, we've never programmed against them. @@ -17,34 +16,25 @@ tags: Coinparty, Cryptocurrency One of the great things about Phoenix is LiveView. It gives you the ability to write websites that updates the page in real-time but without having to write any JavaScript, so all your templates and logic can be handled in the same manner on the server. -This doesn't fully replace JavaScript or frontend frameworks, but you can get very far very quickly. This is something I felt when I implemented the UI transitions for a payment, in the first few hours of the hackathon and without any prior experience with LiveView. - -# Other things I liked - -1. Functional programming and immutability -2. Processes -3. LSP integration with Neovim worked great -4. mix - -# Things I missed +This doesn't fully replace JavaScript or front-end frameworks, but you can get very far very quickly. This is something I felt when I implemented the UI transitions for a payment, in the first few hours of the hackathon and without any prior experience with LiveView. -1. Static typing, and especially enums +I basically wanted to implement a responsive UI for accepting a Bitcoin Cash payment, going through these transitions: -# Phoenix LiveView is great +![The UI transitions. (In the end it's a bit more complicated but this is what I started with.)](/images/coinparty/payment-flow.svg) -One of the big benefits of Phoenix is LiveView: the ability +It doesn't really matter what they mean, just that we should move from one to another when we receive a trigger of some sort. Simulated with timers this is how it looked like: ![](/images/coinparty/payment.gif) +What I started with was trying to render the different states. This could be done with regular Phoenix views, and called dynamically depending on which state you're in: + ```{.elixir} defmodule Demo.PaymentLive do use Demo, :live_view - @required_confirmations 3 - @impl true def mount(_params, _session, socket) do - {:ok, assign(socket, state: :setup, required_confirmations: @required_confirmations)} + {:ok, assign(socket, state: :setup)} end @impl true @@ -52,58 +42,79 @@ defmodule Demo.PaymentLive do template = Atom.to_string(state) <> ".html" render_existing(Demo.PaymentView, template, assigns) end +``` +You begin in state `:setup` and you'll render the `setup.html` template via the `Demo.PaymentView` view. + +After the setup, where you'll enter the email and amount you want to pay, we update the socket with the new state: + +```{.elixir} @impl true - def handle_event("setup", %{"setup" => %{"email" => email, "amount" => amount}}, socket) do - # FIXME ensure email/amount are correct + def handle_event("setup", %{"setup" => %{"email" => email, + "amount" => amount}}, socket) do + address = "bitcoincash:qqpkcce4lzdc8guam5jfys9prfyhr90seqzakyv4tu" + + {:noreply, + assign(socket, + state: :wait_for_tx, # Change state here + email: email, + amount: amount, + address: address)} + end +``` + +And we have a responsive webpage! LiveView will notice we've changed the socket state, render `wait_for_tx.html` and serve the diff to the client. + +All that's left is to simulate the other state transitions, and a simple timer will do: - # FIXME get a new address from payments backend +```{.elixir} + @impl true + def handle_event("setup", %{"setup" => %{"email" => email, + "amount" => amount}}, socket) do address = "bitcoincash:qqpkcce4lzdc8guam5jfys9prfyhr90seqzakyv4tu" # Simulate success after 2s :timer.send_after(2000, self(), :tx_seen) - {:noreply, assign(socket, state: :wait_for_tx, email: email, amount: amount, address: address)} + {:noreply, + assign(socket, + state: :wait_for_tx, + email: email, + amount: amount, + address: address)} end +``` +This will notify `handle_info` (as it's a GenServer), and we can continue our transition from there: + +```{.elixir} @impl true def handle_info(:tx_seen, socket) do - if socket.assigns.required_confirmations == 0 do - # Simulate acceptance after 2s - :timer.send_after(2000, self(), :accepted) - - {:noreply, assign(socket, state: :wait_for_verification)} - else # Simulate a confirmation after 1s :timer.send_after(1000, self(), :new_block) {:noreply, assign(socket, state: :wait_for_confirmations, confirmations: 0)} end end +``` - # FIXME handle double-spend info messages, should goto :denied +And the UI is basically complete! - @impl true - def handle_info(:new_block, socket) do - # FIXME need to confirm that the transaction is in the block, otherwise goto :denied +I managed to figure this out with very little experience with Elixir, Phoenix, LiveView or Genservers, quite quickly, which I think is a testament to how easy it is to work with. - confirmations = socket.assigns.confirmations + 1 - if confirmations >= socket.assigns.required_confirmations do - {:noreply, assign(socket, state: :accepted, confirmations: confirmations)} - else - :timer.send_after(1000, self(), :new_block) - {:noreply, assign(socket, confirmations: confirmations)} - end - end - @impl true - def handle_info(:accepted, socket) do - # FIXME send a confirmation email, if it exists - {:noreply, assign(socket, state: :accepted)} - end -end +# Other things I liked -``` +1. Functional programming and immutability +2. Processes +3. LSP integration with Neovim worked great +4. mix + +# Things I missed + +1. Static typing, and especially enums + +# Closing thoughts diff --git a/graphviz/payment-flow.dot b/graphviz/payment-flow.dot new file mode 100644 index 00000000..d144d14a --- /dev/null +++ b/graphviz/payment-flow.dot @@ -0,0 +1,8 @@ +digraph R { + waiting [label="waiting for transaction"] + confirming [label="waiting for N confirmations"] + verifying [label="verifying 0-conf"] + + setup -> waiting -> {confirming, verifying} -> {accepted} +} + diff --git a/images/coinparty/payment-flow.svg b/images/coinparty/payment-flow.svg new file mode 100644 index 00000000..acae42a5 --- /dev/null +++ b/images/coinparty/payment-flow.svg @@ -0,0 +1,73 @@ + + + + + + +R + + + +waiting + +waiting for transaction + + + +confirming + +waiting for N confirmations + + + +waiting->confirming + + + + + +verifying + +verifying 0-conf + + + +waiting->verifying + + + + + +accepted + +accepted + + + +confirming->accepted + + + + + +verifying->accepted + + + + + +setup + +setup + + + +setup->waiting + + + + +