-
Notifications
You must be signed in to change notification settings - Fork 55
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
BYO network transport when minting tokens #605
base: main
Are you sure you want to change the base?
Conversation
- rename gen_ehash_premint_secrets to create_premint_secrets - rename gen_ehash_proofs to verify_and_store_proofs - make generate_premint_secrets private instead of public - add CurrencyUnit function param to create_premint_secrets - other code readability improvements
|
||
/// Get active keyset for mint from local without querying the mint | ||
#[instrument(skip(self))] | ||
pub async fn get_active_mint_keyset_local(&self) -> Result<KeySetInfo, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a better function name that indicates this function doesn't make any network calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote these functions based on an old version of cdk and copied them over to the latest branch. Need to look back and make sure the code I copied hasn't changed in the mean time. Also, and related, I am uncomfortable with the amount of duplicated code in this module. Need to refactor the shared code between the existing mint function and these new functions.
@@ -28,6 +29,7 @@ pub struct WalletMemoryDatabase { | |||
proofs: Arc<RwLock<HashMap<PublicKey, ProofInfo>>>, | |||
keyset_counter: Arc<RwLock<HashMap<Id, u32>>>, | |||
nostr_last_checked: Arc<RwLock<HashMap<PublicKey, u32>>>, | |||
premint_secrets: Arc<RwLock<HashMap<String, PreMintSecrets>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why String for the HashMap key here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I don't know if this is necessary. It would be great if I could get rid of the string entirely.
It works this way because I copied some code from somewhere else and it used a string as the index. This code comment seems to indicate using a string helps with sorting these items in a BTreeMap.
/// String wrapper for an [Amount].
///
/// It ser-/deserializes the inner [Amount] to a string, while at the same time using the [u64]
/// value of the [Amount] for comparison and ordering. This helps automatically sort the keys of
/// a [BTreeMap] when [AmountStr] is used as key.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AmountStr(Amount);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're planning to remove this memory db so i wouldn't worry about this.
AmountStr is used because the amount in the keyset response is a string not an int. https://github.com/cashubtc/nuts/blob/main/02.md#requesting-public-keys-for-a-specific-keyset
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use AmountStr in hashpool in the same way. Is this the recommended way to put pubkeys in a BTreeMap?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just use the Amount
there no need to wrap it in the AmoutnStr
. The reason we have to use AmountStr
is for when the Keys response is json serialized it needs to be as a string and not an int, so that's really the only place we should use AmountStr
everywhere else is Amount
.
EDIT: I see you're creating a Keys
which currently does expect the AmountStr
cdk/crates/cashu/src/nuts/nut01/mod.rs
Line 63 in e1458b0
pub fn new(keys: BTreeMap<AmountStr, PublicKey>) -> Self { |
AmountStr
type?
cc @crodas
let count = self | ||
.localstore | ||
.get_keyset_counter(&active_keyset_id) | ||
.await? | ||
.unwrap_or(0) | ||
+ 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have a race condition here where premint secrets could be generated twice here before incrementing the counter.
https://github.com/cashubtc/nuts/blob/main/13.md
@thesimplekid will find the exact PR and share here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to change this to atomically increment the counter here instead of when constructing proofs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unwrap_or(default)
seems more rusty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- increment counters when the secrets are generated (not when the proofs are constructed)
- atomicity: race condition between read+write (get counter + bump counter), would require some locking mechanism
&self, | ||
signatures: [Option<BlindSignature>; 64], | ||
quote_id: &str, | ||
) -> Result<Amount, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update this to return proofs instead of amount
mint_url: mint_url.parse()?, | ||
amount: Amount::from(amount), | ||
unit: currency_unit, | ||
request: "todo".to_string(), // TODO: what does request do? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't really translate to our new flow because the "proof of payment" is generated before minting is even initiated. Use empty string for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are pre-paying so we don't need a payment request.
We already have types for bolt11 and bolt12 quotes. Create a new type for a mining share quote. When minting eHash, submit the mining share and blinded secret. The mint will respond with a quote ID. Then any existing ecash wallet can mint ehash just by following the NUT04 protocol. Optionally, also submit pubkey to lock the eHash token to. |
After team review we want to change the flow to work asynchronously within the NUT04 spec. It will work like this:
This makes it easy to run a normal cashu mint and existing wallets can already support this. This decouples the mining protocol from the cashu protocol and lets us use sexy stuff like websockets without changing the mining protocol messages. |
Description
Hashpool operates on a slightly different model from typical ecash mints. It generates ecash tokens atomically when validating mining pool shares. In order to accomplish atomicity we need to piggyback the ecash operations on existing mining protocol messages. This means the ecash mint and wallet will not be able to make synchronous HTTP calls during the process of minting new tokens, which is the default assumption in cdk.
Here's the new flow:
There are two main differences with this design:
Notes to the reviewers
I have exposed some helper functions to convert between
Amount
andAmountStr
. This helps the wallet store blinded secrets using a BTreeMap. This is kind of a code smell (Why are we converting to a string for indexing purposes? Shouldn't the data structure handle this for us?) but it was the shortest path to victory during development. I'm not sure if this is necessary. Hoping to get some clarity in this PR.I didn't discuss it in the minting flow above but mint keysets will be propagated by the pool in a mining message payload and stored locally in the wallet. This requires a new function to add mint keysets to the wallet without making any HTTP calls.
I have opened this PR in draft state with many TODO comments and macros. I will fix these issues in due time but I wanted to get feedback on my general approach.
Suggested CHANGELOG Updates
CHANGED
ADDED
REMOVED
FIXED
Checklist
just final-check
before committing