-
Notifications
You must be signed in to change notification settings - Fork 78
Add expected_weight field to InputPair
#772
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
Conversation
arminsabouri
left a comment
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.
Thank you for taking this challenge on. Do you mind squashing your commits. And writing a commit message that captures the high level changes as well as relevant design decisions. In general Lint and Fmt fixes should be applied in the commit that introduces the issue. Thanks
44fdd80 to
96c0b58
Compare
Pull Request Test Coverage Report for Build 16150204062Details
💛 - Coveralls |
spacebear21
left a comment
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.
At a high-level the current design seems backwards.
I think InternalInputPair should remain mostly untouched. Instead, expected_weight should be added to the public-facing InputPair. When an InputPair is constructed for a predictable input type (p2pkh, p2wpkh...) we can use the expected_input_weight method to calculate the expected weight from the script and store that in InputPair.expected_weight. For p2sh/p2sh... the caller should specify expected_weight manually and it gets stored as-is. This way we shouldn't really need to change contribute_inputs except to use the expected_weight field instead of the expected_input_weight() method.
9c90464 to
fcee2cd
Compare
|
@0xBEEFCAF3 @spacebear21 ready to be reviewed again... |
contribute_inputs_with_weights()InputPair::new_with_weight()
fcee2cd to
762890e
Compare
61351cb to
9f403f1
Compare
762890e to
0e4d27a
Compare
|
Marking this as a draft as we still have things to discuss offline. |
arminsabouri
left a comment
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.
Thanks for pushing this forward. I had a few items. Main concern is unrelated changes.
0e4d27a to
14da2a7
Compare
InputPair::new_with_weight()expected_weight field to InputPair
92ea08e to
fb5bc60
Compare
arminsabouri
left a comment
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.
Approach ACK.
e570cd9 to
f134955
Compare
| ..Default::default() | ||
| }; | ||
| InputPair::new(txin, psbtin).expect("Input pair should be valid") | ||
| InputPair::new(txin, psbtin, None).expect("Input pair should be valid") |
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.
Here, and in other places where InputPair::new is called, wouldn't this cause an issue if this is a script (p2sh, p2wsh, p2tr) spend? From @spacebear21's comment in #583:
the unlocking script (i.e. signature) could be any arbitrary script of unpredictable weight and can't be known in advance of signing. Since wallets should have the information required to predict the max size of the eventual signature, we can have them provide an "max expected weight" or similar parameter when constructing
InputPairs.
If we're just putting None here, we'd run into the same issue of just not having the expected weight field wouldn't we?
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.
Yes, we should probably switch to using the new_p2wpkh and other constructors for payjoin-cli. IIRC ListUnspentResultEntry has an address type field we could match on.
I think for payjoin-cli we could leave script spends unsupported for now since it's unlikely for bitcoin-cli users and not supported now anyway.
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.
The InputPair::new() calls the expected_input_weight() (in the first place) that sets the expected_weight (if AddressType is supported), if that call breaks we then try to set the weight using the given expected_weight param (Option<>).
That said, wouldn't that cover the AddressType check?
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.
Am i correct in assuming there's an objective/correct way to calculate the expected weight? If we're calling expected_input_weight in InputPair::new, we would hopefully be doing the calculation the correct way, and wouldn't need the user to provide the expected weight themselves, since they would just be doing what expected_input_weight is doing. If that's the case, i think it would make sense to keep the new field in InputPair, but populate it only through InputPair::new calling expected_input_weight internally. Then, the constructors would be more ergonomic, and the user wouldn't need to do duplicate work of calculating the weight themselves
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.
The expected_weight field (provided by users/applications) would be used in situations where the expected_input_weight() is not able to predict the weight of the given InputPair (the issue that this PR tries to solve points out to one of them: #659)
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.
oh i see, the expected weight can only really be calculated for the key spends, and this is mostly for the script spends, which will just return an error if the user doesn't provide something, as those aren't supported in expected_input_weight()
f134955 to
454bd17
Compare
| assert_eq!(p2sh_pair.psbtin.non_witness_utxo.unwrap(), utxo); | ||
| InputPair::new_p2sh(utxo.clone(), outpoint, redeem_script.clone(), Some(sequence)); | ||
|
|
||
| assert_eq!( |
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.
@spacebear21 @shinghim what do you guys recommend me to do here?
The issue is that while calling new() (from new_p2sh()) it fails in the match at:
https://github.com/payjoin/rust-payjoin/blob/master/payjoin/src/psbt/mod.rs#L203
As the given script is not a nested p2wpkh (tested by is_p2wpkh()).
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.
Similar to the changes to InputPair::new, I think the script spends should have the additional field in their constructor to provide the expected weight. IIUC those are the types of inputs that actually need the user to provide them, since the key spends have a known max value (P2PKH_COMPRESSED_MAX/P2WPKH_MAX/NESTED_P2WPKH_MAX and maybe P2TR_KEY_DEFAULT_SIGHASH? though I'm not familiar enough with P2TR to know if we should return this value as the expected weight if the address is P2TR since there could also be a script path, right?)
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.
IIRC we decided to solve the p2tr script path spends in a seperate PR. Documented here #788
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.
And yes i agree. Script path input constructors should have a param for weight.
6581cb9 to
190ec3a
Compare
|
PR is now ready to get another round of reviews. |
arminsabouri
left a comment
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 this is moving in the right direction. There a few smaller items. And request to catch duplicate input pairs for the reciver typestate field. One thing i don't love is that we have to create a dummy input_pair in InputPair::new(...). I think this can be resolved if we refactor expected_input_weight to be a pure function and not part of internal input pair. I can take a look into that
c6885eb to
9579130
Compare
|
Last push cleans up |
9579130 to
73c3245
Compare
|
Ended up reverting the dedup logic after noticing that we dont dedup inputs going into the payjoin psbt. |
73c3245 to
b16d70b
Compare
b16d70b to
1be77b7
Compare
arminsabouri
left a comment
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.
One more thing i noticed. I will push a fix for this my self.
b706f7d to
4cef427
Compare
payjoin/src/core/receive/mod.rs
Outdated
| pub fn new_p2wsh( | ||
| txout: TxOut, | ||
| outpoint: OutPoint, | ||
| witness_script: ScriptBuf, |
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.
Is there any reason to keep the witness_script parameter? IIRC it was only needed to calculate the expected weight but that's no longer relevant.
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.
Is there any reason to keep the witness_script parameter?
Can't think of a good reason. I will remove it in a seperate commit on this PR.
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.
one thing to note. Some wallets will require witness_script to be present in the psbt when signing and they may need to insert them later after they use the constructor. The same is true of key derivation fields as well.
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.
For now I think the solution for fancier wallets will be to use the new_unchecked method and pass their own PsbtInput/TxIn. We'll just need to ensure those fields don't get cleared out elsewhere.
| // P2wsh yet is not supported when expected weight is not provided | ||
| assert_eq!( | ||
| p2wsh_pair.err().unwrap(), | ||
| PsbtInputError::from(InternalPsbtInputError::from(InputWeightError::NotSupported)) |
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 makes me wonder if we should change this variant name since "NotSupported" implies it just needs to be implemented, but really this is more like "impossible to predict weight"
4cef427 to
311e775
Compare
Co-authored-by: Armin Sabouri <armins88@gmail.com> This change allows applications to specify the weight of their own inputs via `InputPair::new()` method. This enables more accurate fee estimation for complex inputs, such as P2WSH. It solves issue payjoin#659.
Witness script was used to help derive the expected input weight. We now require the application to provide the weight for variable weight inputs (p2wsh).
spacebear21
left a comment
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.
ACK 311e775 - though there is a failing FFI check in CI.
311e775 to
92996ec
Compare
Whoops missed that. Thanks |
arminsabouri
left a comment
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.
Approving so my req for change doesnt block merge status
spacebear21
left a comment
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.
reACK 92996ec. Nice work!
This PR implements a way to applications be able to set the weight of their inputs via
expected_weightfield ofInputPair.By doing that we will be able to solve #659
This PR also unblock the Liana's integration as it deals with P2wsh:
https://github.com/arturgontijo/liana/blob/payjoin-v3/lianad/src/payjoin/receiver.rs#L191-L197
TODO (if high level design is ok):