Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: Scaling Use Cases #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions text/0000-scaling-use-cases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
- Feature Name: scaling_use_cases
- Start Date: 2021-07-21
- RFC PR: (leave this empty)
- Hathor Issue: (leave this empty)
- Author: Marcelo Salhab Brogliato <[email protected]>

# Summary
[summary]: #summary

This document discusses different approches to integrate applications with Hathor where the number of users might grow indefinitely.

# Motivation
[motivation]: #motivation

There are multiple ways to developing such integrations and it is not trivial to decide which approach works best for each case.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

The context here is that a company has a growing user base, and each user has one or more addresses in Hathor. We can say that each user has a wallet, but we need to carefully understand what "wallet" means in this case.

The company has custody of its users' tokens. In other words, the users' wallets are fully controlled by the company.

Let's explore different ways of generating and maintaining the users' wallets.

<!-- ##### -->
<!-- ##### -->

## Global Wallet

In this case, the company will run one global wallet. Each user will be associated to one or more addresses of the global wallet through their indexes.

The global wallet has an unlimited number of enumerable addresses. Each index will be associated to a user. For instance:

```
address_index | user
--------------|-----------
0 | Alice
1 | Bob
2 | Alice
3 | Charlie
4 | Charlie
5 | Dave
6 | Eve
7 | Alice
```

Notice that each index belongs to only one user. But the same user may have one or more indexes.

This approach does not scale easily because the global wallet will have to keep track of more and more addresses over time. The more users, the more addresses. The more addresses, the more memory is required.

Another challenge is to scan the transactions of the global wallet. The most common stop criteria for the scanner is the GAP Limit, which stops the scanner when `N` addresses in a row are empty. An address is empty when it has no transactions.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that it's a generic doc for use cases, I think we can explain when the gap limit problem can happen. I though about starting this paragraph like this:

Depending on the moment that the use case associates a new address to a user, we may face another challenge to know when to stop scanning the wallet address. E.g. an exchange associates the address to a user as soon as the user requests it but he might never send a transaction to it, on the other hand, some use cases can wait until the moment the user needs to send its first transaction before associating an address to him. In this scenario, no addresses would be unused.


To illustrate the problem, let's say the GAP Limit is three. If Charlie and Dave have not made any deposits, addresses #3, #4, and #5 will be empty, and the wallet will not find the deposit made by Eve in address #6.

A temporary solution might be to increase the GAP Limit, but the issue will likely happen again in the future.

Another solution might be to artificially generate transactions every three users.

<!-- ##### -->
<!-- ##### -->

## Global Wallet with Derivation Paths

This case is similar to the Global Wallet. But, instead of associating one index per user, it associates one derivation path per user. For instance:

```
derivation_path | user
----------------|----------
m/44'/0'/0'/0 | Alice
m/44'/0'/0'/1 | Bob
m/44'/0'/0'/2 | Charlie
m/44'/0'/0'/3 | Dave
m/44'/0'/0'/4 | Eve
```

Notice that each user has one, and only one, derivation path.

Each derivation path leads to an unlimited number of enumerable addresses. Thus, each user has addresses #0, #1, #2, #3, and so on. Notice that Alice's address #0 is different from Bob's address #0 because they had different derivation paths.

This approach has a similar scaling issue of the Global Wallet. The wallet will have to keep track of all these derivation paths and their addresses.

Another challenge is to scan the derivation paths. The most common stop criteria for the scanner is the GAP Limit M/N, which stops the scanner when `M` derivation paths in a row are empty. It considers that a given derivation path is empty when its first `N` addresses are empty.

## One Wallet Per User

This case differs from the previous ones because there will be multiple wallets running.

First of all, there are multiple ways to associate a wallet with a user. To illustrate, some ways are: (i) one seed per user, (ii) one global seed with one passphrase per user, or (iii) one global seed with one derivation path per user.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't option (iii) the same as the last section (Global Wallet with Derivation Paths)?


The challenges here are the same regardless the way the wallet is associated with a user.

The naive implementation here is to run one wallet per user. But it would consume a lot of resources (CPU and memory) and it is hard to scale to millions of users.

A better implementation is to run a pool of wallets that automatically starts and stops wallets on demand. For instance, when a request is made to a wallet that is not running, the pool starts that wallet automatically. When a wallet is idle for too long, it is automatically stopped.

Such pool would consume resources according to the number of concurrent users (instead of total users). It can easily scale up through the addition of more servers to the pool.

## Full-length Wallet Service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add a note explaining that this option is not currently available as it is being tested internally


A Full-Length Wallet Service is a service that keeps track of all addresses in the blockchain and allows the creation of wallets.

Wallets can have two types: manual or xpub.

- Manual wallets have a list of addresses that is manually maintened, i.e., one must manually add or remove addresses from the wallet.
- Xpub wallets have an xpub, and the addresses are scanned using the GAP Limit stop criteria.

In this case, it is easier to use xpub wallets and ensure that the deposits meets the requirements of the GAP Limit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a section explaining a bit of how HD Wallets work (BIP32 and BIP44)

# Useful Links

1. [BIP-0032: Hierarchical Deterministic Wallets](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
2. [BIP-0039: Mnemonic code for generating deterministic keys](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
3. [BIP-0044: Multi-Account Hierarchy for Deterministic Wallets](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
4. [SLIP-0044: Registered coin types for BIP-0044](https://github.com/satoshilabs/slips/blob/master/slip-0044.md)