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

feat: construct BTC transactions #128

Merged
merged 7 commits into from
May 7, 2024

Conversation

djordon
Copy link
Contributor

@djordon djordon commented May 6, 2024

Description

Closes #75.

We need to take a set of deposit and withdrawal requests and turn them into BTC transactions that can be signed by the signers. This PR gets us part of the way there.

Type of Change

This is new code that takes the current state of the signers UTXO, the current market rate in sats-per-vbyte, along with outstanding deposit and withdrawal requests and creates a set of BTC transactions.

Notes

There are several things worth noting though:

  1. The UTXO output scripts for withdrawals are pay-to-witness-public-key-hash (P2WPKH) outputs. I think this is fine, since it's probably okay to a assume our users have native segwit addresses but I don't know about assuming the same for taproot addresses. In the future, maybe the user can decide?
  2. I have not implemented filtering of requests based on their max fee and the current market fee rate.
  3. There is nothing here that accommodates things related to replace-by-fee (RBF) transactions. That stuff will get done in another PR addressing Add functionality to transaction package construction to support RBF #66.
  4. I'm not sure what the final API here will be, the public stuff here is not close to final.

Does this introduce a breaking change?

No breaking changes

Testing information

There are unit tests here that test for most of the asserted functionality and/or invariants. But some further integration testing is required to ensure that properly signed witness data is indeed correct. These tests will happen in another PR (such as one addressing #67).

Checklist

  • Code is commented where needed
  • Unit test coverage for new or modified code paths
  • Tag reviewers

@djordon djordon added the utxo consolidation Tickets related to how we consolidate deposit and withdrawal BTC transactions label May 6, 2024
Copy link
Contributor

@netrome netrome left a comment

Choose a reason for hiding this comment

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

Great stuff so far. The only blocker I see is that we shouldn't dictate the output type for withdrawals.

Comment on lines 170 to 171
/// The public key that will be able to spend the output.
pub public_key: PublicKey,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should assume a public key in the withdraw request. Perhaps the user wants to withdraw to a multisig address? Or another script?

Instead, I think this should be an Address, alternatively a ScriptBuf representing the output scriptPubKey if we want to be more low-level here.

Suggested change
/// The public key that will be able to spend the output.
pub public_key: PublicKey,
/// The public key that will be able to spend the output.
pub public_key: bitcoin::address::Address,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That all makes sense to me, but I think that would lead us being off the low-level designs in the Deposit API LLD and the Clarity LLD. I was following the Deposit API LLD, which uses the public key, while the clarity LLD uses a tuple of a 1 byte version field with a 32 byte hashbytes field (which I think is a compressed public key).

I'm okay changing the designs and it's all equally easy for me to update here. Just want to call out that this would lead to a mismatch.

Copy link
Contributor

Choose a reason for hiding this comment

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

(for reference) We've synced with the team and agreed that this is a LLD bug, and that we want to use addresses here.

Comment on lines 45 to 47
/// The maximum acceptable number of votes against for any given
/// request.
pub reject_capacity: u32,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: It's more intuitive for me to work with the converse: accept_threshold. That's the parameter that we already use in many places in wsts and the Nakamoto signer (just called threshold there). Would you be okay with swapping it here?

Suggested change
/// The maximum acceptable number of votes against for any given
/// request.
pub reject_capacity: u32,
/// The minimum acceptable number of votes for any given request.
pub threshold: u32,

Copy link
Contributor Author

@djordon djordon May 7, 2024

Choose a reason for hiding this comment

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

Yeah, it is more intuitive. I think I had that design originally but with an additional field noting the total number of signers so that we could compute the reject capacity (the accept threshold alone is not enough). I'm okay with switching to that version of things with the additional field.

Comment on lines 96 to 97
/// The public key used in the deposit script.
pub deposit_public_key: PublicKey,
Copy link
Contributor

Choose a reason for hiding this comment

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

Which public key is this? There should only be one public key in the deposit script, and that's the signer aggregate key.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, so this should be the aggregate key. Can we rename it?

Suggested change
/// The public key used in the deposit script.
pub deposit_public_key: PublicKey,
/// The public key used in the deposit script.
pub signer_aggregate_key: PublicKey,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense. Are you opposed to signers_public_key?

signer/src/utxo.rs Show resolved Hide resolved

/// Helper function for generating dummy Schnorr signatures.
fn generate_dummy_signature() -> Signature {
let key_pair = Keypair::new_global(&mut secp256k1::rand::thread_rng());
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: We might as well include rand as an explicit dependency since I'm sure we'll use it more times than just here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, we've defaulted to using OsRng in many places in the nakamoto signer already. I don't have strong opinions on either, but it's nice to be consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool beans, will update.

@djordon
Copy link
Contributor Author

djordon commented May 7, 2024

Updated! @netrome would you mind taking another look?

@djordon djordon requested a review from netrome May 7, 2024 15:58
Copy link
Contributor

@hstove hstove left a comment

Choose a reason for hiding this comment

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

In general this is pretty great, but I have a question around computing the optimal tx. If what I'm talking about is known and out of scope for this PR, that totally makes sense too!

// Create a list of requests where each request can be approved on its own.
let items = deposits.chain(withdrawals);

compute_optimal_packages(items, self.reject_capacity())
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused as to where input/output weight come into play here. In general, inputs will be very similar size (though not exact), and outputs will be varying in size (depending on the type of address). So, wouldn't this function need to have some mechanism of knowing a request's "cost"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. The compute_optimal_packages function computes approval sets using only how the signers have voted on each of the requests. The number of rejections is the request's "weight", it is completely separate from input/output weight of a BTC transaction. That function doesn't care about the BTC weight at all.

You know, I should rename that term but I couldn't think of anything better (size? heaviness? pounds? lbs?). Everything I thought of kinda missed the mark a little but maybe weight is even worse.

@netrome
Copy link
Contributor

netrome commented May 7, 2024

Updated! @netrome would you mind taking another look?

Thanks, will do 👀

Copy link
Contributor

@netrome netrome left a comment

Choose a reason for hiding this comment

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

Thanks for updating, looks good to me!

Comment on lines 170 to 171
/// The public key that will be able to spend the output.
pub public_key: PublicKey,
Copy link
Contributor

Choose a reason for hiding this comment

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

(for reference) We've synced with the team and agreed that this is a LLD bug, and that we want to use addresses here.

signer/src/utxo.rs Show resolved Hide resolved
@djordon djordon merged commit 205cc6f into main May 7, 2024
2 checks passed
@djordon djordon deleted the 75-construct-spend-script-btc-transactions branch May 7, 2024 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
utxo consolidation Tickets related to how we consolidate deposit and withdrawal BTC transactions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Properly construct the spend script in BTC transactions
3 participants