RFC: Standardizing on Bitcoin derivation paths

The current standard derivation path used by Stacks wallets to derive addresses is m/44'/5757'/0'/0/. And it follows the Multi-Account Hierarchy for Deterministic Wallets standard defined in BIP-0044. The unique coin type for Stacks 5757 is registered in SLIP-0044

This presents problems for wallets that support both BTC and STX. Since NFTs built on Stacks and settle on Bitcoin are marketed as Bitcoin NFTs, it should be the case that you can send and receive NFTs using Bitcoin addresses. This is possible currently as all Stacks addresses have a Bitcoin equivalent. i.e. using a b58check encoded address instead of the c32check used for Stacks addresses.

The Stacks address SP3S58FDS450F678FHX2QCM4EXJHJJ1P0QT5Q9746
corresponds to a legacy BTC address 1P6KSbfRCNkp1sf5uCSdfdZyUow7xfJCER

One can simply take BTC addresses as an input for the recipient of an NFT transfer and convert it to a Stacks address by changing the encoding. Then send the NFT to the resulting Stacks address.

Problems:

  1. The BTC address is not on a standard BTC address derivation path, meaning if you used the same seed phrase in another Bitcoin wallet, you would not be able to generate this address. This makes the address difficult to use for sending/receiving BTC, since users expect that if they migrated to a different Bitcoin wallet their Bitcoin would still be there. Though there is an argument to be made here that seed phrases are only for backup and shouldn’t be used to migrate between wallets.

  2. The address is a legacy BTC address, meaning if you were to use it for sending/receiving BTC, the transaction fees will be higher than using a segwit address.

Solution

Stacks wallets use standard Bitcoin derivation paths for both Stacks and Bitcoin addresses. For example, the m/49'/0'/0'/0/ path used for P2WPKH-nested-in-P2SH accounts (wrapped segwit address).

This means the Stacks address generated from the seed phrase will be different from current wallets. But the equivalent BTC address will be one that would be derived by other Bitcoin wallets. And it is a segwit address. (Xverse wallet currently derives a wrapped segwit Bitcoin address for the Bitcoin wallet)

This solves the 2 problems mentioned above. And in the future where Bitcoin transactions are activating Clarity smart contracts, and assets need to be transferred to the sender’s Stacks address, the address does not need to be specified separately.

Consider a Bitcoin transaction that triggers a Clarity contract to mint an NFT and transfer it to the sender. In this transaction, the sender would have paid for the minting in BTC and the contract could transfer the NFT directly to the sender’s BTC address.

This also has the advantage in that the Bitcoin and Stacks wallets feel more like one wallet. And Stacks feels more like the smart contract layer instead of a completely separate blockchain.

Potential downsides:

  1. Hardware wallet makers may not play nice. If Stacks is using the same derivation path as Bitcoin, the hardware wallet may refuse to sign Stacks transactions for security reasons.

  2. As Bitcoin derivation paths change, Stacks wallets would need to keep up. This also results in potential user experience issues since users may not understand or care about derivation paths.

  3. Wallets will need to either offer users a choice of creating/restoring “legacy” Stacks wallets for backward compatibility or automatically scan addresses on both paths and automatically handle the legacy addresses.

More discussion on Twitter for reference: https://twitter.com/TO/status/1479603116643336198

8 Likes

This is brilliant! Well done, Ken. :clap: :clap: :clap:

1. Hardware wallet makers may not play nice. If Stacks is using the same derivation path as Bitcoin, the hardware wallet may refuse to sign Stacks transactions for security reasons.

Can you elaborate on this point further? How would they even know or take notice?

2. As Bitcoin derivation paths change, Stacks wallets would need to keep up. This also results in potential user experience issues since users may not understand or care about derivation paths.

How likely is this and what is the prior history of this derivation path changes?

3 Likes

First of all love this idea and direction.

Would be awesome if we can allow wallets, especially hardware wallets, to support dual use Bitcoin / Stacks addresses.

I have a few concerns which you’ve already started to dig into, namely security and privacy.

Is there another way to accomplish this without the security and privacy implications of using the same derivation path?

What about generating taproot addresses with two public keys, either of which can spend from the address? One pubkey would be m/49’/0’/0’/0/ and one would be m/44’/5757’/0’/0/. I just thought of this off the top of my head so haven’t thought through the implications but I’ll give more thought to this.

5 Likes

Ken love it!

I’ve been suggesting this for awhile and bounced this idea to Jude in Miami.
If you are going to call your summit “Bitcoin Unleased” you obviously aren’t worried about going against the grain, so just drop the SLIP-44 standard all together then.

I’ve been building the Hiro Extensions for ages with deriving addresses at coin_type ‘0’ and as you stated it unlocks all beloved Bitcoin wallets not jus the more technical ones such as Electrum or Sparrow.

I had a wishlist grant for someone to submit a PR to the Hiro wallet to add a setting letting you switch from deriving Stacks address at either ‘5757’ or ‘0’ coin type, sadly after the feedback from approving the LNSwap integration grant from Hiro, I decided to pull it.

Even without making this a standard I think the above feature should be added to the XVerse wallet in the interim and the same goes in reverse for the BTC addresses, offer the ability to derive at the ‘5757’ pathing also.

2 Likes

I generally like the direction of this and good to see more RFCs! Thanks @yukan!

I do have concerns over (a) confusion over derivation paths, and more importantly (b) the hardware devices issue (cold storage with hardware is very important I think).

Are there other ways to achieve the benefits? For example, a method similar to current one but with segwit paths for BTC instead?

2 Likes

Ken, thank you for putting this forward! :clap:

This caught me off guard when initially working with BTC/STX multisig wallets, where I ended up using the default m/44'/0'/0'/0/0 path because that’s how the Trezor wallet generated the legacy Bitcoin addresses and it was much easier to work with when configuring the multisig with Electrum.

This would be an amazing unlock for interacting with Bitcoin addresses, and these two points were my two key takeaways in support of this change.

@OwensTrevor Ledger has an example here, however looking at the actual security bulletin it sounds like they made the restriction specifically to the Bitcoin app and made it display a warning rather than stop the user.

In some ways this feels like natural progression, we already see issues around Segwit and Taproot support in PoX, but I do agree this is a tough challenge from the UX perspective. Being able to scan accounts for assets helps with this, but provides an extra layer of complexity on both the development and user side. (which you covered in point #3)

I like @ryan’s idea of generating an address, and wonder if @hank has any input on the topic as well.

3 Likes

This is good to know, thanks @whoabuddy. I was participating in these conversations when they proposed these restrictions and I’m glad that they decided not to stop the user completely. Great for app developers.

1 Like

To be clear, the BTC address for the p2wpkh-p2sh address will be different from the Stacks address for the same private key. In Bitcoin, the address will be the hash160 of of the redeem script, which is 00 14 <20-byte keyhash>. In Stacks, the address is the hash160 of the public key (i.e just <20-byte keyhash>). These addresses do not represent the same data. You will be able to convert a Stacks address into a p2wpkh-p2sh Bitcoin address, but you will not be able to convert the p2wpkh-p2sh address back into a Stacks address without also knowing the public key. But that’s probably fine if your goal is to mint an NFT to a Bitcoin address, and then make it spendable on Stacks – in Stacks 2.1, you’ll have the ability to generate principals from data, so your Clarity contract could take the p2wpkh-p2sh address and public key as input and convert it into a Stacks address.

Thanks for putting this together Ken!

I’m totally on board with the goals that you’re working towards, but I’m not sure if this solution around derivation paths is the best option.

I’m also not totally clear on the use cases this enables. Can we spell that out more clearly?

For example, it sounds like one use case is “allow NFTs to be sent to a Bitcoin address”, and then “that Bitcoin address can use the NFT”. In that case, I’m not sure that this is a good solve. For example, let’s say an app supports sending a NFT to a Bitcoin address:

  • BTC address starts with “1”? - Sorry, invalid
  • BTC address starts with “bc…” (native segwit) - Invalid
  • BTC address starts with “3” - maybe!
    • “3” just means it’s p2sh, but it’s impossible to know if it’s a p2sh-p2wpkh address by just looking at the address. It could be a multisig, it could be a lightning channel, it could be a timelock vault, or infinite others.
    • If the address has made transactions, then you can know for sure if it’s p2sh-p2wpkh
    • There is no way to know if the address uses the standard derivation path
  • Even if the above conditions were met, this only works if the BTC address holder is willing to setup a Stacks wallet using the same exact seed phrase and derivation path. For many Bitcoiners, asking them to re-use their seed might be a lot to ask.

Given these complications, only under a limited set of cases would that use case work.

If we wanted to support Bitcoin-wallet supported actions, I think there are other options! For example, we could do BIP137 signature verifications, which would support any (almost) any Bitcoin address. In that case, users would never even have to make a Stacks wallet, especially if sponsored transactions were involved.

We can also support p2sh scripts that embed metadata. Users would only have to send a dust tx to this address to say “I want to send this NFT”. By exposing this transaction to a Clarity contract, the contract could fully verify that the “correct” address is making a Stacks tx. For example:

# metadata:
${sendNFTCode} OP_DROP # some constant representing 'send NFT action'
${nftPrincipal} OP_DROP
${nftId} OP_DROP
${recipientPublicKey} OP_DROP
# allow sending the bitcoin back:
${temporarySenderPubKey} OP_CHECKSIGVERIFY

This will result in an address that looks like any other Bitcoin address. Any user with any Bitcoin wallet could make this transaction, and Stacks apps would be able to deterministically generate these addresses. Similar schemes could be done with OP_RETURN, but wallet support for that is more limited.


I’m totally open to the fact that I might be missing the point. At the moment, I feel that this will just cause a lot of pain for not enough benefit.

1 Like

Regarding my “p2sh with stacks metadata” idea, I think we can do even better - we could support arbitrary SIP18 message hashes as the metadata! Now the script is just:

# metadata:
${sip18MessageHash} OP_DROP
# allow sending the bitcoin back:
${temporarySenderPubKey} OP_CHECKSIGVERIFY

By sending to this address, the Bitcoin user is implicitly making a signed SIP18 message. Any Stacks protocol that supports SIP18 could support this scheme for allowing off-chain signatures to make actions.

3 Likes

+1 to @hank here. I think what people are sort of asking for is better integration of NFTs with Bitcoin and just the derivation path thing (while useful) does not fully accomplish it.

We might even want to have a SIP-10 like standard for Bitcoin NFTs where ownership is on BTC addresses and BTC transactions can move the ownership. I see Hank’s idea as a step in that general direction.

1 Like

NFT is the simplest way to think about it, but should be extended to other digital asset types. The idea is to send asset to someone without a STX wallet and make it easy for them to claim it into their ownership. Ideally they can see it in their existing BTC address so they already own it in some capacity and think, “this is mine.”

An alternative would be a way to transfer the assets to a new BTC address from an existing one when they download the Stacks wallet.

1 Like

RE: Use Cases.

Thanks Hank! For use cases,Tighter NFT integration is important. In addition, I’m also working on a use case for creating Bitcoin DAOs. For example:

  1. Bob (donor) sends 1 BTC from Coinbase → to a BitcoinDAO’s BTC address
  2. Bob somehow links his Coinbase BTC wallet address to his BNS “bob.btc” (in order to claim that he in fact sent a certain amount of bitcoin to the DAO, in this case 1 BTC)
  3. Bob uses his BNS/Hiro Wallet for making governance decisions based on the % of funds donated relative to the whole.

Caveat: I’m not sure the Coinbase addresses could work? But maybe another wallet would? With Coinbase the wallet address (I believe) randomly changes without permission from the user. But in short I left Coinbase in my example because I’m trying to find a solution that could be used by the greatest number of people, making funding a Bitcoin DAOs with native BTC possible. a) Funding happens with BTC, b) governance with BNS or STX.

2 Likes

I’m generally supportive of exploring any way in which we can possibly craft the UX for Stacks in ways that empower existing Bitcoin holders while minimizing the steps they need to interact with a new blockchain and its peculiarities.

At the same time, discrete changes (such as to the derivation path) can have ripple effects that inadvertently cause a lot more work for both developers and users alike. So we’d best take care with articulating both the use cases achieved and those ripple effects, to increase the chances that it’s all worth it.

I’m seeing two particularly attractive use cases in this discussion:

  1. Stacks wallets that support both STX and BTC (plus other Stacks-based assets) need to show only one address for all the user’s assets per “account” rather than two separate addresses for Stacks and Bitcoin, which is redundant and presumably confusing for many users.
  2. Stacks users can send / airdrop assets to bitcoin holders using those bitcoin holders’ existing Bitcoin addresses (i.e. ones created with “normal” Bitcoin wallets, not Stacks wallets, since presumably these bitcoin holders don’t already engage with Stacks…or they’d simply have Stacks addresses for usage already).

I’m listing the UX issue here for Stacks wallets first since it’s already a problem. Xverse, for example, supports both STX and BTC but prompts the user to indicate which token to “receive” before showing an address, and it needs to list two addresses per account for identification purposes, taking up additional space and adding cognitive load. We’re currently resorting to similar interactions for the Hiro Wallet as we design Bitcoin support for it.

These issues can be ameliorated to some extent with UI-based solutions, but it certainly leaves the impression that the user is operating in two separate spaces, not a single one in which functionality and interactions take place fluidly across chains.

The second use case (airdropping to Bitcoiners) is vaguely intriguing since it seems like a growth hack / onboarding technique at first glance, but I’m not sure I see clearly just yet how it’s supposed to work out from a UX perspective.

As Hank touches upon, even if we airdrop Stacks assets (e.g. NFTs) to Bitcoin addresses of all types, the recipient would need some independent way of getting notified of such assets. They’d then need to install a Stacks wallet to see / manage / interact with that asset (…presuming they don’t simply want to hodl it invisibly for later delivery to someone else via a Bitcoin transaction that triggers a contract on Stacks).

If that’s the case, are we really gaining anything as far as the reduction of onboarding friction for such Bitcoiners? Is it that much more effective to airdrop assets before notifying recipients and asking them to set up a Stacks wallet vs. after doing so?

In any case, Jude’s comment above seems to suggest that it’s not technically possible to arrive at a scenario in which the same address is being used 1-for-1 across Bitcoin and Stacks (even if we use the same path). Given that, are we stuck with a two-address paradigm in general and regardless of our desired use cases? Or do have the option to modify Stacks’s approach to hashing in support of greater derivation parity?

Wallets will need to either offer users a choice of creating/restoring “legacy” Stacks wallets for backward compatibility or automatically scan addresses on both paths and automatically handle the legacy addresses.

Per this potential downside, I’d just point out that we’ve gone through a lot of pain on the Hiro Wallet end with supporting “legacy” Stacks addresses from Stacks 1.0 which used a different path than the ones now used on Stacks 2.0. App developers wishing to make the leap from Stacks 1.0 and 2.0 also felt this pain with us. So I’m particularly wary of introducing path changes without thorough consideration.

By sending to this address, the Bitcoin user is implicitly making a signed SIP18 message. Any Stacks protocol that supports SIP18 could support this scheme for allowing off-chain signatures to make actions.

Hank, I’d be curious to hear more about how this all works from a UX perspective. And why sending an NFT to the various BTC address types you list would be technically invalid.

1 Like

Hey Mark, that’s not really what I said.

What I said is that a p2wpkh-p2sh address is not derived from the same state as a Stacks address, so converting between the two by a naive re-encoding won’t work. However, it is possible to do the following:

  • Convert a Stacks address to a p2wpkh-p2sh address. Just take the hash160 of 00 14 <20-byte Stacks payload>.

  • Convert a p2wpkh-p2sh address and its public key to a Stacks address. To do so, you would:

    • Compute the p2wpkh-p2sh witness script from the public key by taking its 20-byte hash160
    • Verify that the p2wpkh-p2sh address came from this public key by taking the hash160 of 00 14 <20-byte-witness-script-hash160>, and comparing it to the hash160 of the address
    • Take the hash160 of the public key and turn it into the Stacks address.

In the latter case, the smart contract would likely need to do some bookkeeping to keep the public key around so that anyone could verify that the Stacks address originated from the p2wpkh-p2sh address (i.e. by getting its associated public key from the contract, and authenticating it with the steps above).

If you’re willing to do this extra bookkeeping, then you can definitely convert between a Bitcoin address and a Stacks address. You might even consider making a SIP that describes a standard way for storing this data so that wallets can do the conversion in a verifiable way.

Thanks for the follow-up.

I understood the following comment to mean that even if we did start using the Bitcoin derivation path for generating Stacks addresses, the same value wouldn’t actually result for both Bitcoin and Stacks addresses (given the same private key and index):

To be clear, the BTC address for the p2wpkh-p2sh address will be different from the Stacks address for the same private key.

Is that not what you meant to convey? I.e. would using the same derivation path indeed result in the same address value for both?

Note that I also take it that generating the same address for both Bitcoin and Stacks per user account is what we’re mainly considering here, though there’s also the question of how to convert most efficiently between the two if they remain separate addresses (to which you’ve detailed some possible solutions). But perhaps I’m misunderstanding the original impulse for consolidating derivation paths.

Hi guys, just a thought, would it help if we take this discussion to SIP Community call for you guys to hash it out? Then we can follow up with Stacks Forum again afterwards.
(Like how the “Miner Centralization” group did it.)

SIP Community call is on Fridays 11am EST.

1 Like

I made a discord group but discord limits groups to 10 members so perhaps we can move to signal for coordinating a meeting time? Just DM me on Discord.

1 Like

As I understand it, the point of this thread is to come up with a way for Alice to send Bob some Stacks assets if she knows Bob’s Bitcoin address, such that Bob can later use his Stacks address to spend them. For example, this would enable Alice-the-NFT-creator to airdrop an NFT to Bob-the-Bitcoin-user, so that when Bob later becomes a Stacks user, he can immediately use the NFT on the Stacks blockchain. The challenge to doing this is that because Bob is a Bitcoin user at the time he receives the NFT, he will not have a Stacks-compatible wallet available (and thus not have a Stacks address). This challenge can be overcome if we can come up with a way for Bob to somehow convert his Bitcoin address into a Stacks address at a later time.

The initial suggestion was to make it so the Stacks address and Bitcoin address both use a standard Bitcoin derivation path. This way, when Bob loads his seed phrase into a Stacks wallet, or the wallet he uses later becomes compatible with Stacks, any assets tied to his Bitcoin addresses will immediately be owned by his Stacks addresses. We can trivially do this if we use a legacy Bitcoin address derivation path, since then the Stacks address is simply a re-encoding of the Bitcoin address.

@yukan points out that we’d want to use segwit if possible, since then the transaction would be cheaper. I point out that while you can’t re-encode a segwit bech32 address to a Stacks address, and you can’t naively re-encode a segwit-p2sh base58 address to a Stacks address, you can derive the Stacks address from both the segwit-p2sh base58 address and its associated public key. This would get you (some of) the space savings of segwit while retaining the ability for Alice and Bob to agree on a Bitcoin address that (1) Bob-the-Bitcoin-user’s wallet supports and (2) that Bob can later convert to a Stacks address using only public data.

An alternative to this would be that when Bob starts using a Stacks-compatible wallet and wants to do something with his assets received to his Bitcoin addresses, the Stacks wallet can simply use the derivation path for his Bitcoin addresses to derive the Stacks addresses (and keys) to use. If Bob received assets to a legacy address, then the wallet would simply use the legacy address derivation path to generate a private key that he could use to manipulate the assets on the Stacks chain. If he received assets to a p2wpkh-p2sh address, then the wallet would use my scheme above to generate a Stacks address from his public key. Most of the time, the wallet could guess the derivation path, so no involvement from Bob will be necessary.

1 Like