cmdruid / tapscript Goto Github PK
View Code? Open in Web Editor NEWA humble library for working with Tapscript and Bitcoin Transactions.
Home Page: https://www.npmjs.com/package/@cmdcode/tapscript
License: Creative Commons Zero v1.0 Universal
A humble library for working with Tapscript and Bitcoin Transactions.
Home Page: https://www.npmjs.com/package/@cmdcode/tapscript
License: Creative Commons Zero v1.0 Universal
I'm familiar with generating secret keys randomly and also from a secret phrase using bitcoinjs-lib.
How do I do the same thing with Tapscript? For instance, I have my secret phrase but I'm not sure how to format it to a hex string as shown in the examples.
It seems that the example is generating a public key (to which the funds need to be sent) in such a way that the pubkey depends on the target script that we want the spender to verify.
If I'm not mistaken the power of Taproot is to enable the commitment of funds to a not-pubic unlocking MAST (the whole Tree) and allow the spender to satisfy just one of the possible alternatives, inside the MAST. that way we are actually not using the MAST feature. The pubkey actually depends on the unlock script
Is there any plans to add support for PSBTs?
I want to create a specific transaction using tapscript similar to the ones shown in this example and I can't figure out how to put a 7 before the sig inside the witness. Can you please help ?
`const test = async () => {
// Switch this to true to enable console output.
const VERBOSE = true
// Create a keypair to use for testing.
const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
const seckey = utils.getSecretKey(secret)
const pubkey = utils.getPublicKey(seckey, true)
// Specify an array of scripts to use for testing.
const scripts = [
[ 7, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
[ 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
]
// Convert our array of scripts into tapleaves.
const tree = scripts.map(s => Tap.encodeScript(s))
if (VERBOSE) console.log('tree:', tree)
// Pick one of our scripts as a target for spending.
const index = 0
const script = scripts[index]
const target = Tap.encodeScript(script)
if (VERBOSE) console.log('target:', target)
// Generate a tapkey that includes our tree. Also, create a merlke proof
// (cblock) that targets our leaf and proves its inclusion in the tapkey.
const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { tree, target })
// A taproot address is simply the tweaked public key, encoded in bech32 format.
const address = Address.p2tr.fromPubKey(tpubkey, 'testnet')
if (VERBOSE) console.log('Your address:', address)
/* NOTE: To continue with this example, send 100_000 sats to the above address.
You will also need to make a note of the txid and vout of that transaction,
so that you can include that information below in the redeem tx.
*/
const txdata = Tx.create({
vin : [{
// Use the txid of the funding transaction used to send the sats.
txid: '3a9e52dc01bdb22c4b36a0490d73b06392bccf635e06ba3913339e733e7ab9a2',
// Specify the index value of the output that you are going to spend from.
vout: 0,
// Also include the value and script of that ouput.
prevout: {
// Feel free to change this if you sent a different amount.
value: 10_000,
// This is what our address looks like in script form.
scriptPubKey: [ 'OP_1', tpubkey ]
},
}],
vout : [{
// We are leaving behind 1000 sats as a fee to the miners.
value: 9_000,
// This is the new script that we are locking our funds to.
scriptPubKey: Address.toScriptPubKey('2MzcAVUFfHb1ni46AXCn5z7ADQKrKvz52nn')
}]
})
// For this example, we are signing for input 0 of our transaction,
// using the untweaked secret key. We are also extending the signature
// to include a commitment to the tapleaf script that we wish to use.
const sig = Signer.taproot.sign(seckey, txdata, 0, { extension: target })
// Add the signature to our witness data for input 0, along with the script
// and merkle proof (cblock) for the script.
txdata.vin[0].witness = [ sig, script, cblock ]
// Check if the signature is valid for the provided public key, and that the
// transaction is also valid (the merkle proof will be validated as well).
const isValid = await Signer.taproot.verify(txdata, 0, { pubkey })
if (VERBOSE) {
console.log('Your txhex:', Tx.encode(txdata).hex)
console.dir(txdata, { depth: null })
}
}`
Hi, I'm playing with taproot scripts via tapscript
and bumped into this blocker when trying to send raw transaction on Bitcoin Signet via bitcoin-cli -signet sendrawtransaction
:
error code: -26
error message:
non-mandatory-script-verify-flag (Public key version reserved for soft-fork upgrades)
Here is the code I'm using:
const {Address, Script, Signer, Tap, Tx} = require('@cmdcode/tapscript')
const utils = require('@cmdcode/crypto-utils')
const secret = '.....'
const seckey = utils.keys.get_seckey(secret)
const pubkey = utils.keys.get_pubkey(seckey)
const script = [....]
const tapleaf = Tap.encodeScript(script)
const [tpubkey, cblock] = Tap.getPubKey(pubkey, {target: tapleaf})
const address = Address.p2tr.fromPubKey(tpubkey, 'signet')
const txdata = Tx.create({
vin: [{
txid: '.....',
vout: 1,
prevout: {
value: 100000,
scriptPubKey: ['OP_1', tpubkey]
},
}],
vout: [
{
value: 90000,
scriptPubKey: Address.toScriptPubKey('tb1p.....')
}
]
})
const sig = Signer.taproot.sign(seckey, txdata, 0, {extension: tapleaf, throws: true})
txdata.vin[0].witness = [sig.hex, script, cblock]
await Signer.taproot.verify(txdata, 0, {pubkey, throws: true})
Is there anything wrong with how get_pubkey
derives public key? Or any other reason I might be getting this strange error? Thanks!
I have tried to inscribe file has size < 260b successfully according to the example, but for files larger than 260b it seems that encoding tx is having problems. it seems the 520 byte limit is misdefined leading to the wrong encoding (personally)
I got this error:
C:\path\to\my\project\node_modules\@cmdcode\tapscript\node_modules\@cmdcode\buff-utils\dist\module.mjs:353
const { getRandomValues } = crypto ?? globalThis.crypto ?? window.crypto;
^
ReferenceError: crypto is not defined
at Object.<anonymous> (C:\path\node_modules\@cmdcode\tapscript\node_modules\@cmdcode\buff-utils\dist\module.mjs:353:29)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
at Object.require.extensions.<computed> [as .js] (C:\path\to\AppData\Local\npm-cache\_npx\<random looking num here>\node_modules\ts-node\src\index.ts:1608:43)
at Module.load (node:internal/modules/cjs/loader:1117:32)
at Function.Module._load (node:internal/modules/cjs/loader:958:12)
at Module.require (node:internal/modules/cjs/loader:1141:19)
at require (node:internal/modules/cjs/helpers:110:18)
at Object.<anonymous> (C:\path\to\my\project\index.ts:1:1)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
When I try to run my script that looks like this "index.ts.":
const { Address, Signer, Tap, Tx, } = require('@cmdcode/tapscript')
function main() {
const address = 'address here'
const decoded = Address.decode(address)
console.log(decoded)
}
main()
I like this library a lot, but I have hit a blocker.
Tx.decode()
fails with this error for some transactions that have already been confirmed.
The error message is always the same: 38133322 > 56518
for the txn in my repro, below.
I notice this tends to happen with txns with many inputs, or many outputs.
Here's my repro script (node 19.9.0):
// Version 1.4.4 from npm.
import { Tx } from "@cmdcode/tapscript";
import * as lib_bitcoin_rpc from './lib_bitcoin_rpc.js'
async function main() {
// The following txid causes a decode script crash.
const txid = '74424af880bad9a82b0969b241fc8ee15db56ef5b852fc81649a7545a1e27036'
// The following txid does not cause a crash.
//const txid = '1cd87feb39f3696a0573cdfc5b981f6040954817fb1d59a082fec749693625b9'
// Get txn as hexstring.
const { result: rawTxn } = await lib_bitcoin_rpc.getRawTransaction(txid)
console.log(rawTxn)
const tx = Tx.decode(rawTxn)
console.log('Done.')
}
main()
Output:
020000000001fd24021615950c0d6e983937ca8cf33e8b94ec90b51b67868e66e1626170f942b2b2e80500000000fdffffff374f40f1b0b08e7f7ff144a5266c7e9ecdc3b8d30a7afec3ebd1b0e927ad3f812200000000fdffffff94090c725875db7194e1f47b2f1f1f8ef9c3c4aea3ea487e1c8b66fbadbfbaa60300000000fdfffffffb21f9f7c8b3028afac209af3c7d14e57e6e7dd3b45a452b37324892c71da8100600000000fdffffff8234d40bdb83594b8f9f6bc0f3889007f0ebfaea46bd38fb47de59a85821642b0100000000fdffffff60811e1de61a40b4ad98b55562461d5f380163d8682a1c39
...
c68c914817c641f4a29ae40121023e043fd3e13df9c2a8640adc0d07842fb490f93c5f307ce7d859ed277b83e14f16ae0c00
file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:1069
throw new Error(`Size greater than stream: ${size} > ${this.size}`);
^
Error: Size greater than stream: 38133322 > 56518
at Stream.peek (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:1069:19)
at Stream.read (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:1075:28)
at readData (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:6684:18)
at readScript (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:6688:18)
at readInput (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:6651:20)
at readInputs (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:6643:21)
at Object.decodeTx [as decode] (file:///home/donn/workspace/gitlab.com/proj/inscribe/node_modules/@cmdcode/tapscript/dist/module.mjs:6613:17)
at main (file:///home/donn/workspace/gitlab.com/proj/inscribe/hello_repro_decode_txn_error_for_publishing_to_issue.js:18:17)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v19.9.0
I hope this can be investigated because I would hate to port all my code to another lib.
Please let me know if there's more info I can provide.
Thank you
Just a small problem, in "inscribe.test.ts", L21, new URL() failed in the windowOS, and it should use absolute URL
Error: crypto.getRandomValues must be defined
at randomBytes$2 (/Users/sonpin/Documents/bitcoin/new/node_modules/@cmdcode/tapscript/dist/main.cjs:106:11)
at Function.random (/Users/sonpin/Documents/bitcoin/new/node_modules/@cmdcode/tapscript/dist/main.cjs:1480:25)
at sign (/Users/sonpin/Documents/bitcoin/new/node_modules/@cmdcode/tapscript/dist/main.cjs:7661:46)
at Object.signTx [as sign] (/Users/sonpin/Documents/bitcoin/new/node_modules/@cmdcode/tapscript/dist/main.cjs:7656:17)
I've got this issue when sign
Signer.taproot.sign(seckey, txdata, 0, { extension: tapleaf })
when i push tx i got this error...
here is my code
async function deploy(
secret: string,
brc20: string,
totalSupply: number,
mintLimit: number,
input: {txId: string; index: number; amount: number},
network: Networks,
) {
const text = `{"p":"brc-20","op":"deploy","tick":"${brc20}","max":"${totalSupply}","lim":"${mintLimit}"}`
const inscription = createTextInscription(text)
const seckey = keys.get_seckey(secret)
const pubkey = keys.get_pubkey(secret, true)
const tweakKey = Tap.tweak.getPubKey(pubkey)
const script = [
tweakKey,
"OP_CHECKSIG",
"OP_0",
"OP_IF",
Buffer.from(encoder.encode("ord")),
"01",
inscription.contentType,
"OP_0",
inscription.content,
"OP_ENDIF",
]
const tapleaf = Tap.encodeScript(script)
const [tpubkey, cblock] = Tap.getPubKey(tweakKey, {target: tapleaf})
const address = Address.p2tr.fromPubKey(tweakKey, network)
// const address = Address.p2tr.fromPubKey(tpubkey, network)
console.log(address, "address")
const txdata = Tx.create({
vin: [
{
// Use the txid of the funding transaction used to send the sats.
txid: input.txId,
// Specify the index value of the output that you are going to spend from.
vout: input.index,
// Also include the value and script of that ouput.
prevout: {
// Feel free to change this if you sent a different amount.
value: input.amount,
// This is what our address looks like in script form.
scriptPubKey: ["OP_1", tpubkey],
},
},
],
vout: [
{
// We are leaving behind 1000 sats as a fee to the miners.
value: 1000,
// This is the new script that we are locking our funds to.
scriptPubKey: Address.toScriptPubKey("tb1q7gnys2cwhkm7r73px6nus0g9dcr8mjh6fe2ums"),
},
{
value: input.amount - 5000,
scriptPubKey: Address.toScriptPubKey(address),
},
],
})
const sig = Signer.taproot.sign(seckey, txdata, 0, {extension: tapleaf})
txdata.vin[0].witness = [sig, script, cblock]
const isValid = Signer.taproot.verify(txdata, 0, {pubkey, throws: true})
console.log("Your txhex:", Tx.encode(txdata).hex, isValid)
// console.dir(txdata, {depth: null})
await boardCast(Tx.encode(txdata).hex)
}
Did I get something wrong?
Thanks!
const script = [ pubkey, 'OP_CHECKSIG', 'OP_0', 'OP_IF', marker, '01', mimetype, 'OP_0', imgdata, 'OP_ENDIF' ]
const tapleaf = Tap.encodeScript(script)
const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { target: tapleaf })
How can I decode script from tpubkey
Hi, when I follow the example “Basic Pay-to-Pubkey Spending” the Signature check at the end of the script returns True, however, when I submit the transaction hash to mempool.space, it tells me: non-mandatory-script-verify-flag Invalid-Schnorr-Signature.
Is the script correct? I am using testnet.
I was trying to add multiple parent to a child inscription .
So , according the the "Ordinal Theory Handbook" ,
I follow the first 2 points .According to the second bullet point I spend both parent inscription as first 2 inputs , and the creation of the child inscription is the 3rd input.
But my doubt is in the 3rd bullet point , Which serialised binary inscription ID should I include in the tag 3 the first parent's or the seconds , if i have to add both how do i do it?
According to @cmdcode/tapscript , how will I include 2 parent serialised binary inscription ids whiie designing the script manually
This seemingly random address appears in several places but I will use the inscribe.test.ts file as an example https://github.com/cmdruid/tapscript/blob/master/test/example/taproot/inscribe.test.ts. Notice this code:
const txdata = Tx.create({
vin : [{
// Use the txid of the funding transaction used to send the sats.
txid: 'b8ed81aca92cd85458966de90bc0ab03409a321758c09e46090988b783459a4d',
// Specify the index value of the output that you are going to spend from.
vout: 0,
// Also include the value and script of that ouput.
prevout: {
// Feel free to change this if you sent a different amount.
value: 100_000,
// This is what our address looks like in script form.
scriptPubKey: [ 'OP_1', tpubkey ]
},
}],
vout : [{
// We are leaving behind 1000 sats as a fee to the miners.
value: 99_000,
// This is the new script that we are locking our funds to.
scriptPubKey: Address.toScriptPubKey('bcrt1q6zpf4gefu4ckuud3pjch563nm7x27u4ruahz3y')
}]
})
See scriptPubKey: Address.toScriptPubKey('bcrt1q6zpf4gefu4ckuud3pjch563nm7x27u4ruahz3y')
... Why is bcrt1q6zpf4gefu4ckuud3pjch563nm7x27u4ruahz3y being used as the script that is locking the funds? Is this just an address chosen at random or is it derived from another piece of code that I'm missing?
Does it just mean that address where the UTXO is being spent chosen at random?
Will this this work for Runes also or do you have to change up a lot to make it work for Runes?
test/example/taproot/inscribe.test.ts
`
import { Buff } from '@cmdcode/buff'
import { taproot } from '../../../src/lib/sig/index.js'
import { tap_pubkey } from '../../../src/lib/tap/index.js'
import { assert } from '../../../src/index.js'
import {
get_seckey,
get_pubkey
} from '@cmdcode/crypto-tools/keys'
import {
parse_addr,
P2TR
} from '../../../src/lib/addr/index.js'
import {
encode_tx,
parse_tx
} from '../../../src/lib/tx/index.js'
import { Buffer } from 'buffer/'
const VERBOSE = false
function inscription() {
const ret_addr = "tb1ptv0luvng53jlddlae7y2898pgs9cqfxsaw0qsrcz4k0kvjsdtnsqvkvyzg"
const encoder = new TextEncoder()
try {
const text = {"p":"brc-20","op":"deploy","tick":"hffp","max":"210000","lim":"1"}
const imgdata = Buffer.from(encoder.encode(text))
const marker = Buff.encode('ord')
const mimetype = Buffer.from(encoder.encode('text/plain;charset=utf-8'))
const secret = 'your secret'
const seckey = get_seckey(secret)
const pubkey = get_pubkey(seckey, true)
const script = [pubkey, 'OP_CHECKSIG', 'OP_0', 'OP_IF', marker, '01', mimetype, 'OP_0', imgdata, 'OP_ENDIF']
const { tapkey, cblock } = tap_pubkey(pubkey, { script })
const address = P2TR.create(tapkey, 'testnet')
const txinput = {
"txid": "201d025a29b20985a329947910c293ce9640c639c834c416dcf6440d3ad4697d",
"vout": 0,
"satoshi": 4000000,
"scriptType": "5120",
"scriptPk": "51206602f211fb1a7a56c3c45422fdc5509876af35c095c4973029ae57ea7974edbc",
"codeType": 9,
"address": "tb1pvcp0yy0mrfa9ds7y2s30m32snpm27dwqjhzfwvpf4et757t5ak7qq66r4h",
"height": 2543743,
"idx": 67,
"isOpInRBF": false,
"inscriptions": []
}
const txdata = parse_tx({
vin: [{
// Use the txid of the funding transaction used to send the sats.
txid: txinput.txid,
// Specify the index value of the output that you are going to spend from.
vout: txinput.vout,
// Also include the value and script of that ouput.
prevout: {
// Feel free to change this if you sent a different amount.
value: txinput.satoshi,
// This is what our address looks like in script form.
scriptPubKey: ["OP_1", tapkey],
},
}],
vout: [{
value: 5000,
scriptPubKey: parse_addr(ret_addr).asm
}]
})
const sig = taproot.sign_tx(seckey, txdata, { script, txindex: 0 })
txdata.vin[0].witness = [sig, script, cblock]
const is_valid = taproot.verify_tx(txdata, { txindex: 0, pubkey, throws: true })
assert.ok(is_valid, 'Transaction failed validation.')
const txhex = encode_tx(txdata)
console.log(txhex.toString())
} catch (err) {
}
}
inscription()
`
020000000001017d69d43a0d44f6dc16c434c839c64096ce93c210799429a38509b2295a021d200000000000fdffffff0188130000000000002251205b1ffe3268a465f6b7fdcf88a394e1440b8024d0eb9e080f02ad9f664a0d5ce00340477a3553bc207d380298787fa597e40cd67fa92b5304f1e3826477c9e7792259e75cf72132c0c834d72717aa60ea019146b241ba77a4d08cfb24a24fc8f55cf18820fcf61b477e3ba7ed2fe14521da13960b61332f660204f6089bae65c6ba145685ac0063036f72645118746578742f706c61696e3b636861727365743d7574662d3800437b2270223a226272632d3230222c226f70223a226465706c6f79222c227469636b223a2268666670222c226d6178223a22323130303030222c226c696d223a2231227d6821c0fcf61b477e3ba7ed2fe14521da13960b61332f660204f6089bae65c6ba14568500000000
sendrawtransaction RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Witness program hash mismatch)"}
I performed a sats token mint transaction using the go-ord-tx repository without much knowledge. I did not know that the mempool limit was 25. My transaction was approved, but the script did not execute after 25 transactions. How can I make the utxos at the tahhahut address execute the script using this library? I'm having trouble creating the 1st and 3rd witness values. I'm just starting to learn. Can you help me?
follow inscribe.test.ts, broadcast tx to the mainnet, get error!
const txdata = Tx.create({
vin : [{
txid: '30fcb229bdef4ab1a9d864e2cb4b562c99b8ea00d68ff156f31ec1468ba6d893',
vout: 0,
prevout: {
value: 4_000,
scriptPubKey: [ 'OP_1', tpubkey ]
},
}],
vout : [{
value: 1_000,
scriptPubKey: Address.toScriptPubKey('bc1p93mun355nyke02gvdhx7vmes5l8enh2yw86r8x0fl98hwlug2dvq3xqj66')
}]
})
const sig = Signer.taproot.sign(seckey, txdata, 0, { extension: tapleaf })
txdata.vin[0].witness = [ sig, script, cblock ]
const response = await axios.post('https://blockstream.info/api/tx', Tx.encode(txdata).hex)
console.log(response.data)
print:
sendrawtransaction RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Public key version reserved for soft-fork upgrades)"}
here is my code
const txdata = Tx.create({
vin: [{
txid: '5b8ce54ca0993713552939b3d248c1f1b98c3ba18575f0d34f6b5ae13d58eac0',
vout: 0,
prevout: {
value: 5000,
scriptPubKey: ['OP_1', tpubkey]
}
}],
vout: [
{
value: 4000,
scriptPubKey: Address.toScriptPubKey(receiverAddress)
}
]
})
console.log(txdata)
const sig = Signer.taproot.sign(senderSeckey, txdata, 0, { extension: lockTapleaf })
txdata.vin[0].witness = [sig.hex, puzzle.toString('hex'), lockScript, cblock]
then, I want to set sequence in input like this
const txdata = Tx.create({
vin: [{
txid: '5b8ce54ca0993713552939b3d248c1f1b98c3ba18575f0d34f6b5ae13d58eac0',
vout: 0,
prevout: {
value: 5000,
scriptPubKey: ['OP_1', tpubkey]
},
sequence: 6,
}],
vout: [
{
value: 4000,
scriptPubKey: Address.toScriptPubKey(receiverAddress)
}
]
})
But I always got error "Transaction has superfluous witness data", what should I do?
The way address spending works is:
But how do I specify the order of input UTXOs with this library?
I want to make sure inscription UTXOs are first, while ordinary sats are last, so that inscriptions don't leak to miners.
Hey, is there any way to add metadata to the inscriptions when inscribing? Like it's done in the docs here https://docs.ordinals.com/inscriptions/metadata.html
ReferenceError: crypto is not defined
at Object. (node_modules/.store/@cmdcode[email protected]/node_modules/@cmdcode/tapscript/dist/main.cjs:339:48)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Module._load (node:internal/modules/cjs/loader:827:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object. (/Users/JunK/messi/king_chain/temp_typscript/index.js:2:46)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
It would be useful to have the ability to use custom address prefixes while doing address encoding. For example Litecoin. Found the encoding for it here.
Is it possible for a reveal transaction to not be valid, if the funding transaction went through (meaning the secret key is correct)? Provided the funding transaction was made from the same secret key.
Hello I get the following error in node js:
/@cmdcode/tapscript/dist/module.js:104
throw new Error('crypto.getRandomValues must be defined');
As a local work around I changed (line 56 in module.js)
const crypto$2 = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
to
import * as crypto$2 from 'crypto';
Examples are outdated.
E.g. this one (https://github.com/cmdruid/tapscript/blob/master/test/example/taproot/keyspend.test.ts) uses non-existent methods from "util".
Maybe you already know, would be awesome if you updated those.
Thanks in advance!
problem solved
How to get the byte count or vSize of a transaction in order to accurately calculate an adequate fee?
@cmdruid was trying to implement the runes specification on my existing codebase used for inscriptions.
I feel like this should be a straightforward task but for whatever reason once my txn is broadcast the block explorers show UNKNOWN rather than the OP_RETURN. runes protocol aside, i should be able to use an OP_RETURN with any data following it, right?
Here is some sample code
var payload = hexToBytes( '148dde9d011427' );
var rune = [ 'OP_RETURN', 'OP_13', payload ]
var runestone = Script.encode(rune);
i then add runestone
as the scriptpubkey of a 0 value txn output. the txn signs and broadcasts no problem.
here are some examples on testnet that shown UNKNOWN
It should look something like the OP_RETURN here.
any thoughts appreciated!
@cmdruid appreciate all the work and examples in this library!
I had a question regarding ordinal provenance as documented here.
I have an inscription flow in place which generally follows the example you provided for inscriptions. While I believe i have followed the ordinal provenance documentation correctly as far as the script construction and input values, I am having issues signing the transaction.
Code snippets below, any thoughts/suggestions on how to get this working is appreciated! I am currently getting RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Invalid Schnorr signature)"}
. The ordinal I am trying to use as the parent is here: https://mempool.space/testnet/tx/e13f106578fe0955b375cfb66e4a212fd794b7168a7da5873fada2b78c47e035
Could you show us the example?
import crypto from 'crypto'
is missing, hence throwing an error within node. browser is fine.
My code in Typescript const init_sig = Signer.taproot.sign(seckey, init_redeemtx, 0, { extension: init_leaf });
but it throws error:
throw new Error('crypto.getRandomValues must be defined');
I created an Azure function locally with the following commands in powershell:
func init --worker-runtime node --language typescript --docker func new --name generateBitcoinAddress --template "HTTP trigger"
This creates a boilerplate Azure function that looks like this:
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
export async function generateBitcoinAddress(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
context.log(`Http function processed request for url "${request.url}"`);
const name = request.query.get('name') || await request.text() || 'world';
return { body: `Hello, ${name}!` };
};
app.http('generateBitcoinAddress', {
methods: ['GET', 'POST'],
authLevel: 'anonymous',
handler: generateBitcoinAddress
});
I built it and deployed it on docker:
npm run clean
npm run build
docker build --tag YOUR_DOCKER_ID_HERE/azurefunctionsimage:v1.0.0 . docker run -p 8080:80 -it YOUR_DOCKER_ID_HERE/azurefunctionsimage:v1.0.0
It works, I hit the API with Postman using this post request:
curl --location 'http://localhost:8080/api/generateBitcoinAddress'
--header 'Content-Type: application/json'
--data 'Chris'
It printed:
Hello, Chris!
And I ran this locally without Docker as well:
npm run clean
npm run build
func start
To hit this local non-docker API, I altered my Postman command to use port 7071 instead of 8080 and it succeeded.
Now is the weird part. I modified the code to do something more complex. I took this test from @cmdcode/tapscript, https://github.com/cmdruid/tapscript/blob/master/test/example/taproot/keyspend.test.ts and converted it into an Azure Function:
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
import { keys } from '@cmdcode/crypto-tools';
import { Address, Tap, Tx, Signer } from '@cmdcode/tapscript';
export async function generateBitcoinAddress(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
try {
const body: any = await request.json();
const secret_key = body.secret;
const seckey = keys.get_seckey(secret_key)
const pubkey = keys.get_pubkey(seckey, true)
// For key spends, we need to get the tweaked versions
// of the secret key and public key.
const [ tseckey ] = Tap.getSecKey(seckey)
const [ tpubkey ] = Tap.getPubKey(pubkey)
// A taproot address is simply the tweaked public key, encoded in bech32 format.
const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')
console.log('Your address:', address)
/* NOTE: To continue with this example, send 100_000 sats to the above address.
You will also need to make a note of the txid and vout of that transaction,
so that you can include that information below in the redeem tx.
*/
const txdata = Tx.create({
vin : [{
// Use the txid of the funding transaction used to send the sats.
txid: '1ec5b5403bbc7f26a5d3a3ee30d69166a19fa81b49928f010af38fa96986d472',
// Specify the index value of the output that you are going to spend from.
vout: 1,
// Also include the value and script of that ouput.
prevout: {
// Feel free to change this if you sent a different amount.
value: 100_000,
// This is what our address looks like in script form.
scriptPubKey: [ 'OP_1', tpubkey ]
},
}],
vout : [{
// We are leaving behind 1000 sats as a fee to the miners.
value: 99_000,
// This is the new script that we are locking our funds to.
scriptPubKey: Address.toScriptPubKey('bcrt1q6zpf4gefu4ckuud3pjch563nm7x27u4ruahz3y')
}]
})
// For this example, we are signing for input 0 of our transaction,
// using the tweaked secret key.
const sig = Signer.taproot.sign(tseckey, txdata, 0)
// Let's add this signature to our witness data for input 0.
txdata.vin[0].witness = [ sig ]
// Check if the signature and transaction are valid.
const isValid = await Signer.taproot.verify(txdata, 0)
return { status: 200, jsonBody: { isValid: isValid, address: address, signature: sig }};
} catch (error) {
context.error(`Error processing request: ${error}`);
return { status: 500, jsonBody: { error: "Internal server error. Please try again later." }};
}
};
app.http('generateBitcoinAddress', {
methods: ['POST'],
authLevel: 'anonymous',
handler: generateBitcoinAddress
});
It worked perfectly when I ran it locally WITHOUT Docker...
npm install u/cmdcode/buff
npm install u/cmdcode/buff-utils\
npm install u/cmdcode/tapscript
npm install u/cmdcode/crypto-tools
npm run clean
npm run build
func start
Then in Postman, I did:
curl --location 'http://localhost:7071/api/generateBitcoinAddress'
--header 'Content-Type: application/json'
--data '{ "secret": "ccd54b99acec77d0537b01431579baef998efac6b08e9564bc3047b20ec1bb4c" }'
It worked and produced this output:
{
"isValid": true,
"address": "bcrt1pszn3ahyts27pr8zvqf3p0v53y5f0gwgcz85nwrdh2kxw4xxd00mqh3mjcv",
"signature": {
"0": 43,
"1": 98,
LOTS MORE ARRAY VALUES THAT I WILL OMIT FOR BREVITY
}
}
But when I took this working Azure Function, and tried to launch it in a docker container, it didn't work:
docker build --tag YOUR_DOCKER_ID_HERE/azurefunctionsimage:v1.0.0 .
docker run -p 8080:80 -it YOUR_DOCKER_ID_HERE/azurefunctionsimage:v1.0.0
I noticed this line in the output of the docker run command that wasn't present in the boilerplate code docker run command output:
No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
So I already knew it would fail because no function is at the API endpoint. But just to make sure it would fail, I altered my Postman command for port 8080:
curl --location 'http://localhost:8080/api/generateBitcoinAddress'
--header 'Content-Type: application/json'
--data '{ "secret": "ccd54b99acec77d0537b01431579baef998efac6b08e9564bc3047b20ec1bb4c" }'
And it gave me a 404 error.
So what is the problem? Why does my code work locally when I run it with func start, but doesn't work when I deploy it via Docker?
I have this transaction created with tapscript according to the inscription example , decoded from hex:
{
"addresses": [
"tb1peetwkzdue9eey9at87d72ea98zw4yz62mq5j49w8utcf28z4w4dq49yt5z",
"tb1pezwxd60p00laqcu6fvze0canaczel8rdh44u4yu5995xlczgk4asm3hksg"
],
"block_height": -1,
"block_index": -1,
"confirmations": 0,
"double_spend": false,
"fees": 1000,
"hash": "1899989d5f95661d60b8b5de873252085043cb528fe75b32040169335790421d",
"inputs": [
{
"addresses": [
"tb1peetwkzdue9eey9at87d72ea98zw4yz62mq5j49w8utcf28z4w4dq49yt5z"
],
"age": 2536373,
"output_index": 1,
"output_value": 20000,
"prev_hash": "fa2418bba0a89052bb9f045ed3d9b509e58ffb8d76b3b05aa04b1e76dbc1204e",
"script_type": "pay-to-taproot",
"sequence": 4294967293,
"witness": [
"05cfdbc9641646e51b61dac7ace4436748429004b6e44affbbcbb303126f0ef8a274b4d9d6fcabd3f0eb1b44f9b1e9f6e00b34a218caae4d73d7b50bbcde014e",
"21021344f6fb0eb5b4ad25dc7db247573e92a1bd2f342317db3e931972434fb9b542ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38000068",
"c11344f6fb0eb5b4ad25dc7db247573e92a1bd2f342317db3e931972434fb9b542"
]
}
],
"opt_in_rbf": true,
"outputs": [
{
"addresses": [
"tb1pezwxd60p00laqcu6fvze0canaczel8rdh44u4yu5995xlczgk4asm3hksg"
],
"script": "5120c89c66e9e17bffd0639a4b0597e3b3ee059f9c6dbd6bca939429686fe048b57b",
"script_type": "pay-to-taproot",
"value": 19000
}
],
"preference": "low",
"received": "2023-11-02T15:54:02.249240475Z",
"relayed_by": "44.202.163.146",
"size": 268,
"total": 19000,
"ver": 2,
"vin_sz": 1,
"vout_sz": 1,
"vsize": 138
}
When I try to broadcast the transaction I get this error: 'sendrawtransaction RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Witness program hash mismatch)"}'
How do you import the SecretKey constructor?
It's used in the examples.
I have a method to send sats from one p2tr address to another one. I followed this example to implement it.
The validation goes through, but when I submit the transaction to mempool.space (or blockstream, same thing), I get the following response:
sendrawtransaction RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Invalid Schnorr signature)"}
Here is the code I send sats with (essentially the same as the example, but with many UTXOs in tx input):
https://pastebin.com/hBGXNnJe
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.