paulmillr / scure-btc-signer

Audited & minimal library for creating, signing & decoding Bitcoin transactions.
https://paulmillr.com/noble/#scure
MIT License
151 stars 39 forks source link

How to build custom scripts? #33

Closed radicleart closed 1 year ago

radicleart commented 1 year ago

I'm trying to create a custom script that has some arbitrary data followed by a multisig address paying to one of two public keys, where; pubkey1 is user defined (either ecdsa or schnorr derived) and pubkey2 is a taproot (segwit v1).

The script is should end up something like;

pmnt = btc.p2tr_ms(1, [hex.decode(pubkey1), hex.decode(pubkey2)])
or
pmnt = btc.p2ms(1, [hex.decode(pubkey1), hex.decode(pubkey2)])

with the multi-sig wrapped in a custom script via a p2wsh address (or p2tr address?) e.g.

const wsh2 = {
    type: 'wsh',
    script: btc.Script.encode([<data>, 'DROP', pmnt.script])
}
const script = btc.p2wsh(wsh2, this.net);

My question is whether this is feasible - ie is this the correct way to compile custom scripts? And also which combinations of pubkeys and multi sig payment descriptors are currently possible / supported by scure btc-signer ?

paulmillr commented 1 year ago

I'm trying to create a custom script that has some arbitrary data followed by a multisig address paying to one of two public keys, where; pubkey1 is user defined (either ecdsa or schnorr derived) and pubkey2 is a taproot (segwit v1). ... My question is whether this is feasible - ie is this the correct way to compile custom scripts? And also which combinations of pubkeys and multi sig payment descriptors are currently possible / supported by scure btc-signer ?

This looks reasonable, should work, but I would suggest to test it against testnet first. We don't support custom scripts yet, so what will work:

https://github.com/paulmillr/scure-btc-signer#payments contains a list of currently supported descriptors, tested against regnet.

Taproot only supports schnorr pubkeys, other descriptors only support compressed.

radicleart commented 1 year ago

Instead of going with a p2wsh above we decided to use taproot and funding transaction became;

 wallet = btc.Address(this.net).decode(walletAddress);
tscript =  [
  { script: btc.Script.encode([data, 'DROP', wallet.pubkey]) }
  { script: btc.Script.encode([btc.p2wpkh(hex.decode(userPubKey), this.net).script]) }
]
script = btc.p2tr(wallet.pubkey, tscript, this.net, true);

This tx is here

trying to spend the first output by passing the PSBT to Bitcoin Core (owning walletAddress) for a signature, building the input like this;

const nextI:btc.TransactionInput = {
    txid: hex.decode(commitTx.btcTxid),
    index: 0,
    //tapBip32Derivation:  /// ** unsure how to formulate this ** ///,
    tapInternalKey: (script.tapInternalKey as Uint8Array),
    tapLeafScript: script.tapLeafScript, // both leaves/scripts
    tapMerkleRoot: script.tapMerkleRoot as Uint8Array,
}
this.tx.addInput(nextI);

Bitcoin Core returns not enough information about inputs. Looking at tapBip32Derivation and BIP-174 a 4 byte master key fingerprint concatenated with the derivation path of the public key is needed.

I guess (am a little lost in taproot tbh) the question is the tapBip32Derivation required and if so is their an easy way to locate it from the p2tr data already within the lib ?

paulmillr commented 1 year ago

This?

https://github.com/paulmillr/scure-btc-signer/blob/f242c7b2151521a7d5fc9078716dac968e49fc06/index.ts#LL509C3-L509C21