<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rafael Viscarra</title>
    <description>The latest articles on DEV Community by Rafael Viscarra (@rviscarra).</description>
    <link>https://dev.to/rviscarra</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F209429%2Fd65a164f-6d4e-4959-b6f4-69fb09abfbae.jpeg</url>
      <title>DEV Community: Rafael Viscarra</title>
      <link>https://dev.to/rviscarra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rviscarra"/>
    <language>en</language>
    <item>
      <title>Accepting Bitcoin payments with Python, Rust and PyO3</title>
      <dc:creator>Rafael Viscarra</dc:creator>
      <pubDate>Tue, 23 Jul 2024 03:00:48 +0000</pubDate>
      <link>https://dev.to/rviscarra/accepting-bitcoin-payments-with-python-rust-and-pyo3-3961</link>
      <guid>https://dev.to/rviscarra/accepting-bitcoin-payments-with-python-rust-and-pyo3-3961</guid>
      <description>&lt;h3&gt;
  
  
  What to expect from this blog post
&lt;/h3&gt;

&lt;p&gt;This blog post is meant to be an introduction to &lt;a href="https://pyo3.rs" rel="noopener noreferrer"&gt;PyO3&lt;/a&gt; by walking the reader through the build &lt;br&gt;
process of a non-trivial extension module in Rust using PyO3. Some familiarity with Python and Rust is recommended to &lt;br&gt;
get the most out of this post, basic understanding of Bitcoin concepts may be required to fully grasp the code samples.&lt;/p&gt;
&lt;h4&gt;
  
  
  Sample application
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://github.com/rviscarra/pyo3-psbt" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; exposes a simple UI that allows the user to submit a Bitcoin payment by signing (and optionally broadcasting) a Partially Signed Bitcoin Transaction (PSBT) that was built by the application’s backend. The code has been tested in Chrome using the XVerse Wallet extension, by default the application is configured to use Bitcoin’s Testnet3 chain. The application’s code can be split into three components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend code that handles user input and wallet interaction, implemented in vanilla Javascript.&lt;/li&gt;
&lt;li&gt;Python backend code that takes care of the inbound HTTP request and the outbound API request to mempool.space.&lt;/li&gt;
&lt;li&gt;Rust code that relies on the bitcoin crate to build the PSBT.
The following diagram shows how the data flows across said components:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8z77exaddoyuvky1ads.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8z77exaddoyuvky1ads.png" alt="Data flow across components" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Bootstrapping the extension module
&lt;/h3&gt;

&lt;p&gt;Before we can write a single line of code, we need to figure out a way to build our module, and for that we’ll use &lt;a href="https://github.com/PyO3/maturin" rel="noopener noreferrer"&gt;Maturin&lt;/a&gt;, which is a tool that allows us to build (and publish) Rust-based Python packages. Maturin also includes a useful boilerplate generation sub-command that will create everything we need to compile Rust code into a Python module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate our module skeleton and creatively name it "btc"&lt;/span&gt;
&lt;span class="c"&gt;# We passed the --mixed flag because we want a hybrid (Rust &amp;amp; Python) project so we can add type hints later&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;maturin new &lt;span class="nt"&gt;--bindings&lt;/span&gt; pyo3 &lt;span class="nt"&gt;--mixed&lt;/span&gt; btc
&lt;span class="c"&gt;# Let's check what was generated&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;tree
&lt;span class="nb"&gt;.&lt;/span&gt;
├── Cargo.lock
├── Cargo.toml
├── pyproject.toml
├── python
│   ├── btc
│   │   └── __init__.py
│   └── tests
│       └── test_all.py
└── src
    └── lib.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see Maturin generated a Rust project with some source samples, since we indicated that we wanted to use PyO3 &lt;br&gt;
bindings the &lt;code&gt;pyo3&lt;/code&gt; crate has been included as a dependency, all that is left is to install the &lt;code&gt;bitcoin&lt;/code&gt; crate with &lt;br&gt;
&lt;code&gt;cargo add bitcoin@^0.32.0&lt;/code&gt; and replace the content of the &lt;code&gt;btc/src/lib.rs&lt;/code&gt; file with the following code sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;str&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FromStr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pyo3&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PyValueError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;bitcoin&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The following comment will go into the function's docstring&lt;/span&gt;
&lt;span class="cd"&gt;/// get_address_type returns the provided address' type as a string&lt;/span&gt;
&lt;span class="nd"&gt;#[pyfunction]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;PyValueError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid address"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
        &lt;span class="nf"&gt;.assume_checked&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;address_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="nf"&gt;.address_type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"other"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// A Bitcoin helper module implemented in Rust&lt;/span&gt;
&lt;span class="nd"&gt;#[pymodule]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;btc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PyModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="nf"&gt;.add_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;wrap_pyfunction!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PyO3 is doing some heavylifting for us, these are some notable examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;pyfunction&lt;/code&gt; macro allows us to expose Rust functions to Python.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PyResult&amp;lt;T&amp;gt;&lt;/code&gt; is an alias for &lt;code&gt;Result&amp;lt;T, PyErr&amp;gt;&lt;/code&gt;, a common pattern used across the stdlib and many libraries, if we 
return the &lt;code&gt;Err(...)&lt;/code&gt; variant it will raise an exception in Python, which is what we're doing when the provided address 
cannot be parsed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;pymodule&lt;/code&gt; macro allows us expose a Python module by declaring a function that will "build" our module by adding 
our functions, classes and submodules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, implementing a Python extension module is straightforward thanks to PyO3's types transformations and macros &lt;br&gt;
for exposing functions, classes and modules, this allows experienced Rust developers to write idiomatic code by making &lt;br&gt;
use of standard Rust types, abstractions and patterns.&lt;/p&gt;
&lt;h3&gt;
  
  
  Building and testing our code
&lt;/h3&gt;

&lt;p&gt;We have some functional code so let's give it a quick test by building it and starting a Python interpreter to &lt;br&gt;
import and smoke test the module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This will build and install our module in our virtualenv&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;maturin develop
🍹 Building a mixed python/rust project
🔗 Found pyo3 bindings
&lt;span class="c"&gt;# ... output omitted for brevity ...&lt;/span&gt;
🛠 Installed btc-0.1.0

&lt;span class="c"&gt;# Now spin up a Python interpreter to execute our new native function&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; import btc
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; btc.get_address_type&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bcrt1qg3gmqfdwgteve988hvps7kws2kdzagtkqf6gu0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;'p2wpkh'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even write a simple Python unit test in &lt;code&gt;btc/python/tests/test_all.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;btc&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_address_type&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;btc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bcrt1qg3gmqfdwgteve988hvps7kws2kdzagtkqf6gu0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p2wpkh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or a Rust one in &lt;code&gt;btc/src/lib.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_get_address_type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bcrt1qg3gmqfdwgteve988hvps7kws2kdzagtkqf6gu0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"p2wpkh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we're able to build and run our extension module, we're ready to tackle the actual functionality that we want &lt;br&gt;
to build, a PSBT that allows us to charge our user an arbitrary amount of Bitcoin.&lt;/p&gt;
&lt;h3&gt;
  
  
  Brief introduction to PSBTs
&lt;/h3&gt;

&lt;p&gt;At a very high level, a PSBT is a serialization format that allows a set of actors (i.e. users, services, devices) to &lt;br&gt;
build and sign a Bitcoin transaction incrementally. PSBTs are particularly useful when the transaction being built &lt;br&gt;
requires UTXOs&lt;sup id="fnref1"&gt;1&lt;/sup&gt; owned by different parties.&lt;/p&gt;

&lt;p&gt;For demostration purposes, we'll keep our transaction architecture relatively simple. We'll charge the user a flat fee &lt;br&gt;
of 1000 satoshis, for this we'll select one UTXO with enough sats to cover that amount and the miner fee, the &lt;br&gt;
difference will be returned to the user's payment address. The following diagram portrays our desired transaction:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feab97r9ik3cxfa2xod1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feab97r9ik3cxfa2xod1v.png" alt="TX architecture" width="572" height="396"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Building a simple PSBT with Rust
&lt;/h3&gt;

&lt;p&gt;I've found that a good starting point while designing an API is to think about how myself or other engineers may want &lt;br&gt;
to use said API, this is also a good time to consider the limitations of our platform/system. It's also a good idea to &lt;br&gt;
define some boundaries and/or non-functional concerns for our code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python code should be able to determine the transaction's inputs and outputs.&lt;/li&gt;
&lt;li&gt;Rust shouldn't handle IO.&lt;/li&gt;
&lt;li&gt;We should be able to serialize the PSBT as &lt;code&gt;bytes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We need to be able to estimate the transaction vsize since we'll use it to estimate the miner's fee.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that in mind, we can scribble some Python code that utilizes our soon-to-be &lt;code&gt;PsbtBuilder&lt;/code&gt; abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;btc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PsbtBuilder&lt;/span&gt;

&lt;span class="n"&gt;AMOUNT_TO_CHARGE_SATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_payment_psbt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_utxo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Utxo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;miner_fee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;total_charge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;miner_fee&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;AMOUNT_TO_CHARGE_SATS&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user_utxo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;total_charge&lt;/span&gt;

    &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PsbtBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_utxo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;our_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AMOUNT_TO_CHARGE_SATS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;change_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_utxo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;total_charge&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;change_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# realistically, we may want to verify that the amount is greater than the dust value
&lt;/span&gt;        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;change_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may have noticed, we'll use the &lt;a href="https://refactoring.guru/design-patterns/builder" rel="noopener noreferrer"&gt;builder pattern&lt;/a&gt; to &lt;br&gt;
incrementally build our PSBT by adding inputs and outputs, we also need a way to estimate the miner fee based on the &lt;br&gt;
user provided fee rate in sats/vByte. Let's start implementing our abstraction by declaring our &lt;code&gt;PsbtBuilder&lt;/code&gt; struct &lt;br&gt;
that we'll expose as a Python class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PsbtBuilderIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;prevout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InputUtxo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;owner_pub_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PublicKey&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[pyclass]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PsbtBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PsbtBuilderIn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TxOut&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using the &lt;code&gt;pyclass&lt;/code&gt; macro to generate the required boilerplate to register our struct as a Python class, we &lt;br&gt;
also implemented a &lt;code&gt;PsbtBuilderIn&lt;/code&gt; struct that we'll use internally. There needs to be a way to instantiate and &lt;br&gt;
initialize our new struct/PyClass, let's declare a simple constructor function/method with PyO3's &lt;code&gt;#[new]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[pymethods]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PsbtBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;#[new]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nn"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;PyValueError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// other methods&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;#[pymethods]&lt;/code&gt; makes PyO3 expose the methods inside the &lt;code&gt;impl&lt;/code&gt; block as methods of our PyClass, &lt;code&gt;#[new]&lt;/code&gt; indicates which &lt;br&gt;
method will be executed when our PyClass is instantiated. Let's see how &lt;code&gt;add_input&lt;/code&gt; is implemented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[pymethods]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PsbtBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// other methods&lt;/span&gt;

    &lt;span class="cd"&gt;/// Add a new input to the PSBT, owner_pub_key is required for p2sh addresses&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;add_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;utxo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InputUtxo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;owner_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;owner_pub_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PyBytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;owner_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;address_from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;owner_pub_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_pub_key&lt;/span&gt;
            &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="nf"&gt;.transpose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;PyValueError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid public key"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inputs&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PsbtBuilderIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;prevout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;utxo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;owner_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;owner_pub_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// other methods&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two interesting things are happening in &lt;code&gt;add_input&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The method receives an instance of a class declared in Python, this is possible because &lt;code&gt;InputUtxo&lt;/code&gt; implements the 
&lt;code&gt;FromPyObject&lt;/code&gt; trait.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;owner_pub_key&lt;/code&gt; argument is optional, we are able to achieve this by declaring it as &lt;code&gt;Option&amp;lt;&amp;amp;PyBytes&amp;gt;&lt;/code&gt;. If we 
omit it when we call the method in Python, Rust will receive the &lt;code&gt;None&lt;/code&gt; variant of the &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; enum.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now it's a good time to review how &lt;code&gt;InputUtxo&lt;/code&gt; is implemented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;InputUtxo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;tx_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Txid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FromPyObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;InputUtxo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt; &lt;span class="nn"&gt;pyo3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PyAny&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tx_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  
      &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tx_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
      &lt;span class="nf"&gt;.extract&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Txid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
      &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;PyValueError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid tx hash"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
      &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
      &lt;span class="nf"&gt;.extract&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from_sat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;vout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"vout"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;.extract&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;tx_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// other impl for InputUtxo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As mentioned before, implementing the &lt;code&gt;FromPyObject&lt;/code&gt; object allows us to customize how a Python value will "translate" &lt;br&gt;
to Rust. In this case we're assuming the object we'll receive has the following attributes &lt;code&gt;tx_id&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;vout&lt;/code&gt;; we &lt;br&gt;
obtain a reference to them with &lt;code&gt;getattr&lt;/code&gt; and then we cast them to Rust types with &lt;code&gt;extract()&lt;/code&gt; so we can parse and &lt;br&gt;
validate them to build a &lt;code&gt;InputUtxo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In some instances we may want to implement a method that we don't want Python to be aware of or just use types that &lt;br&gt;
can't be converted to/from Python; when this is the case, we can have them in a different &lt;code&gt;impl&lt;/code&gt; block without &lt;br&gt;
&lt;code&gt;#[pymethods]&lt;/code&gt;, like we did for &lt;code&gt;build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PsbtBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Psbt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// code omitted for brevity&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this context &lt;code&gt;psbt::Psbt&lt;/code&gt; is a type from the &lt;code&gt;bitcoin&lt;/code&gt; crate that can't be converted to a Python equivalent, &lt;br&gt;
therefore we can't expose this method to Python, so we'll keep it in a separate &lt;code&gt;impl&lt;/code&gt; block making it accessible to &lt;br&gt;
Rust only. If you want to check the whole &lt;code&gt;build&lt;/code&gt; method you can read it &lt;br&gt;
&lt;a href="https://github.com/rviscarra/pyo3-psbt/blob/c833cae87d1ad16951ada8e59f83dc7e3b5263ac/btc/src/psbt.rs#L122" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that our PyClass struct is ready, we just need to make sure to register it in our module. We can do so by calling &lt;br&gt;
the &lt;code&gt;add_class&lt;/code&gt; method of the &lt;code&gt;PyModule&lt;/code&gt; struct in our &lt;code&gt;btc&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// A Bitcoin helper module implemented in Rust&lt;/span&gt;
&lt;span class="nd"&gt;#[pymodule]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;btc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PyModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="py"&gt;.add_class&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PsbtBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="nf"&gt;.add_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;wrap_pyfunction!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the final version of the &lt;code&gt;btc/src/lib.rs&lt;/code&gt; file &lt;br&gt;
&lt;a href="https://github.com/rviscarra/pyo3-psbt/blob/main/btc/src/lib.rs" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Unfortunately the module is too complex to test &lt;br&gt;
it in the REPL, if you want to see a fully functional instance of the module working, you can run the provided sample &lt;br&gt;
application. Follow the instructions in the &lt;a href="https://github.com/rviscarra/pyo3-psbt" rel="noopener noreferrer"&gt;repository's&lt;/a&gt; README.&lt;/p&gt;
&lt;h3&gt;
  
  
  Type hints for our native class
&lt;/h3&gt;

&lt;p&gt;Python's type hints has been a welcomed addition to the language, unfortunately PyO3 doesn't support the generation of &lt;br&gt;
type hints at the time of writing, but we can add them manually by creating a &lt;code&gt;btc/python/btc/__init__.pyi&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PsbtBuilder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utxo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner_pub_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;estimate_vbytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_address_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Autocomplete and type checking should now work in editors that have support for them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;PyO3 makes a great effort to shorten the bridge between Python's flexibility and ease of development and Rust's &lt;br&gt;
performance and safety. Thanks to its many abstractions we're able to write different parts of our application in Rust &lt;br&gt;
and interop with Python almost seamlessly, making it a great tool for our belt.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Unspent Transaction Output ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>python</category>
      <category>rust</category>
      <category>bitcoin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Stream a remote (Linux) screen with WebRTC</title>
      <dc:creator>Rafael Viscarra</dc:creator>
      <pubDate>Fri, 09 Aug 2019 17:33:57 +0000</pubDate>
      <link>https://dev.to/rviscarra/stream-a-remote-linux-screen-with-webrtc-2m5m</link>
      <guid>https://dev.to/rviscarra/stream-a-remote-linux-screen-with-webrtc-2m5m</guid>
      <description>&lt;p&gt;Recently I partcipated in a project that involved some server-side GPU rendering, &lt;br&gt;
due to the nature of the technologies we used we needed to run an X server on &lt;br&gt;
our boxes. The initial setup was relatively painless until a weird bug started &lt;br&gt;
appearing in some server and the logs didn't provide enough information to &lt;br&gt;
debug effectively, this is how I ended up coding a remote viewer that streamed &lt;br&gt;
a remote server "screen" using WebRTC.&lt;/p&gt;

&lt;p&gt;TL; DR: The complete source code can be found on this repo.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rviscarra" rel="noopener noreferrer"&gt;
        rviscarra
      &lt;/a&gt; / &lt;a href="https://github.com/rviscarra/webrtc-remote-screen" rel="noopener noreferrer"&gt;
        webrtc-remote-screen
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Stream a remote desktop screen directly to your browser
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;WebRTC remote view&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Dependencies&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/install" rel="nofollow noopener noreferrer"&gt;Go 1.12&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If you want h264 support: libx264 (included in x264-go, you'll need a C compiler / assembler to build it)&lt;/li&gt;
&lt;li&gt;If you want VP8 support: libvpx&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Architecture&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rviscarra/webrtc-remote-screendocs/architecture.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frviscarra%2Fwebrtc-remote-screendocs%2Farchitecture.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;More info in &lt;a href="https://viscarra.dev/post/webrtc-remote-screen/" rel="nofollow noopener noreferrer"&gt;this blog post&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Running the server&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;The server receives the following flags through the command line:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--http.port&lt;/code&gt; (Optional)&lt;/p&gt;
&lt;p&gt;Specifies the port where the HTTP server should listen, by default the port 9000 is used.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--stun.server&lt;/code&gt; (Optional)&lt;/p&gt;
&lt;p&gt;Allows to speficy a different &lt;a href="https://wikipedia.org/wiki/STUN" rel="nofollow noopener noreferrer"&gt;STUN&lt;/a&gt; server, by default a Google STUN server is used.&lt;/p&gt;
&lt;p&gt;Chrome 74+, Firefox 66+, Safari 12.x are supported. Older versions (within reason) should be supported as well but YMMV.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Building the server&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Build the &lt;em&gt;deployment&lt;/em&gt; package by runnning &lt;code&gt;make&lt;/code&gt;. This should create a tar file with the
binary and web directory, by default only support for h264 is included, if you want to use VP8 run &lt;code&gt;make encoders=vp8&lt;/code&gt;, if you want…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rviscarra/webrtc-remote-screen" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Ok, but why?
&lt;/h3&gt;

&lt;p&gt;I'm aware of other solutions to this problem, like setting up &lt;a href="https://wiki.archlinux.org/index.php/VirtualGL#Using_VirtualGL_with_VNC" rel="noopener noreferrer"&gt;Virtual GL&lt;/a&gt;&lt;br&gt;
to stream a remote X server's video thru VNC. I didn't like this solution &lt;br&gt;
because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It isn't as easy to setup as copying a zip file, decompress it and running it.&lt;/li&gt;
&lt;li&gt;It would have to be installed and started on every instance beforehand.&lt;/li&gt;
&lt;li&gt;It would be another component to maintain with its own unknowns and issues.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  General Overview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fis7as67r1wzag0t9ganr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fis7as67r1wzag0t9ganr.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the basic gist of what the tool does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The service starts and listens on port 9000 by default, this can be changed 
with a flag.&lt;/li&gt;
&lt;li&gt;The service exposes two endpoints, &lt;code&gt;POST /session&lt;/code&gt; to start a session and 
&lt;code&gt;GET /screens&lt;/code&gt; to get the available screens from the remote server.&lt;/li&gt;
&lt;li&gt;Once a screen is chosen, a SDP offer is created in the browser,
for simplicity there's a workaround in place to &lt;em&gt;avoid&lt;/em&gt; trickle ICE.&lt;/li&gt;
&lt;li&gt;Once all ICE candidates have been gathered, a &lt;code&gt;POST /session&lt;/code&gt; request with 
the selected screen and browser's SDP is made.&lt;/li&gt;
&lt;li&gt;The backend inspects the offer, checks if the browser supports h264 baseline 
3.1 and if everything goes well, the server responds with a SDP answer.&lt;/li&gt;
&lt;li&gt;Now the browser should receive and show the video stream.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Implementation details
&lt;/h3&gt;
&lt;h4&gt;
  
  
  h264
&lt;/h4&gt;

&lt;p&gt;I decided to use h264 instead of VP8 because the Golang binding for h264 feels &lt;br&gt;
more idiomatic and well thought than the VP8 one. It also means we can support &lt;a href="https://webkit.org/blog/8672/on-the-road-to-webrtc-1-0-including-vp8/" rel="noopener noreferrer"&gt;older versions of Safari&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Sendonly support
&lt;/h4&gt;

&lt;p&gt;Since this application only needs to stream media to the browser, I needed support for sendonly transceiver on the server. &lt;br&gt;
After going thru Pion's code I realized that it wasn't possible at the time. However, not everything's so bad, the need for this feature pushed me to implement it and &lt;a href="https://pion.ly/knowledge-base/pion-internals/contributing/" rel="noopener noreferrer"&gt;contribute to the Pion repo&lt;/a&gt;! &lt;/p&gt;
&lt;h4&gt;
  
  
  X Server (only?) support
&lt;/h4&gt;

&lt;p&gt;The library I'm using to take screenshots supports multiple platforms, so in &lt;br&gt;
&lt;em&gt;theory&lt;/em&gt; it should work but I haven't tested it, if you do YMMV. The only &lt;em&gt;hard&lt;/em&gt; &lt;br&gt;
dependency is libx264 (see the h246  section above).&lt;/p&gt;
&lt;h4&gt;
  
  
  Client-side code
&lt;/h4&gt;

&lt;p&gt;I wanted to keep things simple so I didn't setup Webpack/Babel to transpile to &lt;br&gt;
ES5 or divide my application between multiple files. The main reason for this is &lt;br&gt;
that I wanted to make &lt;em&gt;deployment&lt;/em&gt; as simple as possible, zip it, scp'it, unzip &lt;br&gt;
it and run!&lt;/p&gt;
&lt;h3&gt;
  
  
  The curse of Firefox's WebRTC bug
&lt;/h3&gt;

&lt;p&gt;I originally intended to release this repo / blog post a few weeks ago, since the project was a good MVP back then. However, life's not so simple ... just as I was preparing the repo's README and tried to take a screenshot in FF I realized that I wasn't working which I thought was weird since I got it working before.&lt;/p&gt;

&lt;p&gt;It turns out that I hit &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1333879" rel="noopener noreferrer"&gt;this&lt;/a&gt;  weird and relatively old bug that makes Firefox show a "black" video instead of the incoming stream when Firefox is the offerer. It took a while to debug and fix since there was basically no trace of errors (and as a &lt;em&gt;seasoned&lt;/em&gt; developer I automatically assume the bug is my fault).&lt;/p&gt;

&lt;p&gt;Luckily it wasn't hard to fix, I ended up doing something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;findBestCodec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdp&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sdp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SessionDescription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;webrtc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RTPCodec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;sdp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MediaDescriptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;md&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MediaName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formats&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;intPt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Atoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;payloadType&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intPt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sdpCodec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCodecForPayloadType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payloadType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Can't find codec for %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payloadType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sdpCodec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;webrtc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H264&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;packetSupport&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdpCodec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fmtp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"packetization-mode=1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;supportsProfile&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdpCodec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fmtp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"profile-level-id=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;packetSupport&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;supportsProfile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;codec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webrtc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRTPH264Codec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payloadType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sdpCodec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClockRate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;codec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SDPFmtpLine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sdpCodec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fmtp&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;codec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Couldn't find a matching codec"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It basically looks for the codec I support based on the codec name and profile, &lt;br&gt;
H264 Constrained Baseline 3.1 (&lt;code&gt;42e01f&lt;/code&gt;) in this case.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it look?
&lt;/h3&gt;

&lt;p&gt;Here it is, working on Firefox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1w5gfs3taycytow2arkq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1w5gfs3taycytow2arkq.png" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gfycat.com/ifr/ThunderousBaggyJunebug" rel="noopener noreferrer"&gt;Here's a brief capture of it&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you think that was useful/interesting?
&lt;/h3&gt;

&lt;p&gt;I'm currently looking for a software engineering job in Canada or Europe &lt;br&gt;
(on-site), know of someone who could be interested? &lt;a href="https://viscarra.dev/about" rel="noopener noreferrer"&gt;Let me know!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>webrtc</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
