# Murmur
An airgapped keyless crypto wallet protocol.
1. [Overview](##Overview)
2. [Background](##Background)
3. [Murmur Protocol](##Protocol)
4. [Implementation](##Implementation)
5. [Use Cases](##UseCases)
6. [Appendix](##Appendix)
7. [References](##References)
## Overview
Murmur is an air-gapped, keyless crypto wallet protocol. It enables the construction of ephemeral and anonymous proxy accounts that can be used by proving knowledge of future outputs of a time-restricted function. It works through the use of timelock encryption enabled by the Ideal Network. It allows users to perform on-chain actions (e.g. by sending transactions) without a need for secret keys or seed phrases. It is agnostic of authentication and identity provider mechanisms (e.g. OAuth) and can easily be integrated into them to enable session-based crypto wallets.
In essence, a *murmur wallet* is an ephemeral and anonymous proxy account that lives on the Ideal Network. These types of proxies have no 'delegate' and it can act as a proxy for *any* origin with the caveat that the origin proves future knowledge of an OTP code. OTP codes are only one mechanism that can be used to build such a wallet, other time-based functions could be used as well. MMR Wallets are created by providing the root of an MMR that commits to the future output of an OTP generator, which is generated by the user themselves. The user also holds a map of future block numbers to positions in the MMR, which they can use later on to recalculate valid OTP codes. Finally, they issue a transaction to store the root on-chain, thus 'seeding' the wallet.
The wallet can be used in periodic intervals, or as defined by the block numbers the user specified on creation. Note that this implies that Murmur wallets are 'ephemeral' in one sense, however, we include a protocol to 'update' the wallet as well which will be covered later on. Suppose the current block number of the chain is b and the user's next OTP code is for block b+c. Then at any block between b and b+c, the user can issue a transaction to 'consume' the OTP code, effectively revealing it publicly and invalidating future use. The runtime verifies the input and proxies a user-defined call when the OTP code is correct.
To summarize:
- Air-gapped keyless crypto wallets as anonymous proxies.
- Allows users to access non-custodial crypto wallets without requiring seed phrases or secret keys.
- Wallet creation is based on time-based functions, such as OTP codes, and uses timelock encryption to prove future knowledge.
- Uses a Merkle Mountain Range (MMR) to commit to future outputs of an OTP generator, ensuring efficient verification and updating.
- Supports wallet updates and transactions at predefined intervals, and can be integrated with authentication mechanisms like OAuth.
- Opens potential use cases for decentralized applications where privacy-preserving, session-based wallet management is required.
### Future Work
This type of wallet requires that murmur wallet user transactions are signed on behalf of an origin with enough funding to cover any resultant transaction fees. While we do not address it in this work, we leave it as an open task to address a potential paymaster scheme. This also allows for KYC or other such mechanisms to easily be established (e.g. if there is a semi-centralized API required to sign transction).
- Performance Improvements
- batch verification for execution and updates
- what if we used a Verkle Mountain range instead?
- This could let us represent many murmur wallets with a single data structure
- also a VMR allows for more efficient 'multiproofs', so I suppose you could efficiently prove a set of murmur wallets, connected within a VMR, can efficiently be proved in a batch verification scenario
## Background
### MMR
A [Merkle mountain range](https://docs.grin.mw/wiki/chain-state/merkle-mountain-range) (MMR) is an alternative to a Merkle tree. Merkle mountain ranges are append-only data structures where leaves are added from left to right, adding a parent as soon as two leaves exist. Unlike Merkle trees, an MMR does not need to be perfectly balanced yet still allows for efficient Merkle proofs of membership. In this document, we will refer use the following notation to represent an MMR with a given set of leaves $M \leftarrow MMR(leaves = \{l_1, ..., l_k\})$ and we will refer to the MMR root as $r \leftarrow M.root$.
In the same way as Merkle trees, an MMR allows for Merkle proofs to be constructed and verified. For an MMR $M \leftarrow MMR.New(leaves = \{l_1, ..., l_k\})$, a Merkle proof that $l_j$ is a leaf in the MMR is $\pi \leftarrow MMR.Prove(M.Leaves, l_j, j)$
The proof can be verified with $0/1 \leftarrow MMR.Verify(r, \pi, l_j)$, where $r \leftarrow M.Root$. If the function outputs $0$ then the proof is invalid, otherwise it is valid.
### Timelock Encryption
We define timelock encryption using a computational reference clock, $\mathcal{C}$.
> **Def.** A computational reference clock (CRC) is a stateful probabilistic machine $\mathcal{C}(1^k)$ that takes input a difficulty parameter $k$ and outputs an infinite sequence $w_1, w_2, ...$ in the following way. The initial state of $\mathcal{C}$ is $w_0$. It runs a probabilistic algorithm $f_\mathcal{C}$ which computes $w_\tau = f_{\mathcal{C}} (w_{\tau} - 1)$ and outputs $w_\tau$.
We write $w_\tau \leftarrow \mathcal{C}(1^k , \tau)$ for $\tau \in \mathbb{N}$ to abbreviate the process of executing the clock $\tau$ times
in a row, starting from initial state $w_0$ , and outputting the state $w_\tau$ of $\mathcal{C}$ after $\tau$ executions.
The parameters of the function $f_\mathcal{C}$ can be extended to include additional auxiliary input. On input of a string $aux \in \{0, 1\}^*$, it runs a probabilistic algorithm $f_\mathcal{C}$ which computes $w_{\tau} = f_{\mathcal{C}} (w_{\tau - 1}, aux)$ and outputs $w_\tau$.
With this, we define timelock encryption as encrypting messages to future states of the CRC.
**Def.** Let $\mathcal{C}$ be a CRC and $\omega$ be some future state. Let $ID$ be a deterministic, collision-resistant function that maps each state $\omega$ to a unique string in $\{0,1\}^d$, for some $d > 0$ (likely a crypto hash function). Then **timelock encryption** is a cryptographic scheme that allows an arbitrary-length message $m \in \{0, 1\}^*$ to be encrypted such that the future output of a CRC at a specific step $\omega$ allows for the message to be decrypted. Specifically, our scheme is a hybrid encryption scheme which first uses AES-GCM to encrypt the message with a user provided ephemeral secret key. The secret key is then encrypted for the future. So first, choose $sk \xleftarrow{R} \mathbb{Z}_P^*$, then:
\begin{align}
ct &\xleftarrow{R} TLE(m, ID(\omega), sk) \\
m &\xleftarrow{R} TLD(ct, f_\mathcal{C}(\omega, AUX))
\end{align}

When discussing timelock encryption, we will consider the CRC implemented as a blockchain's finality. As such, we will interchangeably use 'time' and 'block height' or 'block number'.
#### TOTP Generator
We will assume the usage of a time-based OTP generator (TOTP). Specifically, as described in RFC 6238 [ref], a time-based variant of the HMAC-based OTP function. We can model this as two functions:
\begin{align}
seed &\xleftarrow{R} Setup(\lambda) \\
OTP &\xleftarrow{R} \mathcal{G}(seed, t)
\end{align}
Where the setup function takes some security parameter and produces a seed and $\mathcal{G}$ is a deterministic function mapping a seed and a time parameter to a pseudorandom output. The idea is that we will generate OTP codes not for time in the sense of a 24 hour clock, but time in the sense of block production. We now briefly describe the wallet creation and usage schemes, which involves both a client and a runtime module. The client is the “keyless wallet” client which allows users to securely generate the data required to submit onchain in order to interact with the wallet. As the client is air-gapped, there is no ‘leakage’ of codes generated in the client to the external world.
#### Timelocked Transactions
A *timelocked transaction* is a scheduled blockchain transaction whose proxied call is timelocked for a specific block height. That is, by implementing a runtime module (e.g. pallet_scheduler) that allows transactions to be scheduled, we can also allow that module to decrypt ciphertexts when they reach the proper block height and decryption becomes possible. This lets a signed origin schedule a 'shielded' transaction that will execute at a specific block height and whose contents are shielded until that block height is reached.
## Protocol
This solution is inspired by the Hours of Horus idea by Zindros [Zin21]. It is a new kind of keyless crypto wallet that allows the user to authenticate by providing correct OTP codes. This wallet exists as a new type of keyless onchain account called a complete proxy. We make several improvements on the original protocol as outlined by Zindros. Specifically, rather than leveraging Witness Encryption, which is as of yet impractical, we use timelock encryption as defined above. Further, we use a Merkle mountain range over a Merkle tree, allowing for us to specify an arbitrary number of OTP codes.
We assume a client-server model, with Alice being the client and Bob the server. In practice, the 'server' takes the form of a decentralized blockchain network. We assume there is an eventually consistent gossip protocol that ensures messages from a client are relayed to the server by some upper bounded interval. The Murmur protocol consists of the six functions: Create, Execute, VerifyExecute, Update, VerifyUpdate, Recover. Assume we are working over an elliptic curve $\mathbb{E}(\mathbb{F}_p)$ for a finite field $\mathbb{F}_p$ of prime order. Also assume we are using type III pairings. Let $G \in \mathbb{G}_1$ be a generator. For a future block $b$, we define:
1. $\{root, Q, \{(b, leaf_b)\}_{b \in B}\} \xleftarrow{R} Create(seed, B)$
computes a Merkle mountain range and a map of future block numbers to leaves needed to rebuild the MMR and $B = \{b_1, ..., b_k\}$ is a limited block schedule. $Q$ is a public commitment to the protocol.
2. $(\pi, sk) \xleftarrow{R} PrepareExecute(seed, b, AUX, \{(b, leaf_b\}_{b \in B})$
produce parameters required to prove knowledge of a future OTP code
3. $0/1 \leftarrow Execute(AUX, \pi, \sigma, signer)$
gossips parameters required to verify exeuction from client to server. This step requires the participant of a third party who signs the transaction on behalf of the caller. As the signer's identity is not impactful for now, we will ignore its existence.
4. $0/1 \leftarrow VerifyExecute(r, \pi,\sigma, AUX)$
outputs 0 if the proof cannot be verified against the given root and with the given signate and aux data. Outputs 1 if verification is successful.
5. $TODO \leftarrow Update()$
6. $TODO \leftarrow VerifyUpdate()$
7. $TODO \leftarrow Recover()$
### Protocol Details
1. **Create**: $(root, \{(b, ct_b)\}_{b \in B}) \xleftarrow{R} Create(seed, B)$ where $B = \{b_1, ..., b_k\}$ is a limited block schedule
---
- **The client**
The client-side begins by producing an input 'seed', an any length string (e.g. a password), and a 'block schedule', $B$. The block schedule determines the sessions for the murmur wallet. To be precise, an otp code is generated for each of the input block numbers. In a Murmur wallet, an OTP code can be 'consumed' at any time up until the block number for which it is encrypted is reached, at which point the otp code should be considered expired (as it is essentially public information at this point). A wallet owner can consume this code exactly once before its expiration by proving knowledge of the code to the runtime. Thus, the size of the mmr here describes the initial number of total transactions that the user can submit with this wallet before a wallet update is required. For example, say you have specified $B = \{b_1, .., b_n\}$ (where $b_{i-1} < b_i$ ), then given the current block is $b < b_j$ for some $b_j \in B$, the user can potentially consume each otp code they have generated for blocks $b' >= b_j$ before $b_j$ is finalized. Once $b_j$ is finalized, then they can consume any $b' >= b_{j + 1}$ and so on. There are various other nuances that can be considerd to determine an ideal block schedule for wallet creation.
\begin{align}
\{OTP_i\}_{i \in [n]} &\leftarrow \{\mathcal{G}(seed, b_i)\}_{i \in [n]} \\
sk_i &\leftarrow HKDF(seed || OTP_i) \\
\{ct_i\}_{i \in [n]} &\xleftarrow {R} \{TLE(OTP_i, ID(b_i), sk_i)\}_{i \in [n]} \\
M &\leftarrow MMR(leaves = \{ct_i\}_{i \in [n]}) \\
r & \leftarrow M.root
\end{align}
Then call `create_wallet(name, r, size)`, which gossips the data to Bob (aka the Runtime). The client also stores a map $\{(b_i, pos_i)\}_{i \in [n]}$ mapping block numbers to positions in the MMR. The secret keys should be destroyed immediately and safely.
The MMR data can be added to some offchain storage, such as IPFS or simply on the user’s device. In particular, we will assume we have a mapping of block number to leaf position for which the wallet is valid. This will useful later on when accessing the MMR data to generate a Merkle proof.
----
- **The Runtime**
Assume the runtime stores a mapping between murmur wallet `name` and the root of the mmr, called the `Registry`. Then
1. Check if the `name` is unique by querying the regsitry. If not, then abort.
2. add the name and proxy details to the Registry.
\begin{align}
r &\leftarrow Registry.Get(name) \\
r = \emptyset &\implies ABORT \\
() &\leftarrow Registry.Set(name, \{root, size\})
\end{align}
---
2. **Execute**: $(ct, h, \pi, pos, sk) \xleftarrow{R} Execute(seed, b, AUX, \{(b, ct_b\}_{b \in B})$
where $b \in B$
---
Murmur wallets can be used within sequential 'sessions' as defined by the block numbers specified when the MMR was created. Users have 'up until' a block number to send a transaction within a given session, which they accomplish by preparing the data below. Assume that the block $b$ is the block at which the next otp code is valid. Then there are two 'ways' in which the wallet can be invoke. Firstly, it can be called by a user any time up to the deadline for the wallet's session. As we will see below, they do this by providing a valid decryption key to the runtime, which is generated with an HKDF. The other way is for a user to provide an OTP code at precisely the correct time. In the second case, this can either be done by submitting a real-time transaction or a shielded (timelocked) transaction.
- **Client**
Let $CALL$ be some valid call data. This is the 'actual' call that they want to execute. Then they compute:
\begin{align}
OTP_b &\leftarrow \mathcal{G}(seed, b) \\
ct_b & \leftarrow GETCIPHERTEXT \\
\pi_b &\leftarrow MMR.Prove(ct_b, pos, r) \\
CALL &\in \mathbf{C}\\
h_b &\leftarrow H(OTP_b || CALL)
\end{align}
Where $GETCIPHERTEXT$ is a conditional function. If the user has the MMMR data in storage and also knows the position of the leaf they are looking for, then they can just fetch the leaf. IF they do not, then they recalculate the leaf by running timelock encryption on the otp code to generate the expected ciphertext. That is, this step would take the form:
\begin{align}
sk_b &\leftarrow HKDF(seed || OTP_b) \\
ct_b &\leftarrow TLE(OTP_b, ID(b), sk_b) \\
\end{align}
If They want to use their wallet before the deadine of the session, then they call a function that they reveal everything to, passing along the ephemeral secret key $sk_b$ `force_execute(other_params, sk_i)`, otherwise they must submit a transaction at the proper block height. We will cover this in the next section on usage.
---
3. **VerifyExecute**: $b \leftarrow Verify(r, \pi, ct, pos, \sigma, CALL, h)$
The verify function allows the output of the `Execute` function to be trustlessly and publicly verified. In practice, the runtime implements this.
1. Verify the Merkle proof $\pi$ by computing $b \leftarrow MMR.Verify(r, \pi_b, pos)$. If $b = 0$ then the protocol fails and the call is not proxied. If $b = 1$, then continue.
2. Decrypt $otp \leftarrow TLD(ct, \sigma)$ and compute $\hat{h} \leftarrow H(OTP || CALL)$. Check if $h == \hat{h}$. If not, then the protocol fails, otherwise we are done and the call can be proxied.
---
4. **PrepareUpdate**: $0/1 \leftarrow Update()$
5. **DoUpdate**
6. **VerifyUpdate**
7. **Recover**
### Security Analysis
### Vulnerabilities
To investigate:
In which ways is a wallet vulnerable?
- if the IDN stops producing randomness then the wallet is unusable
- if the IDN's security is compromised then wallet security is comrpomised as well
- so some centralization due to reliance on IDN
- however, the IDN is a decentralized network itself, reducing the risks posed by centralization
### Future Work
## Implementation
- Q: secure enclave or TEE for otp computation?
- what about biometrics? this seems cool, but also apple specific, so we may need to target an OS to begin? https://medium.com/@clement.chardine/react-native-biometrics-for-your-web3-project-cbc814132928
In this section we describe the implementation of Murmur as an air-gapped, keyless crypto wallet in a Substrate-based blockchain. To be specific, it functions on the Ideal Network, which is a Substrate-based blockchain that runs the "ETF" post-finality gadget, allowing the network to produce a randomness beacon that is encoded in the pallet_randomness_beacon. The IDN allows us to acquire trustless, unbiased, verifiable on-chain randomness.
At a high level, there are three major components required to implement Murmur.
- Firstly, the blockchain must contain runtime modules that allow for the construction of an anonymous (without a delegate) pure proxy, as well as be able to handle shielded transactions (i.e. timelocked transactions) and to run the `VerifyExecute` algorithm.
- Secondly we require an air-gapped authenticator application that runs (as an embedded service) within the users device. Ideally it should run within a secure environment (e.g. a secure enclave or TEE).
- Lastly a mobile application that can receive data from the authenticator environment when authorized (e.g. with biometrics). This application must also run a light client in sync with the Ideal Network.
We aren't doing this right now, but this is still valid.

#### Components:

<!--  -->
##### Murmur Rust Library
- no_std, wasm compat
##### Ideal Network
##### Authenticator App
- "air-gapped"
- react native
- use murmur-wasm build
### Runtime Modules
#### The Proxy* Pallet
To build a wallet as specified above, we introduce a new anonymous proxy capability to Substrate. An anonymous proxy is a modification of the pure proxy in Substrate. A pure proxy requires a delegate be specified, which is the origin who signed the transaction. In the case of an MMR wallet, we want a proxy that can be called by any origin whatsoever, so we need to specify that there is no delegate for this proxy.,
First modify the ProxyDefinition:
``` rust
pub struct ProxyDefinition<AccountId, ProxyType, BlockNumber> {
/// The account which may act on behalf of another.
pub delegate: Option<AccountId>, // <--- we make this an Option
/// A value defining the subset of calls that it is allowed to make.
pub proxy_type: ProxyType,
/// The number of blocks that an announcement must be in place for before the corresponding
/// call may be dispatched. If zero, then no announcement is needed.
pub delay: BlockNumber,
}
```
and then when creating a pure proxy
``` rust
pub fn create_pure(
origin: OriginFor<T>,
proxy_type: T::ProxyType,
delay: BlockNumberFor<T>,
index: u16,
anonymous: bool, // we add a new parameter: anonymous
) -> DispatchResult {
// do stuff
// if anonymous, then set no delegate
let delegate = match anonymous {
True => None,
False => Some(who.clone())
};
// do stuff
}
```
#### OTP Pallet
Things to determine:
Q: Should the merkle leaves be stored on-chain? otherwise we may need to supply them to the proxy call, which could make the call data quite large
## UseCases
Various ideas for use cases
1. A basic keyless wallet
- **What**: A mobile wallet application based on Murmur. It allows users to create, manage, and use Murmur wallets to send transactions on the Ideal Network.
- **Who**:
- new crypto users
- users who want to use ephemeral wallets for various purposes (e.g. a wallet used for a short-lived auction)
- users who want to have multiple proxy accounts that don't require a key
- **How**: A react-native application that uses the murmur.js library. Users download the application, which contains a murmur client which can read/write to local device storage to storage MMRs and other artifacts. They can create multiple new murmur wallets, view them, use them immediately or schedule transactions, etc. This could potentially use timelock encryption to enable a timelocked escrow capability as well.
- **Why**:
2. A keyless wallet tied to external authorization
- **What**: Integrate the Murmur protocol into session management middleware (e.g. OAuth), allowing applications to provision session-based crypto wallets for their users without requiring an extensive wallet infrastructure (e.g. custodial wallet service).
## Appendix
TODO
LaTeX was stripped out when I pasted this here
Let C = {Ci}i [n] be the initial committee who will be the network authority set. Assume each committee member has an sr25519 keypair, (ski, pki) and all public keys are commonly known. The network is seeded with a set of shares (which could be replaced by an onchain distributed key generation mechanism in practice) which are created through a threshold secret sharing scheme (in reality, we use verifiable secret sharing as part of the DPSS implementation). To do so, choose s ℤp* where p is a prime number (i.e. ℤp is the scalar field over which the elliptic curve is defined) and a threshold t such that 0 < t n and choose a polynomial f(x)=i = 0taixi where each ai is a randomly chosen scalar. Calculate ui = f(i) for each i [n] and distribute each to the appropriate committee member and publicly broadcast commitments ci = uiG for some group generator G. Note that this is a simplified model, in practice this is handled through the DPSS process which uses verifiable secret sharing techniques.
In this model, assume that there is a proof-of-authority style consensus mechanism that assigns block authors in a round-robin fashion. That is, the block authored in the jth slot should be produced by the j mod nth member of the active validator set. When an authority authors a block, they are responsible for calculating the slot secret and DLEQ proof. This is accomplished by assigning an identity to each slot, simply a monotonically increasing sequence represented as a u64, which we convert to an identity. We use a hash-to-G1 function, H1: {0, 1}* 𝔾1 to convert the slot id to a group element. Thus, each block author computes dID = ui QID IBE.Extract(ui, QID) where QID = H1(ID). They also compute a DLEQ proof, which is a sigma protocol with a Fiat-Shamir transform which allows the prover (block author) to convince a verifier that for two generators G and H, that the values xG and xH were computed with the same secret scalar x. Along with this, block authors perform validations and include transactions in the block and sign the header with their sr25519 secret key. Block importers verify the signature alongside the DLEQ proof prior to accepting any blocks.
Timelock Encryption and Secure Delayed Transactions
Our implementation of timelock encryption is really identity based encryption in disguise.
Encryption
Choose some n > 0 and a positive threshold t n . Generate a t-degree polynomial f(x)=i = 0taixi and shares ui = f(i).
Encrypt each share ui for the identity of the corresponding slot sli using IBE, calculating cti←FullIdent.Encrypt(ui,IDi)
Choose a random nonce and let f(0) = x be the AES secret key, which can then be used to encrypt the message: ct←AES.Encrypt(m,nonce,x).
The ciphertext is ((ct,nonce),{cti}i [k] )
Decryption
A ciphertext prepared as above can be decrypted by gathering at least a threshold of the slot secrets leaked in the block headers for the identities used when encrypting the message. These slot secrets can be used to decrypt the IBE ciphertexts, which is interpolated to retrieve the AES key required for decryption of the message.
When convenient, we will write this as ct Tlock.Enc(M, b) to imply we are encrypting the message M for the identity associated with the slot in which block b is authored.
Secure Delayed Transactions
The ETF network includes a runtime module that enables the ability to submit secure, front-running resistant delayed transactions. It does so by sealing transaction call data with timelock encryption for specific future blocks. Network authorities are incentivized to decrypt and execute these transactions when the appropriate slot secret is produced. Heuristically, we can represent this by writing delay(encryptedCall, when), where the encrypted call must be sealed for the block height ‘when’.
## References
[GGH13] Sanjam Garg et al. “Witness Encryption and its Applications”. STOC '13: Proceedings of the forty-fifth annual ACM symposium on Theory of Computing, June 2013, Pages 467–476, https://doi.org/10.1145/2488608.2488667
[ULCG21] Gavin Uberti, Kevin Luo, Oliver Cheng, and Wittmann Goh. 2021. Building Usable Witness Encryption.
[May93] Timothy C. May. Timed-release crypto. 1993. url: https://cypherpunks.venona.com/date/1993/02/msg00129.html
[RWS96] R. L. Rivest, A. Shamir, and D. A. Wagner. 1996. Time-lock Puzzles and Timed-release Crypto. Technical Report. Massachusetts Institute of Technology, USA.
[Liu15] Jia Liu et al. How to build time-lock encryption. Cryptology ePrint Archive, Paper 2015/482. https://eprint.iacr.org/2015/482. 2015. doi: 10.1007/s10623- 018- 0461- x. url: https://eprint.iacr.org/2015/482.
[DHMW22] Döttling, N., Hanzlik, L., Magri, B., & Wohnig, S. (2022). McFly: Verifiable Encryption to the Future Made Practical. Cryptology ePrint Archive, Paper 2022/433. https://eprint.iacr.org/2022/433
[SZ23] Saereesitthipitak, S., & Zindros, D. (2023). Cassiopeia: Practical On-Chain Witness Encryption. Cryptology ePrint Archive, Paper 2023/635. https://eprint.iacr.org/2023/635
[GMR23] Nicolas Gailly, Kelsey Melissaris, and Yolan Romailler. Tlock: Practical Timelock Encryption from Threshold BLS. Cryptology ePrint Archive, Paper 2023/189. https://eprint.iacr.org/2023/189. 2023. url: https://eprint.iacr.org/2023/189.
[Zin21] Dionysis Zindros. 2021. Hours of Horus: Keyless Cryptocurrency Wallets. Cryptology ePrint Archive, Paper 2021/715. Retrieved from https://eprint.iacr.org/2021/715
[CP93] David Chaum and Torben P. Pedersen. Wallet databases with observers. In Ernest F. Brickell,
editor, CRYPTO’92, volume 740 of LNCS, pages 89–105. Springer, Heidelberg, August 1993
[BF01] Dan Boneh and Matthew Franklin. “Identity-Based Encryption from the Weil Pairing”. In: SIAM Journal of Computing 32.3 (2003), pp. 586–615.
[Yur22] Thomas Yurek et al. Long Live The Honey Badger: Robust Asynchronous DPSS and its Applications. Cryptology ePrint Archive, Paper 2022/971. https://eprint.iacr.org/2022/971. 2022. url: https://eprint.iacr.org/2022/971.
[1] Merkle Mountain Range. url:
[2] ERC-4337. url: https://www.erc4337.io/
[3] drand url: https://drand.love/