-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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: add support for pay to taproot #1742
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
Thanks for the PR! Just a quick note before I review: This effort is duplicated with BitGo's fork, which is adding native JS schnorr and using TransactionBuilder. https://github.com/BitGo/bitcoinjs-lib/blob/master/ts_src/payments/p2tr.ts I will most likely pick and choose parts from both efforts and add everyone as a co-author. |
Thanks @junderw! That is fine with me. |
timeline? Not really. Right now:
4 will be more difficult since right now the extensions needed for taproot have not been decided yet, so it will need to be made as an experimental API (that isn't importable/exportable, similar to how TransactionBuilder used to hold extra data that can't be represented in the transaction serialization) added onto Psbt, which we will mark as experimental, and make it clear that the API will break on a patch/minor version change without warning. I am thinking of just releasing the new v6 once we're done with 3, then we can just have a few examples of creating a Transaction manually using just the Transaction class and its methods... Then once the taproot PSBT extensions are in stone, we will need to add support to bip174 for v2 PSBTs (I think taproot will require v2 support), then implement the taproot extensions, then implement into Psbt here, then maybe release v6.1.0. I hope to get done with up to number 3 by beginning of next week ish. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
I was hoping to keep p2tr a non-breaking change. So rather than use a Factory to get all payments, I was thinking either:
I think 2 is better, since it allows people who don't need to perform calcs with ecc to not need an ecc library. |
|
ts_src/payments/p2tr.ts
Outdated
export function p2tr( | ||
a: Payment, | ||
opts?: PaymentOpts, | ||
eccLib?: TinySecp256k1Interface, |
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 was thinking it should be a member of the first argument a
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.
a
and the outputo
are related data structures (o
isa
after the computations and validations have been applied)- if
eccLib
is not expected to be present ono
then it shouldn't be ona
either
- if
- the
PaymentOpts
looks be a better place foreccLib
(eccLib
is actually an option for the payment)
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.
good point. PaymentOpts sounds better
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.
good point. PaymentOpts sounds better
Done!
I'm trying to figure out how this PR can be improved in order to get it merged sooner rather than later.
Any other |
ts_src/payments/index.ts
Outdated
export type PaymentCreator = ( | ||
a: Payment, | ||
opts?: PaymentOpts, | ||
eccLib?: TinySecp256k1Interface, |
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.
not needed anymore
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.
Done!
ts_src/payments/testecc.ts
Outdated
if (!bool) throw new Error('ecc library invalid'); | ||
} | ||
|
||
const tweakAddVectors = [ |
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.
only include the 1st 2nd and 5th vector. Remember these will be included in bundles people make, so the bare minimum to make sure it handles an edge case, and 1-2 main cases should be enough.
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.
Done!
I wonder if we should offer a tool for sorting leafs in order of weight (likelihood of use) I don't think it should be a show stopper, but it would be nice to have. |
It would definitely be nice to have this. I have added this task for it: #1766 |
Me again... I have made the following changes to the The main changes are listed below. They can be rolled back and included in a separate PR if needed. They are useful for testing the
I know that |
ts_src/psbt.ts
Outdated
* @param opts - tweak options | ||
* @returns a Signer having the Private and Public keys tweaked | ||
*/ | ||
export function tweakSigner(signer: Signer, opts: TaprootSignerOpts): Signer { |
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 helper function is out of place. It also is returning ECPair which we removed from the library in v6.
It would be much easier to create a new interface called TweakedSigner which has a method called signTweaked(hash: Buffer, tweak: Buffer, extraEntropy?: Buffer): Buffer
etc.
Then we can implement that for ECPair and bip32 separately.
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.
Although, another way to deal with it is to just tell the Signer it is to sign itself tweaked... and just automatically do that with signSchnorr when the internal flag is set... (it knows the tweak since the "recommended" tweak is of its own xonlypubkey)
I don't know which way is more useable... but this helper function is not the right path imo.
One option is to change the
To:
This would mean:
Implementation quick view:
|
I've open a quick PR with these change bitcoinjs/ecpair#6 |
This comment was marked as resolved.
This comment was marked as resolved.
@junderw what can I do to get this PR merged? |
Is about what I'm thinking right now. Some of this could be split off into separate PRs tho.
|
- does not cover `TapBip32Derivation` - does not cover taproot outputs
@antonilol: thank you for your inputs. I have made the changes you suggested (except 2 of them, see comments)
|
export type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf; | ||
|
||
export function isTaptree(scriptTree: any): scriptTree is Taptree { | ||
if (!Array(scriptTree)) return isTapleaf(scriptTree); |
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.
was this supposed to be Array.isArray()
? This is calling the constructor and always returns a truthy value
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.
nevermind - we have a const Array =
further down
Summary
This is the first version for the
p2tr
payment. It is not complete, but it covers most of the functionality.I'm opening this PR for others to test, review and provide feedback.
Main changes listed below:
New payment type:
P2TR
It follows the same approach as the other payments (
p2wsh
,p2pkh
, ...)PaymentOpts
object takes an extra field:eccLib?: TinySecp256k1Interface
P2TR
(but not for the other payment types)eccLib
is required for the tweak operations. TheECC
logic has been extracted out of this lib.P2TR
can be found in the unit tests and integration tests (see tests section)Payment
interfaceThe
Payment
interface has 3 new fields:internalPubkey
- the internal key as defined by BIP341.scriptTree
- a Merkle tree whose leaves consist of a version number and a script (see BIP341)redeemVersion
- the version of the leaf being spent (if the script-path spend is used). Default value:0xc0
Some
Payment
fields havetaproot
specific meaning:pubkey
- the tweakedinternalPubkey
as defined in BIP341hash
- the hash of the root of the MerklescriptTree
. Empty if notscriptTree
is used.signature
- a Schnorr signature, if key-path spent is used. Empty otherwise.redeem.output
- the leaf script, forscript-path
spendsTaproot Helpers
ts_src/payment/taprootutils.ts
bip341
module. See extract BIP341 interface #1780Address
from|toOutputScript
takes in an optionaleccLib
. If missing, then it skips thep2tr
matching falls down to the final ErrorPSBT
key-path
spend the signer has to be tweaked (it does not perform the tweak internally)script-path
spend a custom finalizer (FinalScriptsFunc
) is required when a Taproot input is finalized. Seetest/psbt.utils.ts
for such an examplePsbtOptsOptional
has a new fieldeccLib?: TinySecp256k1Interface;
.eccLib
is required if the PSBT instance uses taproot ins/outsSigner
interface has a new method:signSchnorr?(hash: Buffer): Buffer;
. This method is required if a Taproot UTXO is being spent.Tests
p2tr.json
: data generated using the bitcoin test framework test/functional/wallet_taproot.pyBIP341 test cases
: Test vectors for scriptPubKey construction recommended here by Pieter Wuilletest/integration/taproot.spec.ts
)testnet
(code sample and transaction links)Further Comments
Note for reviewers: all
.js
files are generated, only the.ts
files require review