bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.7k stars 2.11k forks source link

How to build p2tr transaction using Transaction #1883

Closed earthflower closed 1 year ago

earthflower commented 1 year ago

The use of p2tr transactions has increased, because more people are using ordinals that require p2tr type of transaction.

While there are examples of creating p2tr transactions using Partially Signed Bitcoin Transactions (PSBTs), it would be helpful to have more information on how to use new bitcoin.Transaction(); for this purpose.

Can some bitcoinjs-lib patron please information on how to create p2tr transaction using new bitcoin.Transaction();

earthflower commented 1 year ago

Ordinals are created using commit and reveal transaction

how to get const controlBlock = ??


const createInscription = () => {
let content_type = "text/plain;charset=utf-8";
  let body = Buffer.from("999");
  const inscription = {
    body: body,
    content_type,
  };
  const network = bitcoin.networks.bitcoin;
  const internalKey = bip32.fromSeed(rng(64), bitcoin.networks.bitcoin);

  const tweakInternalPubKey = toXOnly(internalKey.publicKey);

  const randKey = bip32.fromSeed(rng(64), bitcoin.networks.bitcoin);

  const pubKeyChunks = bitcoin.script.compile([
    tweakInternalPubKey,
    bitcoin.opcodes.OP_CHECKSIG,
  ]);

  const revealScript = appendRevealScript(inscription);

  const revealScriptWithCheckSig = bitcoin.script.compile([
    pubKeyChunks,
    revealScript,
  ]);

  const scriptTree = {
    output: revealScriptWithCheckSig,
  };
  bitcoin.initEccLib(ecc);

  //taproot_spend_info
  const { output, witness } = bitcoin.payments.p2tr({
    internalPubkey: toXOnly(internalKey.publicKey),
    scriptTree,
    network: network,
  });

  let commitTxAddress = Bitcoin.address.fromOutputScript(output, network);
  const controlBlock = ??

}

//Adding ord content
export const appendRevealScript = (inscription: Inscription) => {
  let instructions = [
    bitcoin.opcodes.OP_FALSE,
    bitcoin.opcodes.OP_IF,
    PROTOCOL_ID,
  ];
  if (inscription.content_type) {
    instructions.push(CONTENT_TYPE_TAG);
    instructions.push(Buffer.from(inscription.content_type));
  }
  if (inscription.body) {
    instructions.push(BODY_TAG);
    const chunks = makeChunks(Buffer.from(inscription.body), 520);
    for (let chunk of chunks) {
      instructions.push(Buffer.from(chunk));
    }
  }
  instructions.push(bitcoin.opcodes.OP_ENDIF);
  return bitcoin.script.compile(instructions);
};

in inscribe rs line 192, ord is using TaprootBuilder

    let taproot_spend_info = TaprootBuilder::new()
      .add_leaf(0, reveal_script.clone())
      .expect("adding leaf should work")
      .finalize(&secp256k1, public_key)
      .expect("finalizing taproot builder should work");

   let control_block = taproot_spend_info
      .control_block(&(reveal_script.clone(), LeafVersion::TapScript))
      .expect("should compute control block");

Can somebody give some pointers about getting control block?

Much appreciated!!

0xtrou commented 1 year ago

did you manage to handle this?

0xtrou commented 1 year ago

Just found the answer regarding the control block here, would love to know your learning regarding the Ordinals minting using bitcoinjs

https://bitcoin.stackexchange.com/questions/107154/what-is-the-control-block-in-taproot

pefish commented 1 year ago

Maybe you can look this url https://github.com/bitcoinjs/bitcoinjs-lib/blob/v6.1.0/ts_src/payments/p2tr.ts#LL271C23-L271C23

fboucquez commented 1 year ago

Hi team, facing the same issue. Is there any updated script? Thanks!

fboucquez commented 1 year ago

Hi @earthflower , have you managed to inscribe ordinals using bitcoinjs? I'm not having luck

fboucquez commented 1 year ago

Somebody who deleted the comment recommended https://github.com/supertestnet/inscriptions-online. It's not bitcoinjs but it has given me really good pointers. I'll try migrating it to use bitcoinjs. thanks!

fboucquez commented 1 year ago

I've finally solved creating bitcoin inscriptions using the latest bitcoinjs-lib. I've crated a repo if anybody has issues

https://github.com/fboucquez/ordinal-inscription-example-using-bitcoinjs-lib

junderw commented 1 year ago

It concerns me that the inscriptions protocol doesn't even mention minimal pushes at all... At the very least I would have hoped they strongly suggested minimal pushes, but for what it's designed for, a strict "MUST" for minimal push would have been appropriate in my opinion.

Now I guess it really just depends on the various explorers and how they implement it.

junderw commented 1 year ago

Also, if you want to add this as an integration test in our repo, it'll probably be more visible and also benefit the ability to be tested against each version of Core we use in our CI. (You should probably mention the non-minimal push issue in the integration test as well)

fboucquez commented 1 year ago

Hi @junderw , thank you for your comments. I'll create a PR porting the ordinal test cases here.

I've tried using minimal pushes (aka, Buffer.of([1]) = 15 hex rather 1,1 = 0101 hex)

So far, it has not been detected in any Ordinal explorer I know even when confirmed

https://www.blockchain.com/explorer/transactions/btc/2c75a32425246d0812ccbb3985795fe5c133311769ef38580f57a7d9d6a20e32 https://magiceden.io/ordinals/item-details/2c75a32425246d0812ccbb3985795fe5c133311769ef38580f57a7d9d6a20e32i0 https://ordinals.hiro.so/inscription/2c75a32425246d0812ccbb3985795fe5c133311769ef38580f57a7d9d6a20e32i0

I've also inscribed another ordinal using 0101 separator. I'm still waiting for confirmation, I'll check it out tomorrow morning. https://www.blockchain.com/explorer/transactions/btc/450088d408495c90cf63d796c9878f9e2fa2ab6d4dad96bfd51952e950960c55 https://ordinals.hiro.so/inscription/450088d408495c90cf63d796c9878f9e2fa2ab6d4dad96bfd51952e950960c55i0 https://magiceden.io/ordinals/item-details/450088d408495c90cf63d796c9878f9e2fa2ab6d4dad96bfd51952e950960c55i0

junderw commented 1 year ago

btw OP_PUSHNUM_1 is 0x51, not 0x15.

That's unfortunate that no one seems to support minimal push for the tags.

I wonder if they accept oversized push? In total there's 5 ways to push 0x01 on the stack and we only tested 2 ways. heh

If you're curious I'd be curious to see it too.

// Just remove the white space to figure out the bytes.
0x4c 01       01 // OP_PUSHDATA1_1 0x01
0x4d 0100     01 // OP_PUSHDATA2_1 0x01
0x4e 01000000 01 // OP_PUSHDATA4_1 0x01
fboucquez commented 1 year ago

Thanks. For some reason, my last mainnet execution detected the inscription but with an empty body. Checking the witness, it was corrupted.. Strange because I have created many inscriptions. I've just added another here via my e2e integration scripts

Just to confirm before running more tests, What's the script you want to try? I'm curious to try...

export function createInscriptionScript({ xOnlyPublicKey, inscription }) {
  assert(xOnlyPublicKey instanceof Buffer, `xOnlyPublicKey must be a Buffer`)
  assert(inscription, `inscription is required`)
  assert(inscription.content instanceof Buffer, `inscription.content must be a Buffer`)
  assert(inscription.contentType instanceof Buffer, `inscription.content must be a Buffer`)
  const protocolId = Buffer.from(encoder.encode('ord'))
  return [
    xOnlyPublicKey,
    bitcoinjsLib.opcodes.OP_CHECKSIG,
    bitcoinjsLib.opcodes.OP_0,
    bitcoinjsLib.opcodes.OP_IF,
    protocolId,
    1, 1, /// What goes here??
    inscription.contentType,
    bitcoinjsLib.opcodes.OP_0,
    inscription.content,
    bitcoinjsLib.opcodes.OP_ENDIF,
  ]
}
junderw commented 1 year ago

All 17 of these byte sequences will be read a the number 1 by Bitcoin's Script.

const possibleValuesForOne = [
    [0x51],
    [1, 1],
    [0x4c, 1, 1],
    [0x4d, 1, 0, 1],
    [0x4e, 1, 0, 0, 0, 1],
    [2, 1, 0],
    [0x4c, 2, 1, 0],
    [0x4d, 2, 0, 1, 0],
    [0x4e, 2, 0, 0, 0, 1, 0],
    [3, 1, 0, 0],
    [0x4c, 3, 1, 0, 0],
    [0x4d, 3, 0, 1, 0, 0],
    [0x4e, 3, 0, 0, 0, 1, 0, 0],
    [4, 1, 0, 0, 0],
    [0x4c, 4, 1, 0, 0, 0],
    [0x4d, 4, 0, 1, 0, 0, 0],
    [0x4e, 4, 0, 0, 0, 1, 0, 0, 0],
];

ie.

export function createInscriptionScript({ xOnlyPublicKey, inscription }) {
  assert(xOnlyPublicKey instanceof Buffer, `xOnlyPublicKey must be a Buffer`)
  assert(inscription, `inscription is required`)
  assert(inscription.content instanceof Buffer, `inscription.content must be a Buffer`)
  assert(inscription.contentType instanceof Buffer, `inscription.content must be a Buffer`)
  const protocolId = Buffer.from(encoder.encode('ord'))
  return [
    xOnlyPublicKey,
    bitcoinjsLib.opcodes.OP_CHECKSIG,
    bitcoinjsLib.opcodes.OP_0,
    bitcoinjsLib.opcodes.OP_IF,
    protocolId,
    0x4e, 4, 0, 0, 0, 1, 0, 0, 0, /// What goes here??
    inscription.contentType,
    bitcoinjsLib.opcodes.OP_0,
    inscription.content,
    bitcoinjsLib.opcodes.OP_ENDIF,
  ]
}
junderw commented 1 year ago

(Man, looking at this brings back memories of the days when transaction malleability was a thing that we all worried about... lol)