Full Code of cmdruid/tapscript for AI

master 10459abc7d50 cached
94 files
15.1 MB
214.7k tokens
289 symbols
1 requests
Download .txt
Showing preview only (527K chars total). Download the full file or copy to clipboard to get everything.
Repository: cmdruid/tapscript
Branch: master
Commit: 10459abc7d50
Files: 94
Total size: 15.1 MB

Directory structure:
gitextract_1jxgzyz8/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── rollup.config.ts
├── src/
│   ├── class/
│   │   ├── Signature/
│   │   │   └── index.ts
│   │   └── Transaction/
│   │       ├── Transaction.ts
│   │       ├── TxInput.ts
│   │       ├── TxLocktime.ts
│   │       ├── TxOutput.ts
│   │       ├── TxScript.ts
│   │       ├── TxSequence.ts
│   │       ├── TxWitness.ts
│   │       └── index.ts
│   ├── index.ts
│   ├── lib/
│   │   ├── addr/
│   │   │   ├── hash.ts
│   │   │   ├── index.ts
│   │   │   ├── p2pkh.ts
│   │   │   ├── p2sh.ts
│   │   │   ├── p2tr.ts
│   │   │   ├── p2w-pkh.ts
│   │   │   ├── p2w-sh.ts
│   │   │   ├── schema.ts
│   │   │   └── utils.ts
│   │   ├── check.ts
│   │   ├── script/
│   │   │   ├── decode.ts
│   │   │   ├── encode.ts
│   │   │   ├── format.ts
│   │   │   ├── index.ts
│   │   │   └── words.ts
│   │   ├── sig/
│   │   │   ├── index.ts
│   │   │   ├── segwit/
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── sign.ts
│   │   │   │   └── verify.ts
│   │   │   ├── taproot/
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── sign.ts
│   │   │   │   └── verify.ts
│   │   │   └── types.ts
│   │   ├── tap/
│   │   │   ├── index.ts
│   │   │   ├── key.ts
│   │   │   ├── tree.ts
│   │   │   ├── tweak.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   ├── tx/
│   │   │   ├── create.ts
│   │   │   ├── decode.ts
│   │   │   ├── encode.ts
│   │   │   ├── format.ts
│   │   │   ├── index.ts
│   │   │   └── parse.ts
│   │   └── utils.ts
│   └── schema/
│       ├── check.ts
│       └── types.ts
├── test/
│   ├── bin/
│   │   ├── bitcoin-cli
│   │   └── bitcoind
│   ├── bitcoin.conf
│   ├── core.ts
│   ├── example/
│   │   ├── ex_test.ts
│   │   └── taproot/
│   │       ├── inscribe.test.ts
│   │       ├── keyspend.test.ts
│   │       ├── tapscript.test.ts
│   │       └── taptree.test.ts
│   ├── index.html
│   ├── scratch.ts
│   ├── src/
│   │   ├── addr/
│   │   │   ├── addr.test.ts
│   │   │   ├── p2pkh.test.ts
│   │   │   ├── p2sh.test.ts
│   │   │   ├── p2tr.test.ts
│   │   │   ├── p2wpkh.test.ts
│   │   │   └── p2wsh.test.ts
│   │   ├── sig/
│   │   │   ├── segwit/
│   │   │   │   ├── bip0143.vectors.json
│   │   │   │   ├── sighash.test.ts
│   │   │   │   ├── sighash.vectors.json
│   │   │   │   └── utils.ts
│   │   │   ├── sig.test.ts
│   │   │   └── taproot/
│   │   │       ├── sig.test.ts
│   │   │       ├── sig.vectors.json
│   │   │       ├── tx.test.ts
│   │   │       └── tx.vectors.json
│   │   ├── tap/
│   │   │   ├── tree.test.ts
│   │   │   ├── tree.vectors.json
│   │   │   ├── unit.test.ts
│   │   │   └── unit.vectors.json
│   │   └── tx/
│   │       ├── segwit/
│   │       │   ├── segwit.test.ts
│   │       │   ├── utils.ts
│   │       │   └── valid.vectors.json
│   │       └── tx.test.ts
│   ├── tape.ts
│   ├── tsconfig.json
│   └── utils.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

bitcoin: bitcoin:bc1qnx8julmud2vqdqngxw9rmuv3js67kq9rzvtmm5
lnurl: lnurl:cmd@stacker.news


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# nyc
coverage
.nyc_output

# Dependency directories
node_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity
.yarn*

# NPM config
.npmrc

# Env config
.env*

# Distributions
dist

test/data


================================================
FILE: LICENSE
================================================
Creative Commons Legal Code

CC0 1.0 Universal

    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
    HEREUNDER.

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.

For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:

  i. the right to reproduce, adapt, distribute, perform, display,
     communicate, and translate a Work;
 ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
     likeness depicted in a Work;
 iv. rights protecting against unfair competition in regards to a Work,
     subject to the limitations in paragraph 4(a), below;
  v. rights protecting the extraction, dissemination, use and reuse of data
     in a Work;
 vi. database rights (such as those arising under Directive 96/9/EC of the
     European Parliament and of the Council of 11 March 1996 on the legal
     protection of databases, and under any national implementation
     thereof, including any amended or successor version of such
     directive); and
vii. other similar, equivalent or corresponding rights throughout the
     world based on applicable law or treaty, and any national
     implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.

4. Limitations and Disclaimers.

 a. No trademark or patent rights held by Affirmer are waived, abandoned,
    surrendered, licensed or otherwise affected by this document.
 b. Affirmer offers the Work as-is and makes no representations or
    warranties of any kind concerning the Work, express, implied,
    statutory or otherwise, including without limitation warranties of
    title, merchantability, fitness for a particular purpose, non
    infringement, or the absence of latent or other defects, accuracy, or
    the present or absence of errors, whether or not discoverable, all to
    the greatest extent permissible under applicable law.
 c. Affirmer disclaims responsibility for clearing rights of other persons
    that may apply to the Work or any use thereof, including without
    limitation any person's Copyright and Related Rights in the Work.
    Further, Affirmer disclaims responsibility for obtaining any necessary
    consents, permissions or other rights required for any use of the
    Work.
 d. Affirmer understands and acknowledges that Creative Commons is not a
    party to this document and has no duty or obligation with respect to
    this CC0 or use of the Work.


================================================
FILE: README.md
================================================
# Tapscript

A basic library for working with Taproot, Schnorr Signatures, and Bitcoin transactions.

> Note: For nodejs users, please upgrade to version 19+ for globalThis support.

## Introduction

Tapscript uses the latest feature upgrade to Bitcoin called Taproot. If you are new to Bitcoin or the Taproot upgrade, please continue reading for a brief overview of how it works. This library will be easier to follow if you know what taproot is doing under the hood.

If you already have a good understanding of Bitcoin and Taproot, feel free to skip ahead by clicking [here](#tool-index).

## What is Taproot?

Bitcoin uses a simple scripting language (called Bitcoin Script) that allows you to lock up coins into a contract. These contracts are published to the blockchain and enforced by all nodes in the network.

In order to settle a contract (and claim its coins), you are required to publish the *entire* contract, including parts that are not relevant to the settlement. This is expensive and wasteful, plus it leaks information that could have otherwise been kept private.

Taproot is a new way to publish these contracts to the blockchain that fixes the above concerns. It allows you to settle contracts by publishing only the portion of the contract that is relevant. This means smaller transactions, cheaper fees, and better privacy guarantees for the contract as a whole.

Taproot also comes with many other benefits, including:

 * It drastically simplifies the flow and logic of writing a contract.
 * You can create large, complex contracts that only need a small transaction to settle.
 * Commitments to data and other arbitrary things can be thrown into your contract for free.
 * The new schnorr-based signature format lets you do some crazy cool stuff (BIP340).

Read more about the Taproot upgrade in 2019 [here](https://cointelegraph.com/bitcoin-for-beginners/a-beginners-guide-to-the-bitcoin-taproot-upgrade).

## How does Taproot work?

Taproot uses a simple trick involving something called a "merkle tree".

```
                hash(ab, cd)                  <- Final hash    (the root)
              /             \                
      hash(a, b)             hash(c, d)       <- Combined hash (the branches)
     /          \           /          \    
    hash(a) hash(b)        hash(c) hash(d)    <- Initial hash  (the leaves)
[ script(a), script(b), script(c), script(d) ]  
```

A merkle tree is simply a list of data that is reduced down into a single hash value. We do this by hashing values together in pairs of two, repeatedly, until we are naturally left with one value (the root).

The great thing about merkle trees is that you can use the root hash to prove that a piece of data (such as a script) was included somewhere in the tree, without having to reveal the entire tree.

For example, to prove that script(a) exists in the tree, we simply provide hash(b) and hash(c, d). This is all the information we need to recreate the root hash(ab, cd). We do not reveal any of the other scripts.

This allows us to break up a contract into many scripts, then lock coins to the root hash of our combined scripts. To redeem coins, we simply need to provide one of the scripts, plus a 'path' of hashes that lead us to the root hash of the tree.

## About Key Tweaking

Another clever trick that Taproot uses, is something called "key tweaking".

In order to create a pair of keys used for signatures, we start with a secret number, or "key". We then multiply this key by a very large prime number, called a "generator" (G). This process is done in a way that is computationally impossible to reverse without knowing the secret. The resulting number then becomes our public key.

```
seckey * G => pubkey
```

We use a special set of numbers when making key-pairs, so that some arithmetic still works between the keys, without breaking their secret relationship with G. This is how we produce signatures and proofs.

```
seckey +    randomkey    +    msg    = signature      <= Does not reveal seckey.
pubkey + (randomkey * G) + (msg * G) = signature * G  <= Proves that seckey was used.
```

Key tweaking is just an extention of this. We use a piece of data to "tweak" both keys in our key-pair, then use the modified keys to sign and verify transactions.

```
seckey +    tweak    = tweaked_seckey
pubkey + (tweak * G) = tweaked_pubkey
```

Later, we can choose to reveal the original public key and tweak, as proof that both were used to construct the modified key. Or we can simply choose to sign using the modified key, and not reveal anything!

Taproot uses key tweaking in order to lock coins to our pubkey + root of our tree. This provides us with two paths for spending coins:

 * Using the tweaked pubkey (without revealing anything).
 * Using the interal pubkey + script + proof.

> Note: the "proof" is the path of hashes we described earlier, which is needed to recompute the root hash.

If you want to eliminate the key-spending path (so that a script *must* be used to redeem funds), you can replace the pubkey with a random number. However, it is best to use a number that everyone can verify has an unknown secret key. One example of such a number is the following:

```js
// BIP0341 specifies using the following pubkey value for script-only contracts.
// It is created by hashing the DER encoded coordinates of secp256k1 base point G:
'0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0'
// Since this pubkey was generated using a hash function, there is no feasible way 
// to compute what the matching private key would be.
```

## Tool Index

This library provides a suite of tools for working with scripts, taproot, key tweaking, signatures and transactions. Use the links below to jump to the documentation for a certain tool.

[**Address Tool**](#address-tool)  
Encode, decode, check, and convert various address types.  
[**Script Tool**](#script-tool)  
Encode scripts into hex, or decode into a script array.  
[**Signer Tool**](#signer-tool)  
Produce signatures and validate signed transactions.  
[**Tap Tool**](#tap-tool)  
Build, tweak, and validate trees of data / scripts.  
[**Tx Tool**](#tx-tool)  
Encode transactions into hex, or decode into a JSON object.

### About Buff

This library makes heavy use of the [Buff](https://github.com/cmdruid/buff-utils) tool for converting between data types. Buff is an extention of the Uint8Array type, so all Buff objects can naturally be treated as Uint8Array objects. Buff objects however incude an extensive API for converting into different types (*for ex: buff.hex for hex strings*). Please check the above link for more information on how to use Buff.

### Import

Example import into a browser-based project:
```html
<script src="https://unpkg.com/@cmdcode/tapscript"></script>
<script> const { Address, Script, Signer, Tap, Tx } = window.tapscript </script>
```
Example import into a commonjs project:
```ts
const { Address, Script, Signer, Tap, Tx } = require('@cmdcode/tapscript')
```
Example import into an ES module project:
```ts
import { Address, Script, Signer, Tap, Tx } from '@cmdcode/tapscript'
```

### Address Tool

This tool allows you to encode, decode, check, an convert various address types.

```ts
Address = {
  // Work with Pay-to-Pubkey-Hash addresses (Base58 encoded).
  p2pkh  : => AddressTool,
  // Work with Pay-to-Script-Hash addresses (Base58 encoded).
  p2sh   : => AddressTool,
  // Work with Pay-to-Witness PubKey-Hash addresses (Bech32 encoded).
  p2wpkh : => AddressTool,
  // Work with Pay-to-Witness Script-Hash addresses (Bech32 encoded).
  p2wsh  : => AddressTool,
  // Work with Pay-to-Taproot addresses (Bech32m encoded).
  p2tr   : => AddressTool,
  // Decode any address format into a detailed object.
  decode   : (address : string) => AddressData,
  // Convert any address into its scriptPubKey format.
  toScriptPubKey : (address : string) => Buff
}

interface AddressTool {
  // Check if an address is valid.
  check  : (address : string, network ?: Networks) => boolean
  // Decode an address into a pubkey hash or script hash.
  decode : (address : string, network ?: Networks) => Buff
  // Convert a key or script into the proper hash.
  hash   : (input : Bytes | ScriptData) => Buff
  // Encode a pubkey hash or script hash into an address.
  encode : (input : Bytes,  network ?: Networks) => string
  // Return the scriptPubKey script for an address type.
  scriptPubKey : (input : string) => string[]
  // Return an address based on a public key (PKH type addresses only).
  fromPubKey : (pubkey : Bytes, network ?: Networks) => string
  // Return an address based on a script key (SH type addresses only).
  fromScript : (script : ScriptData, network ?: Networks) => string
}

interface AddressData {
  data    : Buff
  network : Networks
  prefix  : string
  script  : string[]
  type    : keyof AddressTools
}

type Networks = 'main' | 'testnet' | 'signet' | 'regtest'
```

#### Examples

Example of using the main `Address` API.

```ts
const address = 'bcrt1q738hdjlatdx9xmg3679kwq9cwd7fa2c84my9zk'
// You can decode any address, extract data, or convert to a scriptPubKey format.
const decoded = Address.decode(address)
// Example of the decoded data object.
{ 
  prefix  : 'bcrt1q', 
  type    : 'p2w', 
  network : 'regtest', 
  data    : 'f44f76cbfd5b4c536d11d78b6700b8737c9eab07',
  script  : [ 'OP_0', 'f44f76cbfd5b4c536d11d78b6700b8737c9eab07' ]
}
// You can also quickly convert between address and scriptPubKey formats.
const bytes = Address.toScriptPubKey(address)
// Bytes: 0014f44f76cbfd5b4c536d11d78b6700b8737c9eab07
const address = Address.fromScriptPubKey(scriptPubKey)
// Address : bcrt1q738hdjlatdx9xmg3679kwq9cwd7fa2c84my9zk
```

Example of using the AddressTool API for a given address type.

```ts
// Example 33-byte public key.
const pubkey  = '03d5af2a3e89cb72ff9ca1b36091ca46e4d4399abc5574b13d3e56bca6c0784679'
// You can encode / decode / convert keys and script hashes.
const address = Address.p2wpkh.fromPubKey(pubkey, 'regtest')
// Address: bcrt1q738hdjlatdx9xmg3679kwq9cwd7fa2c84my9zk
const address = Address.p2wpkh.encode(keyhash, 'regtest')
// Address: bcrt1q738hdjlatdx9xmg3679kwq9cwd7fa2c84my9zk
const bytes   = Address.p2wpkh.decode(address)
// KeyHash: f44f76cbfd5b4c536d11d78b6700b8737c9eab07
const script  = Address.p2wpkh.scriptPubKey(bytes)
// script: script: [ 'OP_0', 'f44f76cbfd5b4c536d11d78b6700b8737c9eab07' ]
```

### Script Tool

This tool helps with parsing / serializing scripts.

```ts
Script = {
  // Encode a JSON formatted script into hex.
  encode : (script : ScriptData, varint = true) => string,
  // Decode a hex formatted script into JSON.
  decode : (script : string, varint = false)    => ScriptData
  // Normalize script / data to a particular format:
  fmt : {
    // Convert script to opcodes / hex data (asm format).
    toAsm()   => string[]  (asm format).
    // Convert script to bytes (script hex).
    toBytes() => Buff
     // Convert non-script witness data to bytes.
    toParam() => Buff  
  }
}
```

### Signer Tool.

This tool helps with signatures and validation.

```ts
Signer.taproot = {
  // Calculate the signature hash for a transaction.
  hash : (
    txdata  : TxData | Bytes,
    index   : number,
    config  : HashConfig = {}
  ) => Uint8Array,
  // Sign a transaction using your *tweaked* private key.
  sign : (
    seckey  : Bytes,
    txdata  : TxData | Bytes,
    index   : number,
    config  : HashConfig = {}
  ) => Uint8Array,
  // Verify a transaction using the included tapkey (or specify a pubkey).
  verify : (
    txdata  : TxData | Bytes,
    index   : number,
    config  : HashConfig = {}
  ) => boolean
}

interface HashConfig {
  extension     ?: Bytes    // Hash and sign using this tapleaf.
  pubkey        ?: Bytes    // Verify using this pubkey instead of the tapkey.
  script        ?: Bytes    // Hash and sign using this script (for segwit spends).
  sigflag       ?: number   // Set the signature type flag.
  separator_pos ?: number   // If using OP_CODESEPARATOR, specify the latest opcode position.
  extflag       ?: number   // Set the extention version flag (future use).
  key_version   ?: number   // Set the key version flag (future use).
  throws        ?: boolean  // Should throw an exception on failure.
}
```

#### Example

Example of a basic pay-to-taproot key spend (similar to pay-to-pubkey):

```ts
// Sample secret / public key pair.
const seckey  = '730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6'
const pubkey  = '0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3'

// For key-spends, we need to tweak both the secret key and public key.
const [ tseckey ] = Tap.getSecKey(seckey)
const [ tpubkey ] = Tap.getPubKey(pubkey)

// Our taproot address is the encoded version of our public tapkey.
const address = Address.p2tr.encode(tpubkey, 'regtest')

// NOTE: For the next step, you need to send 100_000 sats to the above address.
// Make note of the txid of this transaction, plus the index of the output that
// you are spending.

const txdata = Tx.create({
  vin  : [{
    // The txid of your funding transaction.
    txid: 'fbde7872cc1aca4bc93ac9a923f14c3355b4216cac3f43b91663ede7a929471b',
    // The index of the output you are spending.
    vout: 0,
    // For Taproot, we need to specify this data when signing.
    prevout: {
      // The value of the output we are spending.
      value: 100000,
      // This is the script that our taproot address decodes into.
      scriptPubKey: [ 'OP_1', tpubkey ]
    },
  }],
  vout : [{
    // We are locking up 99_000 sats (minus 1000 sats for fees.)
    value: 99000,
    // We are locking up funds to this address.
    scriptPubKey: Address.toScriptPubKey('bcrt1q6zpf4gefu4ckuud3pjch563nm7x27u4ruahz3y')
  }]
})

// For this example, we are signing for input 0.

// Provide your tweaked secret key with the transaction, 
// plus the index # of the input you are signing for.
const sig = Signer.taproot.sign(tseckey, txdata, 0)

// Add your signature to the witness data for that input.
txdata.vin[0].witness = [ sig ]

// For verification, provided your 
await Signer.taproot.verify(txdata, 0, { throws: true })

console.log('Your address:', address)
console.log('Your txhex:', Tx.encode(txdata).hex)
```

You can find more examples in the main **Examples** section further down.

> Note: There is also an identical `Signer.segwit` tool for signing and validating segwit (BIP0143) transactions. The segwit signer currently does not support the use of OP_CODESEAPRATOR. Any scripts containing this opcode will throw an exception by default.

### Tap Tool

```ts
Tap = {
  // Returns the tweaked public key (and cblock) for a given tree (and target).
  getPubKey    : (pubkey : Bytes, config ?: TapConfig)  => TapKey,
  // Returns the tweaked secret key (and cblock) for a given tree (and target).
  getSecKey    : (seckey : Bytes, config ?: TapConfig)  => TapKey,
  // Converts a script into a tapleaf (for script-based spending).
  encodeScript : (script: ScriptData, version?: number) => string,
  // Checks the validity of a given leaf target and control block.
  checkPath    : (  
    tapkey : Bytes, 
    target : Bytes, 
    cblock : Bytes, 
    config ?: TapConfig  
  ) => boolean,  
  // Gives access to the various sub-tools (described below).
  tree  : TreeTool,
  tweak : TweakTool,
  util  : UtilTool
}

interface TapConfig {
  isPrivate ?: boolean
  target    ?: Bytes
  tree      ?: TapTree
  throws    ?: boolean
  version   ?: number
}

type TapKey = [
  tapkey : string,  // The tweaked public key.
  cblock : string   // The control block needed for spending the tapleaf target.
]
```

#### Examples

Example of tapping a key with no scripts (key-spend).

```ts
const [ tapkey ] = Tap.getPubKey(pubkey)
```

Example of tapping a key with a single script and returning a proof.

```ts
// Encode the script as bytes.
const bytes = Script.encode([ 'script' ])
// Convert the bytes into a tapleaf.
const target = Tap.tree.getLeaf(bytes)
// Provide the tapleaf as a target for generating the proof.
const [ tapkey, cblock ] = Tap.getPubKey(pubkey, { target })
```

Example of tapping a key with many scripts.

```ts
const scripts = [
  [ 'scripta' ],
  [ 'scriptb' ],
  [ 'scriptc' ]
]

// Convert the scripts into an array of tap leaves.
const tree = scripts
  .map(e => Script.encode(e))
  .map(e => Tap.tree.getLeaf(e))

// Optional: You can also add data to the tree.
const bytes = encodeData('some data')
const leaf  = Tap.tree.getLeaf(bytes)
tree.push(leaf)

// Select a target leaf for generating the proof.
const target = tree[0]

// Provide the tree and target leaf as arguments.
const [ tapkey, cblock ] = Tap.getPubKey(pubkey, { tree, target })
```

### Tree Tool

This tool helps with creating a tree of scripts / data, plus the proofs to validate items in the tree.

```ts
Tap.tree = {
  // Returns a 'hashtag' used for padding. Mainly for internal use.
  getTag    : (tag : string) => Buff,
  // Returns a 'tapleaf' used for building a tree. 
  getLeaf   : (data : Bytes, version ?: number) => string,
  // Returns a 'branch' which combines two leaves (or branches).
  getBranch : (leafA : string, leafB : string) => string,
  // Returns the root hash of a tree.
  getRoot   : (leaves : TapTree) => Buff,
}

// A tree is an array of leaves, formatted as strings.
// These arrays can also be nested in multiple layers.
type TapTree = Array<string | string[]>
```

### Tweak Tool

This tool helps with tweaking public / secret (private) keys.

```ts
Tap.tweak = {
  // Return a tweaked private key using the provided raw data.
  getSeckey   : (seckey: Bytes, data ?: Bytes | undefined) => Buff,
  // Return a tweaked public key using the provided raw data.
  getPubkey   : (pubkey: Bytes, data ?: Bytes | undefined) => Buff,
  // Return a 'taptweak' which is used for key tweaking.
  getTweak    : (key : Bytes, data ?: Bytes, isPrivate ?: boolean) => Buff,
  // Return a tweaked secret key using the provided tweak.
  tweakSeckey : (seckey: Bytes, tweak: Bytes) => Buff,
  // Return a tweaked public key using the provided tweak.
  tweakPubkey : (seckey: Bytes, tweak: Bytes) => Buff
}
```

### Util Tool

This tool provides helper methods for reading and parsing data related to taproot.

```ts
Tap.util = {
  readCtrlBlock : (cblock : Bytes) => CtrlBlock,
  readParityBit : (parity ?: string | number) => number
}

interface CtrlBlock {
  version : number
  parity  : number
  intkey  : Buff
  paths   : string[]
}
```

#### Example

```ts
const cblock = 'c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27'
const { intkey, parity, paths, version } = Tap.util.readCtrlBlock(cblock)
// Expected output, with key formatted as hex instead of bytes (for readability).
{
  intkey: '187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27',
  parity: 3,
  paths: [],
  version: 192
}
```

### Tx Tool

This tool helps with parsing / serializing transaction data.

```ts
Tx = {
  // Create a transaction object from partial JSON. 
  // Any missing fields will be repalced with default values.
  create : (data : Partial<TxData>) => TxData,
  // Serialize a JSON transaction into a hex-encoded string.
  encode : (
    txdata       : TxData,  // The transaction JSON.
    omitWitness ?: boolean  // If you wish to omit the witness data.
  ) => string,
  // Parse a hex-encoded transaction into a JSON object.
  decode : (bytes : string | Uint8Array) => TxData,
  // Normalize transaction data to a particular format.
  fmt : {
    // Convert transaction data into JSON format.
    toJson  : (txdata ?: TxData | Bytes) => TxData,
    // Convert transaction data into a byte format.
    toBytes : (txdata ?: TxData | Bytes) => Buff
  },
  util : {
    // Get the transaction Id of a transaction.
    getTxid : (txdata : TxData | Bytes) => Buff,
    // Get the size data of a transaction.
    getTxSize : (txdata : TxData | Bytes) => TxSizeData,
    // Parse a scriptPubKey and get the type plus hash data.
    readScriptPubKey : (script : ScriptData) => ScriptPubKeyData,
    // Parse an array of witness data into named values.
    readWitness : (witness : ScriptData[])  => WitnessData
  }
}

interface TxData {
  version  ?: number           // The transaction verion. Defaults to version 2.
  vin       : InputData[]      // An array of transaction inputs.
  vout      : OutputData[]     // An array of transaction outputs.
  locktime ?: LockData         // The locktime of the transaction. Defautls to 0.
}

interface InputData {
  txid : string               // The txid of the UTXO being spent.
  vout : number               // The output index of the UTXO being spent.
  prevout   ?: OutputData     // The output data of the UTXO being spent.
  scriptSig ?: ScriptData     // The ScriptSig field (mostly deprecated).
  sequence  ?: SequenceData   // The sequence field for the input.
  witness   ?: ScriptData[]   // An array of witness data for the input.
}

interface OutputData {
  value : number | bigint     // The satoshi value of the output.
  scriptPubKey : ScriptData   // The locking script data.
}

export interface ScriptPubKeyData {
  type : OutputType
  data : Buff
}

interface WitnessData {
  annex  : Buff | null  // The annex data (if present) or null.
  cblock : Buff | null  // The control block (if present) or null.
  script : Buff | null  // The redeem script (if present) or null.
  params : Bytes[]      // Any remaining witness arguments.
}

interface TxSizeData {
  size   : number       // Size of the transaction in bytes.
  bsize  : number       // Base size of the tx (without witness data).
  vsize  : number       // Size of the tx with witness discount applied.
  weight : number       // Used to calculate the vsize of the tx.
}

type SequenceData = string | number
type LockData     = number
type ScriptData   = Bytes  | Word[]
type Word         = string | number | Uint8Array
type Bytes        = string | Uint8Array
```

#### Transaction Object

This is an example transaction in JSON format.

```ts
const txdata = {
  version: 2
  vin: [
    {
      txid: '1351f611fa0ae6124d0f55c625ae5c929ca09ae93f9e88656a4a82d160d99052',
      vout: 0,
      prevout: { 
        value: 10000,
        scriptPubkey: '512005a18fccd1909f3317e4dd7f11257e4428884902b1c7466c34e7f490e0e627da'
        
      },
      sequence: 0xfffffffd,
      witness: []
    }
  ],
  vout: [
    { 
      value: 9000, 
      address: 'bcrt1pqksclnx3jz0nx9lym4l3zft7gs5gsjgzk8r5vmp5ul6fpc8xyldqaxu8ys'
    }
  ],
  locktime: 0
}
```

## Example Transactions

Here are a few partial examples to help demonstrate using the library. Check out the [`test/example/taproot`](test/example/taproot/) directory to see a full implementation of each example.

Please feel free to contribute more!

### Basic Pay-to-Pubkey Spending

Full example: [keyspend.test.ts](test/example/taproot/keyspend.test.ts)

```ts
// Create a keypair to use for testing.
const secret = 'ccd54b99acec77d0537b01431579baef998efac6b08e9564bc3047b20ec1bb4c'
const seckey = new SecretKey(secret, { type: 'taproot' })
const pubkey = seckey.pub

// 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)

// Optional: You could also derive the public key from the tweaked secret key.
const _tpubkey_example = new SecretKey(tseckey).pub.x.hex

// A taproot address is simply the tweaked public key, encoded in bech32 format.
const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')

/* 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)
```
### Basic Pay-to-TapScript

Full example: [tapscript.test.ts](test/example/taproot/tapscript.test.ts)

```ts
const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
const seckey = new SecretKey(secret, { type: 'taproot' })
const pubkey = seckey.pub

// Specify a basic script to use for testing.
const script = [ pubkey, 'OP_CHECKSIG' ]
const sbytes = Script.encode(script)

// For tapscript spends, we need to convert this script into a 'tapleaf'.
const tapleaf = Tap.tree.getLeaf(sbytes)

// Optional: There is a convenience method that converts scripts directly.
const _tapleaf = Tap.encodeScript(script)

// Generate a tapkey that includes our leaf script. Also, create a merlke proof 
// (cblock) that targets our leaf and proves its inclusion in the tapkey.
const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { target: tapleaf })

// A taproot address is simply the tweaked public key, encoded in bech32 format.
const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')

/* 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: '181508e3be1107372f1ffcbd52de87b2c3e7c8b2495f1bc25f8cf42c0ae167c2',
    // 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')
  }]
})

// 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: tapleaf })

// 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.hex, 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 })
```

### Create / Spend from a Tree of Scripts

Full example: [taptree.test.ts](test/example/taproot/taptree.test.ts)

```ts
// Create a keypair to use for testing.
const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
const seckey = new SecretKey(secret, { type: 'taproot' })
const pubkey = seckey.pub

// Specify an array of scripts to use for testing.
const scripts = [
  [ 1, 7, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
  [ 2, 6, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
  [ 3, 5, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
  [ 4, 4, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
  [ 5, 3, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
  [ 6, 2, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
  [ 7, 1, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ]
]

// Convert our array of scripts into tapleaves.
const tree = scripts.map(s => Tap.encodeScript(s))

// Pick one of our scripts as a target for spending.
const index  = Math.floor(Math.random() * 10) % 7
const script = scripts[index]
const target = Tap.encodeScript(script)

// 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, 'regtest')

/* 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: 'e0b1b0aea95095bf7e113c37562a51cb8c3f50f5145c17952e766f7a84fcc5d7',
    // 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')
  }]
})

// 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.hex, 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 })
```

### Create / Publish an Inscription

Creating an inscription is a three-step process:
 1. We create a script for publishing the inscription, and convert it into a bitcoin address.
 2. Send funds to the bitcoin address.
 3. Create a redeem transaction, which claims the previous funds (and publishes the data).

Full example: [inscribe.test.ts](test/example/taproot/inscribe.test.ts)

```ts
// The 'marker' bytes. Part of the ordinal inscription format.
const marker   = Buff.encode('ord')
/* Specify the media type of the file. Applications use this when rendering 
  * content. See: https://developer.mozilla.org/en-US/docs/Glossary/MIME_type 
  */
const mimetype = Buff.encode('image/png')
// Create a keypair to use for testing.
const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
const seckey = new SecretKey(secret, { type: 'taproot' })
const pubkey = seckey.pub
// Basic format of an 'inscription' script.
const script  = [ pubkey, 'OP_CHECKSIG', 'OP_0', 'OP_IF', marker, '01', mimetype, 'OP_0', imgdata, 'OP_ENDIF' ]
// For tapscript spends, we need to convert this script into a 'tapleaf'.
const tapleaf = Tap.encodeScript(script)
// Generate a tapkey that includes our leaf script. Also, create a merlke proof 
// (cblock) that targets our leaf and proves its inclusion in the tapkey.
const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { target: tapleaf })
// A taproot address is simply the tweaked public key, encoded in bech32 format.
const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')

/* 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: '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')
  }]
})

// 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: tapleaf })

// 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, throws: true })

// You can publish your transaction data using 'sendrawtransaction' in Bitcoin Core, or you 
// can use an external API (such as https://mempool.space/docs/api/rest#post-transaction).
```

More examples to come!

## Development / Testing

This library uses yarn for package management, tape for writing tests, and rollup for bundling cross-platform compatible code. Here are a few scripts that are useful for development.

```bash
## Compiles types and builds release candidates in /dist folder.
yarn build
## Run any typescript file using real-time compilation.
yarn start contrib/example.ts
## Runs all tests listed in test/tape.ts 
yarn test
## Full macro script for generating a new release candidate.
yarn release
```

## Bugs / Issues

If you run into any bugs or have any questions, please submit an issue ticket.

## Contribution

Feel free to fork and make contributions. Suggestions are welcome!

## Future Roadmap

 - Add signature and validation for ecdsa (segwit and earlier).
 - Refactor and stress-test tree compilation with many (many) leaves.
 - Allow arbitrary ordering of tree elements.
 - Write more unit and vector tests (cover all the things).

## Dependencies

This library contains minimal dependencies.  

**Buff-Utils**  
The swiss-army-knife of byte manipulation.  
https://github.com/cmdruid/buff-utils

**Crypto-Utils**  
User-friendly cryptography tools.  
https://github.com/cmdruid/crypto-utils

## Resources  

**BIP340 Wiki Page**  
This BIP covers schnorr signatures and verification.  
https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki

**BIP341 Wiki Page**  
This BIP covers the construction of trees, signature hashes, and proofs.  
https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki

**BIP342 Wiki Page**  
This BIP covers changes to opcodes and signature verification.  
https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki

**Tapscript example using Tap**  
This is a guide on how to use a command-line tool called btcdeb and Tap.  
This tool will help you create a taproot transaction from scratch, which  
is great for learning (and to debug any issues with this library :-)).  
https://github.com/bitcoin-core/btcdeb/blob/master/doc/tapscript-example-with-tap.md

## License

Use this library however you want!

## Contact

You can find me on twitter at `@btctechsupport` or on nostr at `npub1gg5uy8cpqx4u8wj9yvlpwm5ht757vudmrzn8y27lwunt5f2ytlusklulq3`

================================================
FILE: package.json
================================================
{
  "name": "@cmdcode/tapscript",
  "version": "1.5.3",
  "description": "A basic library for working with Tapscript, signatures and Bitcoin transactions.",
  "type": "module",
  "main": "./dist/main.cjs",
  "unpkg": "./dist/bundle.min.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/module.mjs"
      },
      "require": {
        "types": "./dist/index.d.ts",
        "default": "./dist/main.cjs"
      }
    }
  },
  "scripts": {
    "build": "rollup -c rollup.config.ts --configPlugin typescript",
    "clean": "rm -rf dist/* coverage .nyc_output",
    "types": "tsc",
    "load": "NODE_OPTIONS='--no-warnings' tsx --tsconfig ./test/tsconfig.json",
    "release": "yarn test && yarn clean && yarn types && yarn build",
    "scratch": "yarn load test/scratch.ts",
    "test": "tsx --tsconfig ./test/tsconfig.json ./test/tape.ts | tap-spec"
  },
  "keywords": [
    "bitcoin",
    "transaction",
    "psbt",
    "serialize",
    "encode",
    "decode",
    "validate"
  ],
  "author": "Christopher Scott",
  "license": "CC-BY-1.0",
  "repository": {
    "type": "git",
    "url": "https://github.com/cmdruid/tapscript.git"
  },
  "publishConfig": {
    "registry": "https://registry.npmjs.org",
    "access": "public"
  },
  "files": [
    "README.md",
    "LICENSE",
    "dist"
  ],
  "devDependencies": {
    "@cmdcode/buff-utils": "^1.9.7",
    "@cmdcode/core-cmd": "^1.5.37",
    "@cmdcode/crypto-utils": "^2.0.2",
    "@cmdcode/keylink": "^1.4.3",
    "@noble/curves": "^0.9.1",
    "@rollup/plugin-commonjs": "^23.0.2",
    "@rollup/plugin-node-resolve": "^15.0.1",
    "@rollup/plugin-terser": "^0.4.0",
    "@rollup/plugin-typescript": "^9.0.2",
    "@types/node": "^18.14.0",
    "@types/tape": "^4.13.2",
    "rollup": "^3.2.3",
    "tap-spec": "^5.0.0",
    "tape": "^5.6.1",
    "tslib": "^2.6.2",
    "tsx": "^4.19.2",
    "typescript": "^5.1.6",
    "zod": "^3.20.6"
  }
}


================================================
FILE: rollup.config.ts
================================================
// rollup.config.ts
import typescript  from '@rollup/plugin-typescript'
import nodeResolve from '@rollup/plugin-node-resolve'
import commonjs    from '@rollup/plugin-commonjs'
import terser      from '@rollup/plugin-terser'

const libraryName = 'tapscript'

const treeshake = {
	moduleSideEffects: false,
	propertyReadSideEffects: false,
	tryCatchDeoptimization: false
}

const onwarn = warning => {
	// eslint-disable-next-line no-console
	console.error(
		'Building Rollup produced warnings that need to be resolved. ' +
			'Please keep in mind that the browser build may never have external dependencies!'
	);
	// eslint-disable-next-line unicorn/error-message
	throw Object.assign(new Error(), warning);
}

const tsConfig = { 
  compilerOptions: {
    declaration: false,
    declarationDir: null,
    declarationMap: false
  }
}

const nodeConfig = {
  input: 'src/index.ts',
  onwarn,
  output: [
    {
      file      : 'dist/main.cjs',
      format    : 'cjs',
      sourcemap : true,
    },
    {
      file      : 'dist/module.mjs',
      format    : 'es',
      sourcemap : true,
      minifyInternalExports: false
    },
  ],
  plugins: [ typescript(tsConfig), nodeResolve(), commonjs() ],
  strictDeprecations: true,
  treeshake
}

const browserConfig = {
  input: 'src/index.ts',
  onwarn,
  output: [
    {
      file      : 'dist/bundle.min.js',
      format    : 'iife',
      name      : libraryName,
      plugins   : [terser()],
      sourcemap : true
    },
  ],
  plugins: [ 
    typescript(tsConfig), 
    nodeResolve({ browser: true }), 
    commonjs() 
  ],
  strictDeprecations: true,
  treeshake
}

export default [ nodeConfig, browserConfig ]


================================================
FILE: src/class/Signature/index.ts
================================================
export {}

================================================
FILE: src/class/Transaction/Transaction.ts
================================================
import { Buff }     from '@cmdcode/buff-utils'
import { hash256 }  from '@cmdcode/crypto-utils'
import TxInput      from './TxInput.js'
import TxOutput     from './TxOutput.js'
import TxLocktime   from './TxLocktime.js'
import { Tx }       from '../../lib/tx/index.js'
import { Schema }   from '../../schema/check.js'

import {
  TxData,
  TxTemplate
} from '../../schema/types.js'

export default class Transaction {
  readonly _data : TxData

  constructor (
    txdata : string | Uint8Array | TxTemplate
  ) {
    if (typeof txdata === 'string') {
      txdata = Buff.hex(txdata)
    }

    if (txdata instanceof Uint8Array) {
      txdata = Tx.decode(txdata)
    }

    const schema = Schema.TxData
    this._data   = schema.parse(Tx.create(txdata))
  }

  get data () : TxData {
    return this._data
  }

  get version () : number {
    return this.data.version
  }

  get vin () : TxInput[] {
    return this.data.vin.map((_e, i) => new TxInput(this.data, i))
  }

  get vout () : TxOutput[] {
    return this.data.vout.map((e) => new TxOutput(e))
  }

  get locktime () : TxLocktime {
    return new TxLocktime(this.data.locktime)
  }

  get base () : Buff {
    return Tx.encode(this.data, true)
  }

  get buff () : Buff {
    return Tx.encode(this.data)
  }

  get raw () : Uint8Array {
    return this.buff.raw
  }

  get hex () : string {
    return this.buff.hex
  }

  get size () : number {
    return this.raw.length
  }

  get bsize () : number {
    return this.base.length
  }

  get weight () : number {
    return this.bsize * 3 + this.size
  }

  get vsize () : number {
    const remainder = (this.weight % 4 > 0) ? 1 : 0
    return Math.floor(this.weight / 4) + remainder
  }

  get hash () : string {
    const hash = hash256(this.buff)
    return hash.reverse().hex
  }

  get txid () : string {
    const hash = hash256(this.base)
    return hash.reverse().hex
  }

  async export () : Promise<object> {
    const { size, weight, vsize, hex } = this
    const txid = this.txid
    const hash = this.hash
    return { txid, hash, ...this.data, size, weight, vsize, hex }
  }
}


================================================
FILE: src/class/Transaction/TxInput.ts
================================================
// import getSigHash from "../../sighash.js"
import TxScript       from './TxScript.js'
import TxSequence     from './TxSequence.js'
import TxOutput       from './TxOutput.js'
import TxWitness      from './TxWitness.js'
import { Signer }     from '../../lib/sig/index.js'
import { HashConfig } from '../../lib/sig/types.js'
import { readScriptPubKey } from '../../lib/tx/parse.js'
import { Bytes, InputData, InputType, TxData } from '../../schema/types.js'

export default class TxInput {
  readonly _tx : TxData
  readonly idx : number

  constructor (txdata : TxData, index : number) {
    this._tx = txdata
    this.idx = index
  }

  get data () : InputData {
    return this._tx.vin[this.idx]
  }

  get txid () : string {
    return this.data.txid
  }

  get vout () : number {
    return this.data.vout
  }

  get prevout () : TxOutput | undefined {
    return (this.data.prevout !== undefined)
      ? new TxOutput(this.data.prevout)
      : undefined
  }

  get scriptSig () : TxScript {
    return new TxScript(this.data.scriptSig)
  }

  get sequence () : TxSequence {
    return new TxSequence(this.data.sequence)
  }

  get witness () : TxWitness {
    return new TxWitness(this.data.witness)
  }

  get type () : InputType {
    if (this.prevout !== undefined) {
      const script = this.prevout.scriptPubKey.raw
      const { type } = readScriptPubKey(script)
      if (type === 'p2sh') {
        const asm = this.scriptSig.asm
        if (asm[0] === 'OP_0') {
          if (asm[1].length === 20) {
            return 'p2w-p2pkh'
          }
          if (asm[1].length === 32) {
            return 'p2w-p2sh'
          }
        }
        return 'p2sh'
      }
      return type
    }
    return 'raw'
  }

  sign (seckey : Bytes, config : HashConfig) {
    if (this.type.startsWith('p2w')) {
      return Signer.segwit.sign(seckey, this._tx, this.idx, config)
    }
    if (this.type.startsWith('p2tr')) {
      return Signer.taproot.sign(seckey, this._tx, this.idx, config)
    }
    if (
      this.type.startsWith('p2pkh') ||
      this.type.startsWith('p2sh')
    ) {
      throw new Error('This library does not support signing legacy transactions.')
    }
    throw new Error('Unable to sign this input type:' + String(this.type))
  }
}


================================================
FILE: src/class/Transaction/TxLocktime.ts
================================================
import { Buff } from '@cmdcode/buff-utils'
import { LockData }  from '../../schema/types.js'

const LOCKTIME_THRESHOLD = 500000000

export default class TxLocktime {
  public value : number

  constructor (value : LockData = 0) {
    this.value = Buff.bytes(value).num
  }

  get isTimelock () : boolean {
    return this.value > LOCKTIME_THRESHOLD
  }

  get timestamp () : number {
    return this.isTimelock
      ? this.value
      : this.value * 600
  }

  set timestamp (value : number) {
    this.value = value
  }

  get blockheight () : number {
    return !this.isTimelock
      ? this.value
      : Math.floor(this.value / 600)
  }

  set blockheight (value : number) {
    this.value = value
  }

  get estDate () : Date {
    return this.isTimelock
      ? new Date(Date.now() + (this.value * 1000))
      : new Date(Date.now() + (this.value * 600 * 1000))
  }

  set estDate (date : Date) {
    this.value = Math.floor((date.getTime() - Date.now()) / 1000)
  }

  toJSON () : number {
    return this.value
  }
}


================================================
FILE: src/class/Transaction/TxOutput.ts
================================================
import TxScript from './TxScript.js'
import { readScriptPubKey } from '../../lib/tx/parse.js'
import { OutputData, OutputType } from '../../schema/types.js'

export default class TxOutput {
  value : bigint
  scriptPubKey : TxScript

  constructor (txout : OutputData) {
    this.value = BigInt(txout.value)
    this.scriptPubKey = new TxScript(txout.scriptPubKey)
  }

  get type () : OutputType {
    const { type } = readScriptPubKey(this.scriptPubKey.raw)
    return type
  }
}


================================================
FILE: src/class/Transaction/TxScript.ts
================================================
import { Buff }         from '@cmdcode/buff-utils'
import { encodeScript } from '../../lib/script/encode.js'
import { decodeScript } from '../../lib/script/decode.js'
import { ScriptData }   from '../../schema/types.js'
import { TapTree }      from '../../lib/tap/index.js'
import { hash160, hash256 } from '@cmdcode/crypto-utils'

type ScriptFormat = 'p2sh' | 'p2w' | 'p2tr'

export default class TxScript {
  readonly _buff : Buff

  constructor(
    script : ScriptData,
  ) {
    this._buff = Buff.raw(encodeScript(script))
  }

  get raw () : Uint8Array {
    return this._buff.raw
  }

  get hex () : string {
    return this._buff.hex
  }

  get asm () : string[] {
    return decodeScript(this._buff)
  }

  getHash (format : ScriptFormat, version ?: number) : string {
    switch (format) {
      case 'p2w':
        return hash256(this._buff).hex
      case 'p2sh':
        return hash160(this._buff).hex
      case 'p2tr':
        return TapTree.getLeaf(this._buff, version)
      default:
        throw new Error('Unrecognized format: ' + format)
    }
  }

  toJSON() : string[] {
    return this.asm ?? []
  }
}


================================================
FILE: src/class/Transaction/TxSequence.ts
================================================
import { SequenceData } from '../../schema/types.js'

const MAX_VAL     = 0xFFFFFFFF
const NO_LOCK     = (1 << 31)
const TIME_MOD    = 512
const LOCK_TYPE   = (1 << 22)
// const LOCK_MASK   = 0x0000FFFF
// const BIT_SHIFT   = 9
// const MAX_BLOCKS  = LOCK_MASK
// const MAX_SECONDS = LOCK_MASK << BIT_SHIFT

export default class TxSequence {
  public value : number

  constructor (value : SequenceData) {
    if (typeof value === 'string') {
      this.value = parseInt(value, 16)
    } else {
      this.value = value
    }
  }

  get isReplaceable () : boolean {
    return this.value < MAX_VAL
  }

  get isLocked () : boolean {
    return !(this.value !== MAX_VAL || (this.value & NO_LOCK) !== 0)
  }

  get isTimelock () : boolean {
    return (this.value & LOCK_TYPE) !== 0
  }

  get timestamp () : number {
    return this.isLocked
      ? this.isTimelock
        ? this.value * TIME_MOD
        : this.value * TIME_MOD * 600
      : 0
  }

  set timestamp (value : number) {
    // Check if valid timestamp range.
    // Check if within modulo 512.
    this.value = Math.ceil(value / TIME_MOD)
  }

  get blockheight () : number {
    return this.isLocked
      ? !this.isTimelock
        ? this.value
        : Math.ceil((this.value * TIME_MOD) / 600)
      : 0
  }

  set blockheight (value : number) {
    // Check if valid blockheight range.
    this.value = value
  }

  get estDate () : Date {
    return this.isTimelock
      ? new Date(Date.now() + (this.value * TIME_MOD * 1000))
      : new Date(Date.now() + (this.value * 600 * 1000))
  }

  set estDate (date : Date) {
    const delta = date.getTime() - Date.now()
    // Validate delta is above 512,000ms
    this.value = (delta > (TIME_MOD * 1000))
      ? Math.ceil(delta / 1000 / TIME_MOD)
      : 1
  }

  toJSON () : number {
    return this.value
  }
}

// function validateSequence(seq : TxSequence) {
//   const { hex, inSeconds, inBlocks, replaceByFee } = seq

//   switch (true) {
//     case (inSeconds && inBlocks):
//       throw new TypeError('Both timelock and blocklock are specified!')
//     case (inSeconds && typeof inSeconds !== 'number'):
//       throw new TypeError('Invalid timelock value: ' + inSeconds)
//     case (inSeconds && (inSeconds < 0 || inSeconds > MAX_SECONDS)):
//       throw new TypeError('Timelock out of range: ' + inSeconds)
//     case (inSeconds && inSeconds % TIME_MOD !== 0):
//       throw new TypeError('Timelock value must be multiple of 512')
//     case (inSeconds && inSeconds > 0):
//       return LOCK_TYPE | (inSeconds >> BIT_SHIFT) // plus bitfield
//     case (inBlocks && typeof inBlocks !== 'number'):
//       throw new TypeError('Invalid blocklock value: ' + inBlocks)
//     case (inBlocks && (inBlocks < 0 || inBlocks > MAX_BLOCKS)):
//       throw new TypeError('Blocklock out of range: ' + inBlocks)
//     case (inBlocks && inBlocks > 0):
//       return inBlocks // plus bitfield
//     case (replaceByFee === true):
//       return MAX_VAL - 1 // or use bitfield
//     default:
//       return MAX_VAL // ignore bitfield
//   }
// }


================================================
FILE: src/class/Transaction/TxWitness.ts
================================================
import { Buff }        from '@cmdcode/buff-utils'
import { readWitness } from '../../lib/tx/parse.js'
import { Script }      from '../../lib/script/index.js'
import { Bytes, InputType, ScriptData, WitnessData } from '../../schema/types.js'

export default class TxWitness {
  readonly format ?: InputType
  readonly _data   : ScriptData[]
  readonly _meta   : WitnessData

  constructor (
    data    : ScriptData[],
    format ?: InputType
  ) {
    this._data  = data
    this._meta  = readWitness(data)
    this.format = format
  }

  get length () : number {
    return this._data.length
  }

  get annex () : string | undefined {
    const annex = this._meta.annex
    return (annex !== null)
      ? Buff.raw(annex).hex
      : undefined
  }

  get cblock () : string | undefined {
    const cblock = this._meta.cblock
    return (cblock !== null)
      ? Buff.raw(cblock).hex
      : undefined
  }

  get script () : ScriptData | undefined {
    const script = this._meta.script
    return (script !== null)
      ? Script.decode(script)
      : undefined
  }

  get params () : Bytes[] {
    return this._meta.params
  }

  toJSON () : ScriptData[] {
    return this._data
  }
}


================================================
FILE: src/class/Transaction/index.ts
================================================
import Transaction from './Transaction.js'
import Input     from './TxInput.js'
import Output    from './TxOutput.js'
import Script    from './TxScript.js'
import Sequence  from './TxSequence.js'
import Witness   from './TxWitness.js'

export {
  Transaction,
  Input,
  Output,
  Script,
  Sequence,
  Witness
}


================================================
FILE: src/index.ts
================================================
export { Address } from './lib/addr/index.js'
export { Script }  from './lib/script/index.js'
export { Signer }  from './lib/sig/index.js'
export { Tap }     from './lib/tap/index.js'
export { Tx }      from './lib/tx/index.js'

export * from './class/Transaction/index.js'
export * from './schema/types.js'


================================================
FILE: src/lib/addr/hash.ts
================================================
import { Buff, Bytes } from '@cmdcode/buff-utils'
import { Script }      from '../script/index.js'
import { checkSize }   from '../utils.js'
import { ScriptData }  from '../../schema/types.js'
import { hash160, sha256 } from '@cmdcode/crypto-utils'

export function hash160pkh (pubkey : Bytes) : Buff {
  const bytes = Buff.bytes(pubkey)
  checkSize(bytes, 33)
  return hash160(bytes)
}

export function hash160sh (script : ScriptData) : Buff {
  const bytes = Script.fmt.toBytes(script, false)
  return hash160(bytes)
}

export function sha256sh (script : ScriptData) : Buff {
  const bytes = Script.fmt.toBytes(script, false)
  return sha256(bytes)
}


================================================
FILE: src/lib/addr/index.ts
================================================
// https://en.bitcoin.it/wiki/Invoice_address

import { P2PKH }  from './p2pkh.js'
import { P2SH }   from './p2sh.js'
import { P2WPKH } from './p2w-pkh.js'
import { P2WSH }  from './p2w-sh.js'
import { P2TR }   from './p2tr.js'

import {
  decodeAddress,
  fromScriptPubKey,
  toScriptPubKey
} from './utils.js'

export const Address = {
  p2pkh  : P2PKH,
  p2sh   : P2SH,
  p2wpkh : P2WPKH,
  p2wsh  : P2WSH,
  p2tr   : P2TR,
  decode : decodeAddress,
  fromScriptPubKey,
  toScriptPubKey
}


================================================
FILE: src/lib/addr/p2pkh.ts
================================================
import { Buff }            from '@cmdcode/buff-utils'
import { Bytes, Networks } from '../../schema/types.js'
import { checkSize }       from '../utils.js'
import { hash160pkh }      from './hash.js'

export function check (
  address : string,
  network : Networks = 'main'
) : boolean {
  const prefixes = (network === 'main') ? [ '1' ] : [ 'm', 'n' ]
  for (const prefix of prefixes) {
    if (address.startsWith(prefix)) {
      return true
    }
  }
  return false
}

export function encode (
  input   : Bytes,
  network : Networks = 'main'
) : string {
  const bytes  = Buff.bytes(input)
  const prefix = (network === 'main') ? Buff.num(0x00) : Buff.num(0x6F)
  checkSize(input, 20)
  return bytes.prepend(prefix).tob58chk()
}

export function decode (
  address : string,
  network : Networks = 'main'
) : Buff {
  if (!check(address, network)) {
    throw new TypeError('Invalid p2pkh address!')
  }
  return Buff.b58chk(address).slice(1)
}

export function scriptPubKey (input : Bytes) : string[] {
  const bytes = Buff.bytes(input)
  checkSize(bytes, 20)
  return [ 'OP_DUP', 'OP_HASH160', bytes.hex, 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]
}

export function fromPubKey (
  pubkey   : Bytes,
  network ?: Networks
) : string {
  const pkh = hash160pkh(pubkey)
  return encode(pkh, network)
}

export const P2PKH = { check, encode, decode, hash: hash160pkh, scriptPubKey, fromPubKey }


================================================
FILE: src/lib/addr/p2sh.ts
================================================
import { Buff }      from '@cmdcode/buff-utils'
import { checkSize } from '../utils.js'
import { hash160sh } from './hash.js'
import { Bytes, Networks, ScriptData } from '../../schema/types.js'

export function check (
  address : string,
  network : Networks = 'main'
) : boolean {
  const prefixes = (network === 'main') ? [ '3' ] : [ '2' ]

  for (const prefix of prefixes) {
    if (address.startsWith(prefix)) {
      return true
    }
  }
  return false
}

export function encode (
  input   : Bytes,
  network : Networks = 'main'
) : string {
  const prefix = (network === 'main') ? Buff.num(0x05) : Buff.num(0xC4)
  const bytes  = Buff.bytes(input)
  checkSize(bytes, 20)
  return bytes.prepend(prefix).tob58chk()
}

export function decode (
  address : string,
  network : Networks = 'main'
) : Buff {
  if (!check(address, network)) {
    throw new TypeError(`Invalid p2sh address for network ${network}:` + address)
  }
  return Buff.b58chk(address).slice(1)
}

export function scriptPubKey (input : Bytes) : string[] {
  const bytes = Buff.bytes(input)
  return [ 'OP_HASH160', bytes.hex, 'OP_EQUAL' ]
}

export function fromScript (
  script   : ScriptData,
  network ?: Networks
) : string {
  const scriptHash = hash160sh(script)
  return encode(scriptHash, network)
}

export const P2SH = { check, encode, decode, hash: hash160sh, scriptPubKey, fromScript }


================================================
FILE: src/lib/addr/p2tr.ts
================================================
import { Buff } from '@cmdcode/buff-utils'
import { Bytes, Networks } from '../../schema/types.js'
import { xOnlyPub } from '../tap/utils.js'
import { checkSize } from '../utils.js'
import { BECH32_PREFIXES } from './schema.js'

const VALID_PREFIXES = [ 'bc1p', 'tb1p', 'bcrt1p' ]

export function check (address : string) : boolean {
  for (const prefix of VALID_PREFIXES) {
    if (address.startsWith(prefix)) {
      return true
    }
  }
  return false
}

export function encode (
  input   : Bytes,
  network : Networks = 'main'
) : string {
  const prefix = BECH32_PREFIXES[network]
  const bytes  = Buff.bytes(input)
  checkSize(bytes, 32)
  return bytes.toBech32(prefix, 1)
}

export function decode (address : string) : Buff {
  if (!check(address)) {
    throw new TypeError('Invalid taproot address!')
  }
  return Buff.bech32(address)
}

export function scriptPubKey (input : Bytes) : string[] {
  const bytes = Buff.bytes(input)
  checkSize(bytes, 32)
  return [ 'OP_1', bytes.hex ]
}

export function fromPubKey (
  pubkey   : Bytes,
  network ?: Networks
) : string {
  const bytes = xOnlyPub(pubkey)
  return encode(bytes, network)
}

export const P2TR = { check, encode, decode, scriptPubKey, fromPubKey }


================================================
FILE: src/lib/addr/p2w-pkh.ts
================================================
import { Buff }            from '@cmdcode/buff-utils'
import { Bytes, Networks } from '../../schema/types.js'
import { checkSize }       from '../utils.js'
import { BECH32_PREFIXES } from './schema.js'
import { hash160pkh }      from './hash.js'

const VALID_PREFIXES = [ 'bc1q', 'tb1q', 'bcrt1q' ]

export function check (address : string) : boolean {
  for (const prefix of VALID_PREFIXES) {
    if (address.startsWith(prefix)) {
      return true
    }
  }
  return false
}

export function encode (
  input   : Bytes,
  network : Networks = 'main'
) : string {
  const prefix = BECH32_PREFIXES[network]
  const bytes = Buff.bytes(input)
  checkSize(bytes, 20)
  return bytes.toBech32(prefix, 0)
}

export function decode (address : string) : Buff {
  if (!check(address)) {
    throw new TypeError('Invalid segwit address!')
  }
  return Buff.bech32(address)
}

export function scriptPubKey (input : Bytes) : string[] {
  const bytes = Buff.bytes(input)
  checkSize(bytes, 20)
  return [ 'OP_0', bytes.hex ]
}

export function fromPubKey (
  pubkey   : Bytes,
  network ?: Networks
) : string {
  const pkh = hash160pkh(pubkey)
  return encode(pkh, network)
}

export const P2WPKH = { check, encode, decode, hash: hash160pkh, scriptPubKey, fromPubKey }


================================================
FILE: src/lib/addr/p2w-sh.ts
================================================
import { Buff }            from '@cmdcode/buff-utils'
import { checkSize }       from '../utils.js'
import { BECH32_PREFIXES } from './schema.js'
import { sha256sh }        from './hash.js'
import { Bytes, Networks, ScriptData } from '../../schema/types.js'

const VALID_PREFIXES = [ 'bc1q', 'tb1q', 'bcrt1q' ]

export function check (address : string) : boolean {
  for (const prefix of VALID_PREFIXES) {
    if (address.startsWith(prefix)) {
      return true
    }
  }
  return false
}

export function encode (
  input   : Bytes,
  network : Networks = 'main'
) : string {
  const prefix = BECH32_PREFIXES[network]
  const bytes = Buff.bytes(input)
  checkSize(bytes, 32)
  return bytes.toBech32(prefix, 0)
}

export function decode (address : string) : Buff {
  if (!check(address)) {
    throw new TypeError('Invalid segwit address!')
  }
  return Buff.bech32(address)
}

export function scriptPubKey (input : Bytes) : string[] {
  const bytes = Buff.bytes(input)
  checkSize(bytes, 32)
  return [ 'OP_0', bytes.hex ]
}

export function fromScript (
  script   : ScriptData,
  network ?: Networks
) : string {
  const sh = sha256sh(script)
  return encode(sh, network)
}

export const P2WSH = { check, encode, decode, hash: sha256sh, scriptPubKey, fromScript }


================================================
FILE: src/lib/addr/schema.ts
================================================
/* eslint-disable quote-props */

import { Buff } from '@cmdcode/buff-utils'
import { Bytes, Networks, OutputType, ScriptData } from '../../schema/types.js'

export type AddressType = [
  prefix  : string,
  type    : OutputType,
  network : Networks,
  size    : number,
  format  : 'base58' | 'bech32' | 'bech32m'
]

export interface AddressData {
  data    : Buff
  network : Networks
  prefix  : string
  script  : string[]
  type    : OutputType
}

export interface AddressTool {
  check  : (address : string, network ?: Networks) => boolean
  encode : (input   : Bytes, network ?: Networks) => string
  decode : (address : string, network ?: Networks) => Buff
  scriptPubKey : (keyhash : Bytes) => string[]
}

export interface AddrKeyTool extends AddressTool {
  fromPubKey : (pubkey : Bytes, network ?: Networks) => string
}

export interface AddrScriptTool extends AddressTool {
  fromScript : (script : ScriptData, network ?: Networks) => string
}

export const BECH32_PREFIXES : Record<Networks, string> = {
  main    : 'bc',
  testnet : 'tb',
  signet  : 'tb',
  regtest : 'bcrt'
}


================================================
FILE: src/lib/addr/utils.ts
================================================

import { Buff }      from '@cmdcode/buff-utils'
import { Script }    from '../script/index.js'
import { P2PKH }     from './p2pkh.js'
import { P2SH }      from './p2sh.js'
import { P2WPKH }    from './p2w-pkh.js'
import { P2WSH }     from './p2w-sh.js'
import { P2TR }      from './p2tr.js'
import { Tx }        from '../tx/index.js'

import { Networks, OutputType, ScriptData } from '../../schema/types.js'

import {
  AddressData,
  AddressTool,
  AddressType,
  AddrScriptTool
} from './schema.js'

export const ADDRESS_TYPES : AddressType[] = [
  [ '1',      'p2pkh',   'main',    20, 'base58'  ],
  [ '3',      'p2sh',    'main',    20, 'base58'  ],
  [ 'm',      'p2pkh',   'testnet', 20, 'base58'  ],
  [ 'n',      'p2pkh',   'testnet', 20, 'base58'  ],
  [ '2',      'p2sh',    'testnet', 20, 'base58'  ],
  [ 'bc1q',   'p2w-pkh', 'main',    20, 'bech32'  ],
  [ 'tb1q',   'p2w-pkh', 'testnet', 20, 'bech32'  ],
  [ 'bcrt1q', 'p2w-pkh', 'regtest', 20, 'bech32'  ],
  [ 'bc1q',   'p2w-sh',  'main',    32, 'bech32'  ],
  [ 'tb1q',   'p2w-sh',  'testnet', 32, 'bech32'  ],
  [ 'bcrt1q', 'p2w-sh',  'regtest', 32, 'bech32'  ],
  [ 'bc1p',   'p2tr',    'main',    32, 'bech32m' ],
  [ 'tb1p',   'p2tr',    'testnet', 32, 'bech32m' ],
  [ 'bcrt1p', 'p2tr',    'regtest', 32, 'bech32m' ]
]

function decodeFormat (address : string, format : string) : Buff {
  switch (format) {
    case 'base58'  : return Buff.b58chk(address).slice(1)
    case 'bech32'  : return Buff.bech32(address)
    case 'bech32m' : return Buff.bech32(address)
    default: throw new Error('Invalid address format: ' + format)
  }
}

function getData (address : string) : AddressType {
  for (const row of ADDRESS_TYPES) {
    const [ prefix, _type, _network, size, format ] = row
    if (
      address.startsWith(prefix)
    ) {
      const bytes = decodeFormat(address, format)
      if (bytes.length === size) return row
    }
  }
  throw new Error('Invalid address: ' + address)
}

function getTool (type : OutputType) : AddressTool | AddrScriptTool {
  switch (type) {
    case 'p2pkh'   : return P2PKH
    case 'p2sh'    : return P2SH
    case 'p2w-pkh' : return P2WPKH
    case 'p2w-sh'  : return P2WSH
    case 'p2tr'    : return P2TR
    default        : throw new Error('Invalid address type: ' + type)
  }
}

export function decodeAddress (
  address : string
) : AddressData {
  const [ prefix, type, network ] = getData(address)
  const tool   = getTool(type)
  const data   = tool.decode(address, network)
  const script = tool.scriptPubKey(data)
  return { prefix, type, network, data, script }
}

export function fromScriptPubKey (
  script   : ScriptData,
  network ?: Networks
) : string {
  const { type, data } = Tx.util.readScriptPubKey(script)
  const tool = getTool(type)
  return tool.encode(data, network)
}

export function toScriptPubKey (address : string) : ScriptData {
  const { script } = decodeAddress(address)
  return Script.fmt.toAsm(script, false)
}


================================================
FILE: src/lib/check.ts
================================================
import { Bytes } from '../schema/types'

export function isHex<T> (value : T) : value is Extract<T, string> {
  return (
    typeof value === 'string' &&
    value.length % 2 === 0    &&
    /[0-9a-fA-F]/.test(value)
  )
}

export function isBytes<T> (value : T) : value is Extract<T, Bytes> {
  return (isHex(value) || value instanceof Uint8Array)
}

export function isValidAnnex (annex : any) : boolean {
  return (
    typeof annex === 'string' &&
    annex.startsWith('50')
  )
}


================================================
FILE: src/lib/script/decode.ts
================================================
import { Buff, Stream } from '@cmdcode/buff-utils'

import {
  getOpLabel,
  getWordType,
  isValidWord
} from './words.js'

export function decodeScript (
  script : string | Uint8Array,
  varint = false
) : string[] {
  let buff = Buff.bytes(script)
  if (varint) {
    const stream = buff.stream
    const len = stream.readSize('le')
    buff = buff.slice(1)
    if (buff.length !== len) {
      throw new Error(`Varint does not match stream size: ${len} !== ${buff.length}`)
    }
  }
  return decodeWords(buff)
}

export function decodeWords (
  words : Uint8Array
) : string[] {
  const stream = new Stream(words)

  const stack : string[] = []
  const stackSize = stream.size

  let word     : number
  let wordType : string
  let wordSize : number

  let count = 0

  while (count < stackSize) {
    word = stream.read(1).num
    wordType = getWordType(word)
    count++
    switch (wordType) {
      case 'varint':
        stack.push(stream.read(word).hex)
        count += word
        break
      case 'pushdata1':
        wordSize = stream.read(1).reverse().num
        stack.push(stream.read(wordSize).hex)
        count += wordSize + 1
        break
      case 'pushdata2':
        wordSize = stream.read(2).reverse().num
        stack.push(stream.read(wordSize).hex)
        count += wordSize + 2
        break
      case 'pushdata4':
        wordSize = stream.read(4).reverse().num
        stack.push(stream.read(wordSize).hex)
        count += wordSize + 4
        break
      case 'opcode':
        if (!isValidWord(word)) {
          throw new Error(`Invalid OPCODE: ${word}`)
        }
        stack.push(getOpLabel(word))
        break
      default:
        throw new Error(`Word type undefined: ${word}`)
    }
  }
  return stack
}


================================================
FILE: src/lib/script/encode.ts
================================================
import { Buff, Stream } from '@cmdcode/buff-utils'
import { getOpCode }    from './words.js'
import { isHex }        from '../check.js'
import { ScriptData, Word } from '../../schema/types.js'

const MAX_WORD_SIZE = 0x208

export function encodeScript (
  script : ScriptData = [],
  varint = true
) : Buff {
  let buff = Buff.num(0)

  if (Array.isArray(script)) {
    buff = Buff.raw(encodeWords(script))
  }

  if (isHex(script)) {
    buff = Buff.hex(script)
  }

  if (script instanceof Uint8Array) {
    buff = Buff.raw(script)
  }

  if (varint) {
    buff = buff.prefixSize('le')
  }

  return buff
}

export function encodeWords (
  wordArray : Word[]
) : Uint8Array {
  const words = []
  for (const word of wordArray) {
    words.push(encodeWord(word))
  }
  return (words.length > 0)
    ? Buff.join(words)
    : new Uint8Array()
}

export function encodeWord (
  word : Word
) : Uint8Array {
  /** Check if the word is a valid opcode,
   *  and return its integer value.
   */
  // Initialize buff variable.
  let buff = new Uint8Array()

  if (typeof (word) === 'string') {
    if (word.startsWith('OP_')) {
      // If word is an opcode, return a
      // number value without size prefix.
      return Buff.num(getOpCode(word), 1)
    } else if (isHex(word)) {
      // If word is valid hex, encode as hex.
      buff = Buff.hex(word)
    } else {
      // Else, encode word as UTF8 string.
      buff = Buff.str(word)
    }
  } else {
    // If not a string, encode as bytes.
    buff = Buff.bytes(word)
  }

  // If the buffer contains a single value:
  if (buff.length === 1) {
    // If value is between 1-16:
    if (buff[0] !== 0 && buff[0] <= 16) {
      // Convert values 1-16 into opcode values.
      buff[0] += 0x50
      // Return buffered code without varint.
      return buff
    // If the value is between 129-255:
    } else if (buff[0] > 128 && buff[0] <= 255) {
      // Value must be padded with a zero byte.
      buff = new Uint8Array([ buff[0], 0 ])
    }
    return Buff.join([ encodeSize(buff.length), buff ])
  // If the buffer is greater than the max size:
  } else if (buff.length > MAX_WORD_SIZE) {
    // Split the buffer into chunks.
    const words = splitWord(buff)
    // Run data chunks through the encoder.
    return encodeWords(words)
  // Else:
  } else {
    // Return the current buffer with a prefixed varint.
    return Buff.join([ encodeSize(buff.length), buff ])
  }
}

function encodeSize (size : number) : Uint8Array {
  const OP_DATAPUSH1 = Buff.num(0x4c, 1)
  const OP_DATAPUSH2 = Buff.num(0x4d, 1)

  switch (true) {
    case (size <= 0x4b):
      return Buff.num(size)
    case (size > 0x4b && size < 0x100):
      return Buff.join([ OP_DATAPUSH1, Buff.num(size, 1, 'le') ])
    case (size >= 0x100 && size <= MAX_WORD_SIZE):
      return Buff.join([ OP_DATAPUSH2, Buff.num(size, 2, 'le') ])
    default:
      throw new Error('Invalid word size:' + size.toString())
  }
}

function splitWord (word : Uint8Array) : Word[] {
  const words = []
  const buff  = new Stream(word)
  while (buff.size > MAX_WORD_SIZE) {
    // Push a word chunk to the array.
    words.push(buff.read(MAX_WORD_SIZE))
  }
  // Push the remainder to the array.
  words.push(buff.read(buff.size))
  return words
}


================================================
FILE: src/lib/script/format.ts
================================================
import { Buff }         from '@cmdcode/buff-utils'
import { isHex }        from '../check.js'
import { decodeScript } from './decode.js'
import { encodeScript } from './encode.js'
import { ScriptData }   from '../../schema/types.js'

function toAsm (
  script ?: ScriptData,
  varint ?: boolean
) : string[] {
  if (Array.isArray(script)) {
    script = encodeScript(script, varint)
  }
  if (
    script instanceof Uint8Array ||
    isHex(script)
  ) {
    return decodeScript(script, varint)
  }
  throw new Error('Invalid format: ' + String(typeof script))
}

function toBytes (
  script ?: ScriptData,
  varint ?: boolean
) : Buff {
  if (
    script instanceof Uint8Array ||
    isHex(script)
  ) { script = decodeScript(script, varint) }
  if (Array.isArray(script)) {
    return encodeScript(script, varint)
  }
  throw new Error('Invalid format: ' + String(typeof script))
}

function toParam (
  script : ScriptData
) : Buff {
  if (!Array.isArray(script)) {
    return Buff.bytes(script)
  }
  throw new Error('Invalid format: ' + String(typeof script))
}

export const FmtScript = {
  toAsm,
  toBytes,
  toParam
}


================================================
FILE: src/lib/script/index.ts
================================================
import { encodeScript } from './encode.js'
import { decodeScript } from './decode.js'
import { FmtScript }    from './format.js'

export const Script = {
  encode : encodeScript,
  decode : decodeScript,
  fmt    : FmtScript
}


================================================
FILE: src/lib/script/words.ts
================================================
export const OPCODE_MAP = {
  OP_0                   : 0,
  OP_PUSHDATA1           : 76,
  OP_PUSHDATA2           : 77,
  OP_PUSHDATA4           : 78,
  OP_1NEGATE             : 79,
  OP_SUCCESS80           : 80,
  OP_1                   : 81,
  OP_2                   : 82,
  OP_3                   : 83,
  OP_4                   : 84,
  OP_5                   : 85,
  OP_6                   : 86,
  OP_7                   : 87,
  OP_8                   : 88,
  OP_9                   : 89,
  OP_10                  : 90,
  OP_11                  : 91,
  OP_12                  : 92,
  OP_13                  : 93,
  OP_14                  : 94,
  OP_15                  : 95,
  OP_16                  : 96,
  OP_NOP                 : 97,
  OP_SUCCESS98           : 98,
  OP_IF                  : 99,
  OP_NOTIF               : 100,
  OP_ELSE                : 103,
  OP_ENDIF               : 104,
  OP_VERIFY              : 105,
  OP_RETURN              : 106,
  OP_TOALTSTACK          : 107,
  OP_FROMALTSTACK        : 108,
  OP_2DROP               : 109,
  OP_2DUP                : 110,
  OP_3DUP                : 111,
  OP_2OVER               : 112,
  OP_2ROT                : 113,
  OP_2SWAP               : 114,
  OP_IFDUP               : 115,
  OP_DEPTH               : 116,
  OP_DROP                : 117,
  OP_DUP                 : 118,
  OP_NIP                 : 119,
  OP_OVER                : 120,
  OP_PICK                : 121,
  OP_ROLL                : 122,
  OP_ROT                 : 123,
  OP_SWAP                : 124,
  OP_TUCK                : 125,
  OP_SUCCESS126          : 126,
  OP_SUCCESS127          : 127,
  OP_SUCCESS128          : 128,
  OP_SUCCESS129          : 129,
  OP_SIZE                : 130,
  OP_SUCCESS131          : 131,
  OP_SUCCESS132          : 132,
  OP_SUCCESS133          : 133,
  OP_SUCCESS134          : 134,
  OP_EQUAL               : 135,
  OP_EQUALVERIFY         : 136,
  OP_SUCCESS137          : 137,
  OP_SUCCESS138          : 138,
  OP_1ADD                : 139,
  OP_1SUB                : 140,
  OP_SUCCESS141          : 141,
  OP_SUCCESS142          : 142,
  OP_NEGATE              : 143,
  OP_ABS                 : 144,
  OP_NOT                 : 145,
  OP_0NOTEQUAL           : 146,
  OP_ADD                 : 147,
  OP_SUB                 : 148,
  OP_SUCCESS149          : 149,
  OP_SUCCESS150          : 150,
  OP_SUCCESS151          : 151,
  OP_SUCCESS152          : 152,
  OP_SUCCESS153          : 153,
  OP_BOOLAND             : 154,
  OP_BOOLOR              : 155,
  OP_NUMEQUAL            : 156,
  OP_NUMEQUALVERIFY      : 157,
  OP_NUMNOTEQUAL         : 158,
  OP_LESSTHAN            : 159,
  OP_GREATERTHAN         : 160,
  OP_LESSTHANOREQUAL     : 161,
  OP_GREATERTHANOREQUAL  : 162,
  OP_MIN                 : 163,
  OP_MAX                 : 164,
  OP_WITHIN              : 165,
  OP_RIPEMD160           : 166,
  OP_SHA1                : 167,
  OP_SHA256              : 168,
  OP_HASH160             : 169,
  OP_HASH256             : 170,
  OP_CODESEPARATOR       : 171,
  OP_CHECKSIG            : 172,
  OP_CHECKSIGVERIFY      : 173,
  OP_CHECKMULTISIG       : 174,
  OP_CHECKMULTISIGVERIFY : 175,
  OP_NOP1                : 176,
  OP_CHECKLOCKTIMEVERIFY : 177,
  OP_CHECKSEQUENCEVERIFY : 178,
  OP_NOP4                : 179,
  OP_NOP5                : 180,
  OP_NOP6                : 181,
  OP_NOP7                : 182,
  OP_NOP8                : 183,
  OP_NOP9                : 184,
  OP_NOP10               : 185,
  OP_CHECKSIGADD         : 186,
  OP_SUCCESS187          : 187,
  OP_SUCCESS188          : 188,
  OP_SUCCESS189          : 189,
  OP_SUCCESS190          : 190,
  OP_SUCCESS191          : 191,
  OP_SUCCESS192          : 192,
  OP_SUCCESS193          : 193,
  OP_SUCCESS194          : 194,
  OP_SUCCESS195          : 195,
  OP_SUCCESS196          : 196,
  OP_SUCCESS197          : 197,
  OP_SUCCESS198          : 198,
  OP_SUCCESS199          : 199,
  OP_SUCCESS200          : 200,
  OP_SUCCESS201          : 201,
  OP_SUCCESS202          : 202,
  OP_SUCCESS203          : 203,
  OP_SUCCESS204          : 204,
  OP_SUCCESS205          : 205,
  OP_SUCCESS206          : 206,
  OP_SUCCESS207          : 207,
  OP_SUCCESS208          : 208,
  OP_SUCCESS209          : 209,
  OP_SUCCESS210          : 210,
  OP_SUCCESS211          : 211,
  OP_SUCCESS212          : 212,
  OP_SUCCESS213          : 213,
  OP_SUCCESS214          : 214,
  OP_SUCCESS215          : 215,
  OP_SUCCESS216          : 216,
  OP_SUCCESS217          : 217,
  OP_SUCCESS218          : 218,
  OP_SUCCESS219          : 219,
  OP_SUCCESS220          : 220,
  OP_SUCCESS221          : 221,
  OP_SUCCESS222          : 222,
  OP_SUCCESS223          : 223,
  OP_SUCCESS224          : 224,
  OP_SUCCESS225          : 225,
  OP_SUCCESS226          : 226,
  OP_SUCCESS227          : 227,
  OP_SUCCESS228          : 228,
  OP_SUCCESS229          : 229,
  OP_SUCCESS230          : 230,
  OP_SUCCESS231          : 231,
  OP_SUCCESS232          : 232,
  OP_SUCCESS233          : 233,
  OP_SUCCESS234          : 234,
  OP_SUCCESS235          : 235,
  OP_SUCCESS236          : 236,
  OP_SUCCESS237          : 237,
  OP_SUCCESS238          : 238,
  OP_SUCCESS239          : 239,
  OP_SUCCESS240          : 240,
  OP_SUCCESS241          : 241,
  OP_SUCCESS242          : 242,
  OP_SUCCESS243          : 243,
  OP_SUCCESS244          : 244,
  OP_SUCCESS245          : 245,
  OP_SUCCESS246          : 246,
  OP_SUCCESS247          : 247,
  OP_SUCCESS248          : 248,
  OP_SUCCESS249          : 249,
  OP_SUCCESS250          : 250,
  OP_SUCCESS251          : 251,
  OP_SUCCESS252          : 252,
  OP_SUCCESS253          : 253,
  OP_SUCCESS254          : 254
}

export function getOpLabel (num : number) : string {
  if (num > 186 && num < 255) {
    return 'OP_SUCCESS' + String(num)
  }
  for (const [ k, v ] of Object.entries(OPCODE_MAP)) {
    if (v === num) return k
  }
  throw new Error('OPCODE not found:' + String(num))
}

export function getOpCode (string : string) : number {
  for (const [ k, v ] of Object.entries(OPCODE_MAP)) {
    if (k === string) return Number(v)
  }
  throw new Error('OPCODE not found:' + string)
}

export function getWordType (word : number) : string {
  switch (true) {
    case (word === 0):
      return 'opcode'
    case (word >= 1 && word <= 75):
      return 'varint'
    case (word === 76):
      return 'pushdata1'
    case (word === 77):
      return 'pushdata2'
    case (word === 78):
      return 'pushdata4'
    case (word <= 254):
      return 'opcode'
    default:
      throw new Error(`Invalid word range: ${word}`)
  }
}

export function isValidWord (word : number) : boolean {
  /** Check if the provided value
   * is a valid script opcode.
   * */
  const MIN_RANGE = 75
  const MAX_RANGE = 254

  const DISABLED_OPCODES : number[] = []

  switch (true) {
    case (typeof (word) !== 'number'):
      return false
    case (word === 0):
      return true
    case (DISABLED_OPCODES.includes(word)):
      return false
    case (MIN_RANGE < word && word < MAX_RANGE):
      return true
    default:
      return false
  }
}


================================================
FILE: src/lib/sig/index.ts
================================================
import { SWSigner } from './segwit/index.js'
import { TRSigner } from './taproot/index.js'

export const Signer = {
  segwit  : SWSigner,
  taproot : TRSigner
}


================================================
FILE: src/lib/sig/segwit/hash.ts
================================================
import { Buff }       from '@cmdcode/buff-utils'
import * as ENC       from '../../tx/encode.js'
import { Script }     from '../../script/index.js'
import { HashConfig } from '../types.js'
import { Tx }         from '../../tx/index.js'

import {
  InputData,
  OutputData,
  TxTemplate,
  Bytes
} from '../../../schema/types.js'
import { hash160, hash256 } from '@cmdcode/crypto-utils'

const VALID_HASH_TYPES = [ 0x01, 0x02, 0x03 ]

export function hashTx (
  txdata : TxTemplate | Bytes,
  idx    : number,
  config : HashConfig = {}
) : Buff {
  // Unpack the sigflag from our config object.
  const { sigflag = 0x01 } = config
  // Check if the ANYONECANPAY flag is set.
  const isAnypay = (sigflag & 0x80) === 0x80
  // Save a normalized version of the sigflag.
  const flag = sigflag % 0x80
  // Check if the sigflag exists as a valid type.
  if (!VALID_HASH_TYPES.includes(flag)) {
    throw new Error('Invalid hash type: ' + String(sigflag))
  }
  // Normalize the tx into JSON format.
  const tx = Tx.fmt.toJson(txdata)
  // Unpack the tx object.
  const { version, vin, vout, locktime } = tx
  // Unpack the chosen input for signing.
  const { txid, vout: prevIdx, prevout, sequence } = vin[idx]
  // Unpack the prevout for the chosen input.
  const { value } = prevout ?? {}
  // Check if a prevout value is provided.
  if (value === undefined) {
    throw new Error('Prevout value is empty!')
  }
  // Initialize our script variable from the config.
  let script = config.script
  // Check if a pubkey is provided (instead of a script).
  if (
    script === undefined &&
    config.pubkey !== undefined
  ) {
    const pkhash = hash160(config.pubkey)
    script = `76a914${pkhash.hex}88ac`
  }
  // Make sure that some form of script has been provided.
  if (script === undefined) {
    throw new Error('No pubkey / script has been set!')
  }
  // Throw if OP_CODESEPARATOR is used in a script.
  if (Script.fmt.toAsm(script).includes('OP_CODESEPARATOR')) {
    throw new Error('This library does not currently support the use of OP_CODESEPARATOR in segwit scripts.')
  }

  const sighash = [
    ENC.encodeVersion(version),
    hashPrevouts(vin, isAnypay),
    hashSequence(vin, flag, isAnypay),
    ENC.encodeTxid(txid),
    ENC.encodePrevOut(prevIdx),
    Script.encode(script, true),
    ENC.encodeValue(value),
    ENC.encodeSequence(sequence),
    hashOutputs(vout, idx, flag),
    ENC.encodeLocktime(locktime),
    Buff.num(sigflag, 4).reverse()
  ]

  // console.log('sighash:', sighash.map(e => Buff.bytes(e).hex))

  return hash256(Buff.join(sighash))
}

function hashPrevouts (
  vin : InputData[],
  isAnypay ?: boolean
) : Uint8Array {
  if (isAnypay === true) {
    return Buff.num(0, 32)
  }

  const stack = []

  for (const { txid, vout } of vin) {
    stack.push(ENC.encodeTxid(txid))
    stack.push(ENC.encodePrevOut(vout))
  }

  return hash256(Buff.join(stack))
}

function hashSequence (
  vin      : InputData[],
  sigflag  : number,
  isAnyPay : boolean
) : Uint8Array {
  if (isAnyPay || sigflag !== 0x01) {
    return Buff.num(0, 32)
  }

  const stack = []

  for (const { sequence } of vin) {
    stack.push(ENC.encodeSequence(sequence))
  }
  return hash256(Buff.join(stack))
}

function hashOutputs (
  vout    : OutputData[],
  idx     : number,
  sigflag : number
) : Uint8Array {
  const stack = []

  if (sigflag === 0x01) {
    for (const { value, scriptPubKey } of vout) {
      stack.push(ENC.encodeValue(value))
      stack.push(Script.encode(scriptPubKey, true))
    }
    return hash256(Buff.join(stack))
  }

  if (sigflag === 0x03 && idx < vout.length) {
    const { value, scriptPubKey } = vout[idx]
    stack.push(ENC.encodeValue(value))
    stack.push(Script.encode(scriptPubKey, true))
    return hash256(Buff.join(stack))
  }

  return Buff.num(0, 32)
}


================================================
FILE: src/lib/sig/segwit/index.ts
================================================
import { hashTx }   from './hash.js'
import { signTx }   from './sign.js'
import { verifyTx } from './verify.js'

export const SWSigner = {
  hash   : hashTx,
  sign   : signTx,
  verify : verifyTx
}


================================================
FILE: src/lib/sig/segwit/sign.ts
================================================
import { Buff }       from '@cmdcode/buff-utils'
import { noble }      from '@cmdcode/crypto-utils'
import { hashTx }     from './hash.js'
import { TxTemplate } from '../../../schema/types.js'
import { HashConfig } from '../types.js'

export function signTx (
  seckey  : string | Uint8Array,
  txdata  : TxTemplate | string | Uint8Array,
  index   : number,
  config  : HashConfig = {}
) : Buff {
  const { sigflag = 0x01 } = config
  const hash = hashTx(txdata, index, config)
  const sig  = noble.secp.sign(hash, seckey).toDERRawBytes(true)
  return Buff.join([ sig, sigflag ])
}


================================================
FILE: src/lib/sig/segwit/verify.ts
================================================
import { Buff }       from '@cmdcode/buff-utils'
import { noble }      from '@cmdcode/crypto-utils'
import { safeThrow }  from '../../utils.js'
import { Tx }         from '../../tx/index.js'
import { hashTx }     from './hash.js'
import { TxTemplate } from '../../../schema/types.js'
import { Script }     from '../../script/index.js'
import { HashConfig } from '../types.js'

export function verifyTx (
  txdata : TxTemplate | string | Uint8Array,
  index  : number,
  config : HashConfig = {}
) : boolean {
  const tx = Tx.fmt.toJson(txdata)
  const { throws = false } = config
  const { witness = [] }   = tx.vin[index]
  const witnessData = Tx.util.readWitness(witness)

  const { script, params } = witnessData

  let pub : Buff | null = null

  if (params.length < 1) {
    return safeThrow('Invalid witness data: ' + String(witness), throws)
  }

  if (
    config.script === undefined &&
    script !== null
  ) {
    config.script = script
  }

  if (config.pubkey !== undefined) {
    pub = Buff.bytes(config.pubkey)
  } else if (
    params.length > 1 &&
    params[1].length === 33
  ) {
    pub = Buff.bytes(params[1])
  } else {
    return safeThrow('No pubkey provided!', throws)
  }

  const rawsig    = Script.fmt.toParam(params[0])
  const signature = rawsig.slice(0, -1)
  const sigflag   = rawsig.slice(-1)[0]

  const hash = hashTx(tx, index, { ...config, sigflag })

  // console.log('sign:', signature.hex)
  // console.log('flag:', Buff.num(sigflag).hex)
  // console.log('hash:', hash.hex)
  // console.log('pubk:', pub.hex)

  if (!noble.secp.verify(signature, hash, pub)) {
    return safeThrow('Invalid signature!', throws)
  }

  return true
}


================================================
FILE: src/lib/sig/taproot/hash.ts
================================================
import { Buff }         from '@cmdcode/buff-utils'
import * as ENC         from '../../tx/encode.js'
import { Tx }           from '../../tx/index.js'
import { Script }       from '../../script/index.js'
import { encodeScript } from '../../script/encode.js'
import { HashConfig }   from '../types.js'

import {
  Bytes,
  InputData,
  OutputData,
  ScriptData,
  TxTemplate
} from '../../../schema/types.js'

const VALID_HASH_TYPES = [ 0x00, 0x01, 0x02, 0x03, 0x81, 0x82, 0x83 ]

export function hashTx (
  template : TxTemplate | Bytes,
  index    : number,
  config   : HashConfig = {}
) : Buff {
  // Unpack configuration.
  const {
    extension,
    sigflag       = 0x00,
    extflag       = 0x00,
    key_version   = 0x00,
    separator_pos = 0xFFFFFFFF
  } = config

  // Normalize and unpack the txdata object.
  const txdata = Tx.fmt.toJson(template)
  const { version, vin: input, vout: output, locktime } = txdata

  if (index >= input.length) {
    // If index is out of bounds, throw error.
    throw new Error('Index out of bounds: ' + String(index))
  }

  if (!VALID_HASH_TYPES.includes(sigflag)) {
    // If the sigflag is an invalid type, throw error.
    throw new Error('Invalid hash type: ' + String(sigflag))
  }

  if (extflag < 0 || extflag > 127) {
    // If the extflag is out of range, throw error.
    throw new Error('Extention flag out of range: ' + String(extflag))
  }

  // Unpack the input being signed.
  const { txid, vout, sequence, witness = [] } = input[index]

  // Define the parameters of the transaction.
  const isAnyPay  = (sigflag & 0x80) === 0x80
  const annex     = getAnnexData(witness)
  const annexBit  = (annex !== undefined) ? 1 : 0
  const extendBit = (extension !== undefined) ? 1 : 0
  const spendType = ((extflag + extendBit) * 2) + annexBit
  const hashtag   = Buff.str('TapSighash').digest

  // Begin building our preimage.
  const preimage = [
    hashtag,                      // Buffer input with
    hashtag,                      // 2x hashed strings.
    Buff.num(0x00, 1),            // Add zero-byte.
    Buff.num(sigflag, 1),         // Commit to signature flag.
    ENC.encodeVersion(version),   // Commit to tx version.
    ENC.encodeLocktime(locktime)  // Commit to tx locktime.
  ]

  if (!isAnyPay) {
    // If flag ANYONE_CAN_PAY is not set,
    // then commit to all inputs.
    const prevouts = input.map(e => getPrevout(e))
    preimage.push(
      hashOutpoints(input),   // Commit to txid/vout for each input.
      hashAmounts(prevouts),  // Commit to prevout amount for each input.
      hashScripts(prevouts),  // Commit to prevout script for each input.
      hashSequence(input)     // Commit to sequence value for each input.
    )
  }

  if ((sigflag & 0x03) < 2 || (sigflag & 0x03) > 3) {
    // If neither SINGLE or NONE flags are set,
    // include a commitment to all outputs.
    preimage.push(hashOutputs(output))
  }

  // At this step, we include the spend type.
  preimage.push(Buff.num(spendType, 1))

  if (isAnyPay) {
    // If ANYONE_CAN_PAY flag is set, then we will
    // provide a commitment to the input being signed.
    const { value, scriptPubKey } = getPrevout(input[index])
    preimage.push(
      ENC.encodeTxid(txid),               // Commit to the input txid.
      ENC.encodePrevOut(vout),            // Commit to the input vout index.
      ENC.encodeValue(value),             // Commit to the input's prevout value.
      Script.encode(scriptPubKey, true),  // Commit to the input's prevout script.
      ENC.encodeSequence(sequence)        // Commit to the input's sequence value.
    )
  } else {
    // Otherwise, we must have already included a commitment
    // to all inputs in the tx, so simply add a commitment to
    // the index of the input we are signing for.
    preimage.push(Buff.num(index, 4).reverse())
  }

  if (annex !== undefined) {
    // If an annex has been set, include it here.
    preimage.push(annex)
  }

  if ((sigflag & 0x03) === 0x03) {
    // If the SINGLE flag is set, then include a
    // commitment to the output which is adjacent
    // to the input that we are signing for.
    preimage.push(hashOutput(output[index]))
  }

  if (extension !== undefined) {
    // If we are extending this signature to include
    // other commitments (such as a tapleaf), then we
    // will add it to the preimage here.
    preimage.push(
      Buff.bytes(extension),      // Extention data (in bytes).
      Buff.num(key_version),      // Key version (reserved for future upgrades).
      Buff.num(separator_pos, 4, 'le')  // If OP_CODESEPARATOR is used, this must be set.
    )
  }

  // Useful for debugging the preimage stack.
  // console.log(preimage.map(e => Buff.raw(e).hex))

  return Buff.join(preimage).digest
}

export function hashOutpoints (
  vin : InputData[]
) : Uint8Array {
  const stack = []
  for (const { txid, vout } of vin) {
    stack.push(ENC.encodeTxid(txid))
    stack.push(ENC.encodePrevOut(vout))
  }
  return Buff.join(stack).digest
}

export function hashSequence (
  vin : InputData[]
) : Uint8Array {
  const stack = []
  for (const { sequence } of vin) {
    stack.push(ENC.encodeSequence(sequence))
  }
  return Buff.join(stack).digest
}

export function hashAmounts (
  prevouts : OutputData[]
) : Uint8Array {
  const stack = []
  for (const { value } of prevouts) {
    stack.push(ENC.encodeValue(value))
  }
  return Buff.join(stack).digest
}

export function hashScripts (
  prevouts : OutputData[]
) : Uint8Array {
  const stack = []
  for (const { scriptPubKey } of prevouts) {
    stack.push(encodeScript(scriptPubKey, true))
  }
  return Buff.join(stack).digest
}

export function hashOutputs (
  vout : OutputData[]
) : Uint8Array {
  const stack = []
  for (const { value, scriptPubKey } of vout) {
    stack.push(ENC.encodeValue(value))
    stack.push(Script.encode(scriptPubKey, true))
  }
  return Buff.join(stack).digest
}

export function hashOutput (
  vout : OutputData
) : Uint8Array {
  return Buff.join([
    ENC.encodeValue(vout.value),
    Script.encode(vout.scriptPubKey, true)
  ]).digest
}

function getAnnexData (
  witness ?: ScriptData[]
) : Uint8Array | undefined {
  // If no witness exists, return undefined.
  if (witness === undefined) return
  // If there are less than two elements, return undefined.
  if (witness.length < 2) return
  // Define the last element as the annex.
  let annex = witness.at(-1)
  // If element is a string,
  if (typeof annex === 'string') {
    // convert to bytes.
    annex = Buff.hex(annex)
  }
  // If first byte is annex flag,
  if (
    annex instanceof Uint8Array &&
    annex[0] === 0x50
  ) {
    // return a digest of the annex.
    return Buff.raw(annex).prefixSize('be').digest
  }
  // Else, return undefined.
  return undefined
}

function getPrevout (vin : InputData) : OutputData {
  if (vin.prevout === undefined) {
    throw new Error('Prevout data missing for input: ' + String(vin.txid))
  }
  return vin.prevout
}


================================================
FILE: src/lib/sig/taproot/index.ts
================================================
import { hashTx }   from './hash.js'
import { signTx }   from './sign.js'
import { verifyTx } from './verify.js'

export const TRSigner = {
  hash   : hashTx,
  sign   : signTx,
  verify : verifyTx
}


================================================
FILE: src/lib/sig/taproot/sign.ts
================================================
import { Buff, Bytes }  from '@cmdcode/buff-utils'
import { Field, Point } from '@cmdcode/crypto-utils'
import { hashTx }       from './hash.js'
import { HashConfig }   from '../types.js'
import { TxTemplate }   from '../../../schema/types.js'
import { xOnlyPub }     from '../../tap/utils.js'
import { hashTag, safeThrow } from '../../utils.js'

const FIELD_SIZE  = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2Fn
const CURVE_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n

export function signTx (
  seckey  : string | Uint8Array,
  txdata  : TxTemplate | string | Uint8Array,
  index   : number,
  config  : HashConfig = {}
) : Buff {
  // Set the signature flag type.
  const { sigflag = 0x00 } = config
  // Calculate the transaction hash.
  const hash = hashTx(txdata, index, config)
  // Sign the transaction hash with secret key.
  const sig  = sign(seckey, hash)
  // Return the signature.
  return (sigflag === 0x00)
    ? Buff.raw(sig)
    : Buff.join([ sig, sigflag ])
}

export function sign (
  secret  : Bytes,
  message : Bytes,
  rand    : Bytes = Buff.random(32)
) : Buff {
  /**
   * Implementation of signature algorithm as specified in BIP0340.
   * https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
   */
  // Normalize our message into bytes.
  const m = Buff.bytes(message)
  // Let d' equal the integer value of secret key.
  const dp = new Field(secret)
  // Let P equal d' * G
  const P  = dp.point
  // If P has an odd Y coordinate, return negated version of d'.
  const d  = (P.hasEvenY) ? dp.big : dp.negated.big
  // Hash the auxiliary data according to BIP 0340.
  const a  = hashTag('BIP0340/aux', Buff.bytes(rand))
  // Let t equal the byte-wise xor of (d) and (a).
  const t  = d ^ a.big
  // Let our nonce value equal the tagged hash('BIP0340/nonce', t || P || m).
  const n  = hashTag('BIP0340/nonce', t, P.x.raw, m)
  // Let k' equal our nonce mod N.
  const kp = new Field(n)
  // Let R equal k' * G.
  const R  = kp.point
  // If R has an odd Y coordinate, return negated version of k'.
  const k  = (R.hasEvenY) ? kp.big : kp.negated.big
  // Let e equal the tagged hash('BIP0340/challenge' || R || P || m) mod n.
  const e  = new Field(hashTag('BIP0340/challenge', R.x.raw, P.x.raw, m))
  // Let s equal (k + ed) mod n.
  const s  = new Field(k + (e.big * d))
  // Return (R || s) as a signature
  return Buff.join([ R.x.raw, s.raw ])
}

export function verify (
  signature : Bytes,
  message   : Bytes,
  pubkey    : Bytes,
  shouldThrow = false
) : boolean {
   /**
   * Implementation of verify algorithm as specified in BIP0340.
   * https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
   */
  // Get the Point value for pubkey.
  const P = Point.from_x(xOnlyPub(pubkey))
  // Normalize the message into bytes.
  const m = Buff.bytes(message)
  // Convert signature into a stream object.
  const stream = Buff.bytes(signature).stream
  // Check if the signature size is at least 64 bytes.
  if (stream.size < 64) {
    safeThrow('Signature length is too small: ' + String(stream.size), shouldThrow)
  }
  // Let r equal first 32 bytes of signature.
  const r = stream.read(32)
  // Fail if r > p (field size).
  if (r.big > FIELD_SIZE) {
    safeThrow('Signature r value greater than field size!', shouldThrow)
  }
  // Let s equal next 32 bytes of signature.
  const s = stream.read(32)
  // Fail if s > n (curve order).
  if (s.big > CURVE_ORDER) {
    safeThrow('Signature s value greater than curve order!', shouldThrow)
  }
  // Let e equal the tagged hash('BIP0340/challenge' || R || P || m) mod n.
  const e = new Field(hashTag('BIP0340/challenge', r.raw, P.x.raw, m))
  // Let R equal s * G - eP.
  const sG = new Field(s).point
  const eP = P.mul(e.big)
  const R  = sG.sub(eP)
  // Reject if R value has an odd Y coordinate.
  if (R.hasOddY) {
    safeThrow('Signature R value has odd Y coordinate!', shouldThrow)
  }
  // Reject if R value is infinite.
  if (R.x.big === 0n) {
    safeThrow('Signature R value is infinite!', shouldThrow)
  }
  // Return if x coordinate of R value equals r.
  return R.x.big === r.big
}


================================================
FILE: src/lib/sig/taproot/verify.ts
================================================
import { Buff, Stream }  from '@cmdcode/buff-utils'
import { checkPath }     from '../../tap/key.js'
import { verify }        from './sign.js'
import { safeThrow }     from '../../utils.js'
import { getTapLeaf }    from '../../tap/tree.js'
import { Tx }            from '../../tx/index.js'
import { hashTx }        from './hash.js'
import { TxTemplate }    from '../../../schema/types.js'
import { Script }        from '../../script/index.js'
import { HashConfig }    from '../types.js'

export function verifyTx (
  txdata  : TxTemplate | string | Uint8Array,
  index   : number,
  config  : HashConfig = {}
) : boolean {
  const tx = Tx.fmt.toJson(txdata)
  const { throws = false } = config
  const { prevout, witness = [] } = tx.vin[index]
  const witnessData = Tx.util.readWitness(witness)
  const { cblock, script, params } = witnessData

  let pub : Buff

  if (params.length < 1) {
    return safeThrow('Invalid witness data: ' + String(witness), throws)
  }

  const { scriptPubKey } = prevout ?? {}

  if (scriptPubKey === undefined) {
    return safeThrow('Prevout scriptPubKey is empty!', throws)
  }

  const { type, data: tapkey } = Tx.util.readScriptPubKey(scriptPubKey)

  if (type !== 'p2tr') {
    return safeThrow('Prevout script is not a valid taproot output:' + tapkey.hex, throws)
  }

  if (tapkey.length !== 32) {
    return safeThrow('Invalid tapkey length: ' + String(tapkey.length), throws)
  }

  if (
    cblock !== null &&
    script !== null
  ) {
    const version    = cblock[0] & 0xfe
    const target     = getTapLeaf(script, version)
    config.extension = target

    if (!checkPath(tapkey, target, cblock, { throws })) {
      return safeThrow('cblock verification failed!', throws)
    }
  }

  if (config.pubkey !== undefined) {
    pub = Buff.bytes(config.pubkey)
  } else if (params.length > 1 && params[1].length === 32) {
    pub = Buff.bytes(params[1])
  } else {
    pub = Buff.bytes(tapkey)
  }

  const rawsig    = Script.fmt.toParam(params[0])
  const stream    = new Stream(rawsig)
  const signature = stream.read(64).raw

  if (stream.size === 1) {
    config.sigflag = stream.read(1).num
    if (config.sigflag === 0x00) {
      return safeThrow('0x00 is not a valid appended sigflag!', throws)
    }
  }

  const hash = hashTx(tx, index, config)

  if (!verify(signature, hash, pub, throws)) {
    return safeThrow('Invalid signature!', throws)
  }

  return true
}


================================================
FILE: src/lib/sig/types.ts
================================================
import { Bytes } from '../../schema/types.js'

export interface HashConfig {
  extension     ?: Bytes    // Include a tapleaf hash with your signature hash.
  pubkey        ?: Bytes    // Verify using this pubkey instead of the tapkey.
  script        ?: Bytes    // Hash using this script (for segwit spends).
  sigflag       ?: number   // Set the signature type flag.
  separator_pos ?: number   // If using OP_CODESEPARATOR, specify the latest opcode position.
  extflag       ?: number   // Set the extention version flag (future use).
  key_version   ?: number   // Set the key version flag (future use).
  throws        ?: boolean  // Should throw an exception on failure.
}


================================================
FILE: src/lib/tap/index.ts
================================================
import * as SCR  from './tree.js'
import * as TWK  from './tweak.js'
import * as CHK  from './key.js'

export const TapTree = {
  getTag    : SCR.getTapTag,
  getLeaf   : SCR.getTapLeaf,
  getBranch : SCR.getTapBranch,
  getRoot   : SCR.getTapRoot
}

export const TapUtil = {
  readCtrlBlock : CHK.readCtrlBlock,
  readParityBit : CHK.readParityBit
}

export const TapTweak = {
  getPubKey   : TWK.getTweakedPub,
  getSecKey   : TWK.getTweakedSec,
  getTweak    : TWK.getTapTweak,
  tweakSecKey : TWK.tweakSecKey,
  tweakPubKey : TWK.tweakPubKey
}

export const Tap = {
  getPubKey     : CHK.getTapPubKey,
  getSecKey     : CHK.getTapSecKey,
  encodeScript  : SCR.getTapScript,
  checkPath     : CHK.checkPath,
  tree          : TapTree,
  tweak         : TapTweak,
  util          : TapUtil,
  SCRIPT_PUBKEY : TWK.SCRIPT_PUBKEY
}


================================================
FILE: src/lib/tap/key.ts
================================================
import { Buff, Stream }      from '@cmdcode/buff-utils'
import { util }              from '@cmdcode/crypto-utils'
import { getTweakedKey }     from './tweak.js'
import { safeThrow }         from '../utils.js'
import { xOnlyPub }          from './utils.js'
import { getTapBranch, merkleize }      from './tree.js'
import { CtrlBlock, TapConfig, TapKey } from './types.js'
import { Bytes } from '../../schema/types.js'

const DEFAULT_VERSION = 0xc0

export function getTapSecKey (
  seckey  : Bytes,
  config  : TapConfig = {}
) : TapKey {
  return getTapKey(seckey, { ...config, isPrivate: true })
}

export function getTapPubKey (
  pubkey  : Bytes,
  config  : TapConfig = {}
) : TapKey {
  return getTapKey(pubkey, { ...config, isPrivate: false })
}

function getTapKey (
  intkey : Bytes,
  config : TapConfig = {}
) : TapKey {
  const {
    isPrivate = false,
    tree      = [],
    version   = DEFAULT_VERSION
  } = config

  const pubkey = (isPrivate)
    ? util.getPublicKey(intkey, true)
    : xOnlyPub(intkey)

  let { target } = config

  if (target !== undefined) target = Buff.bytes(target).hex

  let tapkey, ctrlpath : string[] = []

  if (tree.length > 0) {
    // Merkelize the leaves into a root hash (with proof).
    const [ root, _t, path ] = merkleize(tree, target)
    // Get the control path from the merkelized output.
    ctrlpath = path
    // Get the tapped key from the internal key.
    tapkey = getTweakedKey(intkey, root, isPrivate)
  } else {
    if (target !== undefined) {
      // Get the tapped key from the single tapleaf.
      tapkey = getTweakedKey(intkey, target, isPrivate)
    } else {
      // Get the tapped key from an empty tweak.
      tapkey = getTweakedKey(intkey, undefined, isPrivate)
    }
  }
  // Get the parity bit for the (public) tapkey.
  const parity : number = (isPrivate)
    ? util.getPublicKey(tapkey)[0]
    : tapkey[0]
  // Get the block version / parity bit.
  const cbit = Buff.num(version + readParityBit(parity))
  // Create the control block, starting with
  // the control bit and the (x-only) pubkey.
  const block = [ cbit, pubkey ]
  // If there is more than one path, add to block.
  if (ctrlpath.length > 0) {
    ctrlpath.forEach(e => block.push(Buff.hex(e)))
  }
  // Merge the data together into one array.
  const cblock = Buff.join(block)
  // If target is not undefined:
  if (target !== undefined) {
    // Check that the path is valid.
    if (!checkPath(tapkey, target, cblock, config)) {
      throw new Error('Path checking failed! Unable to generate path.')
    }
  }
  return [ xOnlyPub(tapkey).hex, cblock.hex ]
}

export function checkPath (
  tapkey : Bytes,
  target : Bytes,
  cblock : Bytes,
  config : TapConfig = {}
) : boolean {
  const { isPrivate = false, throws = false } = config
  const { parity, paths, intkey } = readCtrlBlock(cblock)

  const pub = (isPrivate)
    ? util.getPublicKey(tapkey, true)
    : xOnlyPub(tapkey)

  const extkey = Buff.join([ parity, pub ])

  if (extkey.length !== 33) {
    return safeThrow('Invalid tapkey: ' + extkey.hex, throws)
  }

  let branch = Buff.bytes(target).hex

  for (const path of paths) {
    branch = getTapBranch(branch, path)
  }

  const k = getTweakedKey(intkey, branch)

  // console.log('branch:', branch)
  // console.log('intkey:', Buff.raw(intkey).hex)
  // console.log('extkey:', extkey.hex)
  // console.log('tapkey:', k.hex)

  return (Buff.raw(k).hex === Buff.raw(extkey).hex)
}

export function readCtrlBlock (cblock : Bytes) : CtrlBlock {
  const buffer = new Stream(Buff.bytes(cblock))
  const cbyte  = buffer.read(1).num
  const intkey = buffer.read(32)
  const [ version, parity ] = (cbyte % 2 === 0)
    ? [ cbyte, 0x02 ]
    : [ cbyte - 1, 0x03 ]
  const paths  = []
  while (buffer.size >= 32) {
    paths.push(buffer.read(32).hex)
  }
  if (buffer.size !== 0) {
    throw new Error('Non-empty buffer on control block: ' + String(buffer))
  }
  return { intkey, paths, parity, version }
}

export function readParityBit (parity : number | string = 0x02) : number {
  if (parity === 0 || parity === 1) return parity
  if (parity === 0x02 || parity === '02') return 0
  if (parity === 0x03 || parity === '03') return 1
  throw new Error('Invalid parity bit: ' + String(parity))
}


================================================
FILE: src/lib/tap/tree.ts
================================================
import { Buff }              from '@cmdcode/buff-utils'
import { TapTree, TapProof } from './types.js'
import { Bytes, ScriptData } from '../../schema/types.js'
import { Script } from '../script/index.js'

const DEFAULT_VERSION = 0xc0

export function getTapTag (tag : string) : Buff {
  const htag = Buff.str(tag).digest
  return Buff.join([ htag, htag ])
}

export function getTapLeaf (
  data : Bytes,
  version = DEFAULT_VERSION
) : string {
  return Buff.join([
    getTapTag('TapLeaf'),
    getVersion(version),
    Buff.bytes(data)
  ]).digest.hex
}

export function getTapScript (
  script   : ScriptData,
  version ?: number
) : string {
  return getTapLeaf(Script.fmt.toBytes(script), version)
}

export function getTapBranch (
  leafA : string,
  leafB : string
) : string {
  // Compare leaves in lexical order.
  if (leafB < leafA) {
    // Swap leaves if needed.
    [ leafA, leafB ] = [ leafB, leafA ]
  }
  // Return digest of leaves as a branch hash.
  return Buff.join([
    getTapTag('TapBranch'),
    Buff.hex(leafA).raw,
    Buff.hex(leafB).raw
  ]).digest.hex
}

export function getTapRoot (
  leaves : TapTree
) : Buff {
  // Merkelize the leaves into a root hash.
  return Buff.hex(merkleize(leaves)[0])
}

export function merkleize (
  taptree  : TapTree,
  target  ?: string,
  path     : string[] = []
) : TapProof {
  const leaves : string[] = []
  const tree   : string[] = []

  if (taptree.length < 1) {
    throw new Error('Tree is empty!')
  }

  // If there are any nested leaves,
  // resolve them before moving on.
  for (let i = 0; i < taptree.length; i++) {
    const leaf = taptree[i]
    if (Array.isArray(leaf)) {
      const [ r, t, p ] = merkleize(leaf, target)
      target = t
      leaves.push(r)
      for (const e of p) {
        path.push(e)
      }
    } else { leaves.push(leaf) }
  }

  // If there is only one leaf,
  // then return it as the root.
  if (leaves.length === 1) {
    return [ leaves[0], target, path ]
  }
  // Ensure the tree is sorted.
  leaves.sort()
  // Ensure the tree is balanced evenly.
  if (leaves.length % 2 !== 0) {
    // If uneven, duplicate the last leaf.
    leaves.push(leaves[leaves.length - 1])
  }

  // Sort through the leaves (two at a time).
  for (let i = 0; i < leaves.length - 1; i += 2) {
    // Compute two leaves into a branch.
    const branch = getTapBranch(leaves[i], leaves[i + 1])
    // Push our branch to the tree.
    tree.push(branch)
    // Check if a proof target is specified.
    if (typeof target === 'string') {
      // Check if this branch is part of our proofs.
      if (target === leaves[i]) {
        // If so, include right-side of branch.
        path.push(leaves[i + 1])
        target = branch
      } else if (target === leaves[i + 1]) {
        // If so, include left-side of branch.
        path.push(leaves[i])
        target = branch
      }
    }
  }
  return merkleize(tree, target, path)
}

export function getVersion (version = 0xc0) : number {
  return version & 0xfe
}


================================================
FILE: src/lib/tap/tweak.ts
================================================
import { Buff }         from '@cmdcode/buff-utils'
import { Field, Point } from '@cmdcode/crypto-utils'
import { getTapTag }    from './tree.js'
import { xOnlyPub }     from './utils.js'
import { Bytes }        from '../../schema/types.js'

export function getTapTweak (
  key   : Bytes,
  data  : Bytes = new Uint8Array(),
  isPrivate = false
) : Buff {
  const pub = (isPrivate)
    ? new Field(key).point.x.raw
    : xOnlyPub(key)
  return Buff.join([ getTapTag('TapTweak'), pub, Buff.bytes(data) ]).digest
}

export function getTweakedKey (
  intkey  : Bytes,
  data   ?: Bytes,
  isPrivate = false
) : Buff {
  if (data === undefined) data = new Uint8Array()
  const k = Buff.bytes(intkey)
  // Calculate the tweak.
  const t = getTapTweak(intkey, data, isPrivate)
  // Return the tweaked key based on type.
  if (isPrivate) {
    // Return tweaked private key.
    return tweakSecKey(k, t)
  } else {
    // Return tweaked public key.
    return tweakPubKey(k, t)
  }
}

export function getTweakedPub (
  pubkey  : Bytes,
  data   ?: Bytes
) : Buff {
  return getTweakedKey(pubkey, data)
}

export function getTweakedSec (
  seckey  : Bytes,
  data   ?: Bytes
) : Buff {
  return getTweakedKey(seckey, data, true)
}

export function tweakSecKey (
  seckey : Bytes,
  tweak  : Bytes
) : Buff {
  let sec = new Field(seckey)
  if (sec.point.hasOddY) {
    sec = sec.negate()
  }
  return Buff.raw(sec.add(tweak).raw)
}

export function tweakPubKey (
  pubkey : Bytes,
  tweak  : Bytes
) : Buff {
  pubkey = xOnlyPub(pubkey)
  const P = Point.from_x(pubkey)
  const Q = P.add(tweak)
  return Buff.raw(Q.raw)
}

function getScriptOnlyPubkey () : Buff {
  // Generated as specified in BIP0341:
  // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
  const G = Buff.hex('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
  return Point.from_x(G.digest).x
}

export const SCRIPT_PUBKEY = getScriptOnlyPubkey()


================================================
FILE: src/lib/tap/types.ts
================================================
import { Buff } from '@cmdcode/buff-utils'
import { Bytes, ScriptData } from '../../schema/types.js'

export type TapKey = [
  key    : string,
  cblock : string
]

export interface TapConfig {
  data      ?: Bytes
  isPrivate ?: boolean
  script    ?: ScriptData
  tapleaf   ?: Bytes
  target    ?: Bytes
  throws    ?: boolean
  tree      ?: TapTree
  version   ?: number
}

export interface CtrlBlock {
  version : number
  parity  : number
  intkey  : Buff
  paths   : string[]
}

export type TapTree = Array<string | string[]>

export type TapProof = [
  root   : string,
  target : string | undefined,
  path   : string[]
]


================================================
FILE: src/lib/tap/utils.ts
================================================
import { Buff, Bytes } from '@cmdcode/buff-utils'

export function xOnlyPub (key : Bytes) : Buff {
  const bytes = Buff.bytes(key)
  return (bytes.length > 32) ? bytes.slice(1, 33) : bytes
}


================================================
FILE: src/lib/tx/create.ts
================================================
import { TxData, TxTemplate } from '../../schema/types.js'

const DEFAULT_TX = {
  version  : 2,
  vin      : [],
  vout     : [],
  locktime : 0
}

const DEFAULT_VIN = {
  scriptSig : [],
  sequence  : 4294967293,
  witness   : []
}

const DEFAULT_VOUT = {
  value        : 0n,
  scriptPubKey : []
}

export function createTx (template : TxTemplate) : TxData {
  const tx = { ...DEFAULT_TX, ...template }
  tx.vin  = tx.vin.map(txin   => { return { ...DEFAULT_VIN, ...txin }   })
  tx.vout = tx.vout.map(txout => { return { ...DEFAULT_VOUT, ...txout } })
  return tx as TxData
}


================================================
FILE: src/lib/tx/decode.ts
================================================
import { Buff, Stream } from '@cmdcode/buff-utils'

import {
  TxData,
  InputData,
  OutputData
} from '../../schema/types.js'

export function decodeTx (bytes : string | Uint8Array) : TxData {
  /** Decode a raw bitcoin transaction. */

  if (typeof bytes === 'string') {
    bytes = Buff.hex(bytes).raw
  }

  // Setup a byte-stream.
  const stream = new Stream(bytes)

  const version = readVersion(stream)

  // Check and enable any flags that are set.
  const hasWitness = checkWitnessFlag(stream)

  // Parse our inputs and outputs.
  const vin  = readInputs(stream)
  const vout = readOutputs(stream)

  // If witness flag is set, parse witness data.
  if (hasWitness) {
    for (const txin of vin) {
      txin.witness = readWitness(stream)
    }
  }

  // Parse locktime.
  const locktime = readLocktime(stream)

  // Return transaction object with calculated fields.
  return { version, vin, vout, locktime }
}

function readVersion (stream : Stream) : number {
  return stream.read(4).reverse().toNum()
}

function checkWitnessFlag (stream : Stream) : boolean {
  const [ marker, flag ] : number[] = [ ...stream.peek(2) ]
  if (marker === 0) {
    stream.read(2)
    if (flag === 1) {
      return true
    } else {
      throw new Error(`Invalid witness flag: ${flag}`)
    }
  }
  return false
}

function readInputs (stream : Stream) : InputData[] {
  const inputs = []
  const vinCount = stream.readSize('le')
  for (let i = 0; i < vinCount; i++) {
    inputs.push(readInput(stream))
  }
  return inputs
}

function readInput (stream : Stream) : InputData {
  const txin = {
    txid      : stream.read(32).reverse().toHex(),
    vout      : stream.read(4).reverse().toNum(),
    scriptSig : readScript(stream, true),
    sequence  : stream.read(4).reverse().toHex(),
    witness   : []
  }
  return txin
}

function readOutputs (stream : Stream) : OutputData[] {
  const outputs  = []
  const outcount = stream.readSize('le')
  for (let i = 0; i < outcount; i++) {
    outputs.push(readOutput(stream))
  }
  return outputs
}

function readOutput (stream : Stream) : OutputData {
  const txout = {
    value        : stream.read(8).reverse().big,
    scriptPubKey : readScript(stream, true)
  }
  return txout
}

function readWitness (stream : Stream) : string[] {
  const stack = []
  const count = stream.readSize()
  for (let i = 0; i < count; i++) {
    const word = readData(stream, true)
    stack.push(word ?? '')
  }
  return stack
}

function readData (
  stream  : Stream,
  varint ?: boolean
) : string | null {
  const size = (varint === true)
    ? stream.readSize('le')
    : stream.size
  return size > 0
    ? stream.read(size).hex
    : null
}

function readScript (
  stream  : Stream,
  varint ?: boolean
) : string | string[] {
  const data = readData(stream, varint)
  return (data !== null) ? data : []
}

function readLocktime (stream : Stream) : number {
  return stream.read(4).reverse().toNum()
}


================================================
FILE: src/lib/tx/encode.ts
================================================
import { Buff }         from '@cmdcode/buff-utils'
import { encodeScript } from '../script/encode.js'
import { createTx }     from './create.js'

import {
  InputData,
  OutputData,
  SequenceData,
  ScriptData,
  TxTemplate,
  LockData,
  ValueData,
  TxData
} from '../../schema/types.js'

export function encodeTx (
  txdata : TxTemplate | TxData,
  omitWitness ?: boolean
) : Buff {
  /** Convert a JSON-based Bitcoin transaction
   * into hex-encoded bytes.
   * */
  const { version, vin, vout, locktime } = createTx(txdata)

  const useWitness = (omitWitness !== true && checkForWitness(vin))

  const raw = [ encodeVersion(version) ]

  if (useWitness) {
    raw.push(Buff.hex('0001'))
  }

  raw.push(encodeInputs(vin))
  raw.push(encodeOutputs(vout))

  for (const txin of vin) {
    if (useWitness) {
      raw.push(encodeWitness(txin.witness))
    }
  }

  raw.push(encodeLocktime(locktime))

  return Buff.join(raw)
}

function checkForWitness (vin : InputData[]) : boolean {
  /** Check if any witness data is present. */
  for (const txin of vin) {
    const { witness } = txin
    if (
      typeof witness === 'string'   ||
      witness instanceof Uint8Array ||
      (Array.isArray(witness) && witness.length > 0)
    ) {
      return true
    }
  }
  return false
}

export function encodeVersion (num : number) : Uint8Array {
  return Buff.num(num, 4).reverse()
}

export function encodeTxid (txid : string) : Uint8Array {
  return Buff.hex(txid, 32).reverse()
}

export function encodePrevOut (vout : number) : Uint8Array {
  return Buff.num(vout, 4).reverse()
}

export function encodeSequence (
  sequence : SequenceData
) : Uint8Array {
  if (typeof sequence === 'string') {
    return Buff.hex(sequence, 4).reverse()
  }
  if (typeof sequence === 'number') {
    return Buff.num(sequence, 4).reverse()
  }
  throw new Error('Unrecognized format: ' + String(sequence))
}

function encodeInputs (arr : InputData[]) : Uint8Array {
  const raw : Uint8Array[] = [ Buff.varInt(arr.length, 'le') ]
  for (const vin of arr) {
    const { txid, vout, scriptSig, sequence } = vin
    raw.push(encodeTxid(txid))
    raw.push(encodePrevOut(vout))
    raw.push(encodeScript(scriptSig, true))
    raw.push(encodeSequence(sequence))
  }
  return Buff.join(raw)
}

export function encodeValue (
  value : ValueData
) : Uint8Array {
  if (typeof value === 'number') {
    if (value % 1 !== 0) {
      throw new Error('Value must be an integer:' + String(value))
    }
    return Buff.num(value, 8).reverse()
  }
  return Buff.big(value, 8).reverse()
}

function encodeOutputs (arr : OutputData[]) : Uint8Array {
  const raw : Uint8Array[] = [ Buff.varInt(arr.length, 'le') ]
  for (const vout of arr) {
    raw.push(encodeOutput(vout))
  }
  return Buff.join(raw)
}

function encodeOutput (
  vout : OutputData
) : Uint8Array {
  const { value, scriptPubKey } = vout
  const raw : Uint8Array[] = []
  raw.push(encodeValue(value))
  raw.push(encodeScript(scriptPubKey, true))
  return Buff.join(raw)
}

function encodeWitness (
  data : ScriptData[] = []
) : Uint8Array {
  const buffer : Uint8Array[] = []
  if (Array.isArray(data)) {
    const count = Buff.varInt(data.length)
    buffer.push(count)
    for (const entry of data) {
      buffer.push(encodeData(entry))
    }
    return Buff.join(buffer)
  } else { return Buff.bytes(data) }
}

function encodeData (data : ScriptData) : Buff {
  return (!isEmpty(data))
    ? encodeScript(data, true)
    : new Buff(0)
}

function isEmpty (data : ScriptData) : boolean {
  if (Array.isArray(data)) {
    return data.length === 0
  }
  if (typeof data === 'string') {
    if (data === '') return true
  }
  const bytes = Buff.bytes(data)
  return bytes.length === 1 && bytes[0] === 0
}

export function encodeLocktime (locktime : LockData) : Uint8Array {
  if (typeof locktime === 'string') {
    return Buff.hex(locktime, 4)
  }
  if (typeof locktime === 'number') {
    return Buff.num(locktime, 4).reverse()
  }
  throw new Error('Unrecognized format: ' + String(locktime))
}


================================================
FILE: src/lib/tx/format.ts
================================================
import { Buff }     from '@cmdcode/buff-utils'
import { isBytes }  from '../check.js'
import { decodeTx } from './decode.js'
import { encodeTx } from './encode.js'
import { createTx } from './create.js'

import { Bytes, TxData, TxTemplate } from '../../schema/types.js'

export function toJson (
  txdata ?: Bytes | TxData | TxTemplate
) : TxData {
  if (isBytes(txdata)) {
    return decodeTx(txdata)
  }
  if (
    typeof txdata === 'object' &&
    !(txdata instanceof Uint8Array)
  ) {
    encodeTx(txdata)
    return createTx(txdata)
  }
  throw new Error('Invalid format: ' + String(typeof txdata))
}

export function toBytes (
  txdata ?: Bytes | TxData | TxTemplate
) : Buff {
  if (isBytes(txdata)) {
    decodeTx(txdata)
    return Buff.bytes(txdata)
  }
  if (typeof txdata === 'object') {
    return encodeTx(txdata)
  }
  throw new Error('Invalid format: ' + String(typeof txdata))
}

export const TxFmt = {
  toBytes,
  toJson
}


================================================
FILE: src/lib/tx/index.ts
================================================
import { encodeTx } from './encode.js'
import { decodeTx } from './decode.js'
import { TxFmt }    from './format.js'
import { createTx } from './create.js'

import {
  getTxid,
  getTxSize,
  readScriptPubKey,
  readWitness
} from './parse.js'

export const Tx = {
  create : createTx,
  encode : encodeTx,
  decode : decodeTx,
  fmt    : TxFmt,
  util   : {
    getTxSize,
    getTxid,
    readScriptPubKey,
    readWitness
  }
}


================================================
FILE: src/lib/tx/parse.ts
================================================
import { Buff }     from '@cmdcode/buff-utils'
import { isHex }    from '../check.js'
import { Script }   from '../script/index.js'
import { encodeTx } from './encode.js'
import { TxFmt }    from './format.js'

import {
  Bytes,
  OutputType,
  ScriptData,
  ScriptPubKeyData,
  TxData,
  WitnessData
} from '../../schema/types.js'

import { hash256 } from '@cmdcode/crypto-utils'

interface TxSizeData {
  size   : number
  bsize  : number
  vsize  : number
  weight : number
}

const OUTPUT_TYPES : Array<[ string, RegExp ]> = [
  [ 'p2pkh',   /^76a914(?<hash>\w{40})88ac$/ ],
  [ 'p2sh',    /^a914(?<hash>\w{40})87$/     ],
  [ 'p2w-pkh', /^0014(?<hash>\w{40})$/       ],
  [ 'p2w-sh',  /^0020(?<hash>\w{64})$/       ],
  [ 'p2tr',    /^5120(?<hash>\w{64})$/       ]
]

const LEAF_VERSIONS = [
  0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce,
  0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
  0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee,
  0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
  0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc,
  0xbe
]

function parseAnnex (
  data : ScriptData[]
) : Buff | null {
  let item = data.at(-1)

  if (isHex(item)) {
    item = Buff.hex(item)
  }

  if (
    data.length > 1            &&
    item instanceof Uint8Array &&
    item[0] === 0x50
  ) {
    data.pop()
    return Buff.raw(item)
  }

  return null
}

function parseBlock (
  data : ScriptData[]
) : Buff | null {
  let item = data.at(-1)

  if (isHex(item)) {
    item = Buff.hex(item)
  }

  if (
    data.length > 1            &&
    item instanceof Uint8Array &&
    item.length > 32           &&
    LEAF_VERSIONS.includes(item[0] & 0xfe)
  ) {
    data.pop()
    return Buff.raw(item)
  }

  return null
}

function parseWitScript (
  data : ScriptData[]
) : Buff | null {
  if (data.length > 1) {
    const item = data.at(-1)
    try {
      const script = Script.fmt.toBytes(item)
      data.pop()
      return script
    } catch (err) {
      return null
    }
  }
  return null
}

function parseParams (
  data : ScriptData[]
) : Bytes[] {
  const params : Bytes[] = []
  for (const d of data) {
    if (isHex(d)            ||
    d instanceof Uint8Array || 
    typeof d === 'number'
  ) {
      params.push(Buff.bytes(d))
    } else {
      throw new Error('unrecognized value: ' + String(d))
    }
  }
  return params
}

export function readWitness (
  data : ScriptData[] = []
) : WitnessData {
  const items  = [ ...data ]
  const annex  = parseAnnex(items)
  const cblock = parseBlock(items)
  const script = parseWitScript(items)
  const params = parseParams(items)
  return { annex, cblock, script, params }
}

export function readScriptPubKey (
  script : ScriptData
) : ScriptPubKeyData {
  const hex = Script.fmt.toBytes(script, false).hex
  for (const [ keytype, pattern ] of OUTPUT_TYPES) {
    const type = keytype as OutputType
    const { groups } = pattern.exec(hex) ?? {}
    const { hash   } = groups ?? {}
    if (isHex(hash)) {
      return { type, data: Buff.hex(hash) }
    }
  }
  return { type: 'raw', data: Buff.hex(hex) }
}

export function getTxid (txdata : TxData | Bytes) : string {
  const json = TxFmt.toJson(txdata)
  const data = encodeTx(json, true)
  return hash256(data).reverse().hex
}

export function getTxSize (txdata : TxData | Bytes) : TxSizeData {
  const json   = TxFmt.toJson(txdata)
  const bsize  = encodeTx(json, true).length
  const fsize  = encodeTx(json, false).length
  const weight = bsize * 3 + fsize
  const remain = (weight % 4 > 0) ? 1 : 0
  const vsize  = Math.floor(weight / 4) + remain
  return { size: fsize, bsize, vsize, weight }
}


================================================
FILE: src/lib/utils.ts
================================================
import { Buff, Bytes } from '@cmdcode/buff-utils'

export function checkSize (input : Bytes, size : number) : void {
  const bytes = Buff.bytes(input)
  if (bytes.length !== size) {
    throw new Error(`Invalid input size: ${bytes.hex} !== ${size}`)
  }
}

export function safeThrow (
  errorMsg    : string,
  shouldThrow : boolean
) : boolean {
  if (shouldThrow) {
    throw new Error(errorMsg)
  } else { return false }
}

export function hashTag (
  tag : string,
  ...data : Bytes[]
) : Buff {
  const htag = Buff.str(tag).digest.raw
  const buff = data.map(e => Buff.bytes(e))
  return Buff.join([ htag, htag, Buff.join(buff) ]).digest
}


================================================
FILE: src/schema/check.ts
================================================
import { z } from 'zod'

const hexstr  = z.string().regex(/^[a-fA-F0-9]$/)
const hash    = z.string().regex(/^[a-fA-F0-9]{64}$/)
const uint32  = z.number().min(0).max(0xFFFFFFFF)
const uint64  = z.bigint()
const byteArr = z.instanceof(Uint8Array)
const asmcode = z.union([ hexstr, uint32, z.string(), byteArr ]).array()
const script  = z.union([ asmcode, hexstr, byteArr ])
const witness = z.array(script)

const TxOutput = z.object({
  value        : z.union([ uint32, uint64 ]),
  scriptPubKey : script
})

const TxInput = z.object({
  txid      : hash,
  vout      : uint32,
  scriptSig : script,
  sequence  : uint32,
  prevout   : TxOutput.optional(),
  witness
})

const TxData = z.object({
  version  : uint32,
  vin      : z.array(TxInput),
  vout     : z.array(TxOutput),
  locktime : uint32
})

export const Schema = {
  TxData,
  TxInput,
  TxOutput,
  witness,
  script,
  hexstr,
  hash,
  uint32,
  uint64
}


================================================
FILE: src/schema/types.ts
================================================
import { Buff } from '@cmdcode/buff-utils'

export type Networks   = 'main' | 'testnet' | 'signet' | 'regtest'

export type InputType  = 'p2pkh'   | 'p2sh'   | 'p2w-p2pkh' | 'p2w-p2sh' |
                         'p2w-pkh' | 'p2w-sh' | 'p2tr' | 'raw'

export type OutputType = 'p2pkh'  | 'p2sh'  | 'p2w-pkh' | 'p2w-sh' | 'p2tr' | 'raw'

export interface TxTemplate {
  version  ?: number
  vin      ?: Array<{
    txid : string
    vout : number
    scriptSig ?: ScriptData
    sequence  ?: SequenceData
    witness   ?: ScriptData[]
    prevout   ?: OutputData
  }>
  vout ?: Array<{
    value        ?: ValueData
    scriptPubKey ?: ScriptData
  }>
  locktime ?: LockData
}

export interface TxData {
  version  : number
  vin      : InputData[]
  vout     : OutputData[]
  locktime : LockData
}

export interface InputData {
  txid : string
  vout : number
  scriptSig : ScriptData
  sequence  : SequenceData
  witness   : ScriptData[]
  prevout  ?: OutputData
}

export interface OutputData {
  value        : ValueData
  scriptPubKey : ScriptData
}

export interface ScriptPubKeyData {
  type : OutputType
  data : Buff
}

export interface WitnessData {
  annex  : Buff | null
  cblock : Buff | null
  script : Buff | null
  params : Bytes[]
}

export type SequenceData = string | number
export type LockData     = string | number
export type ValueData    = number | bigint
export type ScriptData   = Bytes  | Word[]
export type Bytes        = string | Uint8Array
export type Word         = string | number | Uint8Array


================================================
FILE: test/bin/bitcoind
================================================
[File too large to display: 14.6 MB]

================================================
FILE: test/bitcoin.conf
================================================


================================================
FILE: test/core.ts
================================================
import { CoreClient, CoreConfig, CoreDaemon } from '@cmdcode/core-cmd'

const DEFAULT_CONFIG = {
  core_params : [ '-txindex' ],
  corepath    : 'test/bin/bitcoind',
  clipath     : 'test/bin/bitcoin-cli',
  confpath    : 'test/bitcoin.conf',
  datapath    : 'test/data',
  network     : 'regtest',
  isolated    : true,
  debug       : false,
  verbose     : false
}

let daemon : CoreDaemon | null = null

export function get_client (
  config : Partial<CoreConfig> = DEFAULT_CONFIG
) : Promise<CoreClient> {
  if (daemon === null) {
    daemon = new CoreDaemon(config)
  }
  return daemon.startup()
}


================================================
FILE: test/example/ex_test.ts
================================================
import { Test } from 'tape'
import { key_spend }    from './taproot/keyspend.test.js'
import { script_spend } from './taproot/tapscript.test.js'
import { tree_spend }   from './taproot/taptree.test.js'
import { inscription }  from './taproot/inscribe.test.js'
import { get_client }   from '../core.js'

const client = await get_client()
const wallet = await client.load_wallet('test')

await wallet.ensure_funds(1_000_000)

export default async function example_tests (t : Test) : Promise<void> {
  t.test('Example Tests', async t => {
    await key_spend(t, wallet)
    await script_spend(t, wallet)
    await tree_spend(t, wallet)
    await inscription(t, wallet)
  })

  t.teardown(() => client.core.shutdown())
}


================================================
FILE: test/example/taproot/inscribe.test.ts
================================================
import { Test }       from 'tape'
import { CoreWallet } from '@cmdcode/core-cmd'
import { Buff }       from '@cmdcode/buff-utils'
import { util }       from '@cmdcode/crypto-utils'

import { Address, Signer, Tap, Tx, } from '../../../src/index.js'

import fs      from 'fs/promises'
import { URL } from 'url'

export async function inscription (t : Test, wallet : CoreWallet) : Promise<void> {
  t.test('Example of an inscription transaction.', async t => {
    t.plan(2)

    // Switch this to true to enable console output.
    const VERBOSE = false

    /** 
    * The code marked below is a quick example of how to load an image 
    * within a NodeJS environment. It may not work in other environments.
    *
    * For examples of how to convert images into binary from within a browser
    * environment, please check out the Web File API:
    * https://developer.mozilla.org/en-US/docs/Web/API/File 
    */
    const imgpath = new URL('./image.png', import.meta.url).pathname
    const imgdata = await fs.readFile(imgpath).then(e => new Uint8Array(e))

    /**
     * Specify the "marker" bytes. Ord uses this to recognize your script
     * as an inscription.
     */
    const marker = Buff.encode('ord')

    /**
     * Specify the media type of the file. Applications use this when rendering 
     * content. See: https://developer.mozilla.org/en-US/docs/Glossary/MIME_type 
     */
    const mimetype = Buff.encode('image/png')

    /**
     * Specify a secret key to use for signing.
     */
    const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
    const seckey = util.getSecretKey(secret)
    const pubkey = util.getPublicKey(seckey, true)


    // Basic format of an 'inscription' script.
    const script = [ pubkey, 'OP_CHECKSIG', 'OP_0', 'OP_IF', marker, '01', mimetype, 'OP_0', imgdata, 'OP_ENDIF' ]

    // For tapscript spends, we need to convert this script into a 'tapleaf'.
    const tapleaf = Tap.encodeScript(script)

    // Generate a tapkey that includes our leaf script. Also, create a merlke proof 
    // (cblock) that targets our leaf and proves its inclusion in the tapkey.
    const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { target: tapleaf })

    // A taproot address is simply the tweaked public key, encoded in bech32 format.
    const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')

    if (VERBOSE) console.log('Your address:', address)

    // Generate a utxo for testing (using a local core client).
    const utxo = await wallet.create_utxo(100_000, address)

    const txdata = Tx.create({
      vin  : [{
        // Use the txid of the funding transaction used to send the sats.
        txid    : utxo.txid,
        // Specify the index value of the output that you are going to spend from.
        vout    : utxo.vout,
        // Also include the value and script of that ouput.
        prevout : utxo.prevout
      }],
      vout : [{
        // We are leaving behind 10_000 sats as a fee to the miners.
        value: 90_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 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: tapleaf })

    // 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 = Signer.taproot.verify(txdata, 0, { pubkey, throws: true })

    t.equal(isValid, true, 'Transaction should pass validation.')

    // Encode the final transaction as hex.
    const txhex = Tx.encode(txdata)
    
    // Publish the transaction using a local bitcoin core client.
    const txid  = await wallet.client.publish_tx(txhex, true)

    t.pass('Transaction broadcast with txid: ' + txid)

    if (VERBOSE) {
      console.log('Your txhex:', Tx.encode(txdata).hex)
      console.dir(txdata, { depth: null })
    }
  })
}


================================================
FILE: test/example/taproot/keyspend.test.ts
================================================

import { Test }       from 'tape'
import { util }       from '@cmdcode/crypto-utils'
import { CoreWallet } from '@cmdcode/core-cmd'

import { Address, Signer, Tap, Tx, } from '../../../src/index.js'

export async function key_spend (t : Test, wallet : CoreWallet) : Promise<void> {
  t.test('Basic spend using key-path.', async t => {
    t.plan(2)
    
    // Switch this to true to enable console output.
    const VERBOSE = false

    // Create a keypair to use for testing.
    const secret = 'ccd54b99acec77d0537b01431579baef998efac6b08e9564bc3047b20ec1bb4c'
    const seckey = util.getSecretKey(secret)
    const pubkey = util.getPublicKey(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)

    // Optional: You could also derive the public key from the tweaked secret key.
    const _tpubkey_example = util.getPublicKey(tseckey, true).hex

    // A taproot address is simply the tweaked public key, encoded in bech32 format.
    const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')

    if (VERBOSE) console.log('Your address:', address)

    // Generate a utxo for testing (using a local core client).
    const utxo = await wallet.create_utxo(100_000, address)

    const txdata = Tx.create({
      vin  : [{
        // Use the txid of the funding transaction used to send the sats.
        txid: utxo.txid,
        // Specify the index value of the output that you are going to spend from.
        vout: utxo.vout,
        // Also include the value and script of that ouput.
        prevout: utxo.prevout,
      }],
      vout : [{
        // We are leaving behind 10_000 sats as a fee to the miners.
        value: 90_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 = Signer.taproot.verify(txdata, 0)

    t.equal(isValid, true, 'Transaction should pass validation.')

    // Encode the final transaction as hex.
    const txhex = Tx.encode(txdata)
    
    // Publish the transaction using a local bitcoin core client.
    const txid  = await wallet.client.publish_tx(txhex, true)

    t.pass('Transaction broadcast with txid: ' + txid)

    if (VERBOSE) {
      console.log('Your txhex:', Tx.encode(txdata).hex)
      console.dir(txdata, { depth: null })
    }
  })
}


================================================
FILE: test/example/taproot/tapscript.test.ts
================================================
import { Test }       from 'tape'
import { util }       from '@cmdcode/crypto-utils'
import { CoreWallet } from '@cmdcode/core-cmd'

import { Address, Script, Signer, Tap, Tx, } from '../../../src/index.js'

export async function script_spend (t : Test, wallet : CoreWallet) : Promise<void> {
  t.test('Basic spend using tapscript.', async t => {
    t.plan(2)

    // Switch this to true to enable console output.
    const VERBOSE = false

    // Create a keypair to use for testing.
    const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
    const seckey = util.getSecretKey(secret)
    const pubkey = util.getPublicKey(seckey, true)

    // Specify a basic script to use for testing.
    const script = [ pubkey, 'OP_CHECKSIG' ]
    const sbytes = Script.encode(script)

    // For tapscript spends, we need to convert this script into a 'tapleaf'.
    const tapleaf = Tap.tree.getLeaf(sbytes)

    // Generate a tapkey that includes our leaf script. Also, create a merlke proof 
    // (cblock) that targets our leaf and proves its inclusion in the tapkey.
    const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { target: tapleaf })

    // A taproot address is simply the tweaked public key, encoded in bech32 format.
    const address = Address.p2tr.fromPubKey(tpubkey, 'regtest')
    if (VERBOSE) console.log('Your address:', address)

    // Generate a utxo for testing (using a local core client).
    const utxo = await wallet.create_utxo(100_000, address)

    const txdata = Tx.create({
      vin  : [{
        // Use the txid of the funding transaction used to send the sats.
        txid    : utxo.txid,
        // Specify the index value of the output that you are going to spend from.
        vout    : utxo.vout,
        // Also include the value and script of that ouput.
        prevout : utxo.prevout
      }],
      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 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: tapleaf })

    // 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.hex, 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 = Signer.taproot.verify(txdata, 0, { pubkey })

    t.equal(isValid, true, 'Transaction should pass validation.')

    // Encode the final transaction as hex.
    const txhex = Tx.encode(txdata)
    
    // Publish the transaction using a local bitcoin core client.
    const txid  = await wallet.client.publish_tx(txhex, true)

    t.pass('Transaction broadcast with txid: ' + txid)

    if (VERBOSE) {
      console.log('Your txhex:', Tx.encode(txdata).hex)
      console.dir(txdata, { depth: null })
    }
  })
}


================================================
FILE: test/example/taproot/taptree.test.ts
================================================
import { Test }       from 'tape'
import { util }       from '@cmdcode/crypto-utils'
import { CoreWallet } from '@cmdcode/core-cmd'

import { Address, Signer, Tap, Tx, } from '../../../src/index.js'

export async function tree_spend (t : Test, wallet : CoreWallet) : Promise<void> {
  t.test('Spend a script inside a tree.', async t => {
    // Switch this to true to enable console output.
    const VERBOSE = false

    // Create a keypair to use for testing.
    const secret = '0a7d01d1c2e1592a02ea7671bb79ecd31d8d5e660b008f4b10e67787f4f24712'
    const seckey = util.getSecretKey(secret)
    const pubkey = util.getPublicKey(seckey, true)

    // Specify an array of scripts to use for testing.
    const scripts = [
      [ 1, 7, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
      [ 2, 6, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
      [ 3, 5, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
      [ 4, 4, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
      [ 5, 3, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
      [ 6, 2, 'OP_ADD', 8, 'OP_EQUALVERIFY', pubkey, 'OP_CHECKSIG' ],
      [ 7, 1, 'OP_ADD', 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  = Math.floor(Math.random() * 10) % 7
    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, 'regtest')

    if (VERBOSE) console.log('Your address:', address)

    // Generate a utxo for testing (using a local core client).
    const utxo = await wallet.create_utxo(100_000, address)

    const txdata = Tx.create({
      vin  : [{
        // Use the txid of the funding transaction used to send the sats.
        txid    : utxo.txid,
        // Specify the index value of the output that you are going to spend from.
        vout    : utxo.vout,
        // Also include the value and script of that ouput.
        prevout : utxo.prevout
      }],
      vout : [{
        // We are leaving behind 10_000 sats as a fee to the miners.
        value: 90_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 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.hex, 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 })

    t.equal(isValid, true, 'Transaction should pass validation.')

    // Encode the final transaction as hex.
    const txhex = Tx.encode(txdata)
    
    // Publish the transaction using a local bitcoin core client.
    const txid  = await wallet.client.publish_tx(txhex, true)

    t.pass('Transaction broadcast with txid: ' + txid)

    if (VERBOSE) {
      console.log('Your txhex:', Tx.encode(txdata).hex)
      console.dir(txdata, { depth: null })
    }
  })
}


================================================
FILE: test/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Browser Scratch</title>
</head>
<body>
  <script src="../dist/bundle.min.js"></script>
  <script type="module">
    const { Tx } = window.tapscript

    const testTx = Tx.create({
      version: 2,
      vin: [
        {
          txid: '1b4135af95b2728fcf6c77df66e83c7dea903d83856b7cb770983bfad8c32719',
          vout: 0,
          witness: [
            '30440220084d33f3184626e745ded7792ef265230465d90308698a3ea858579428c0ac1f0220528224177f1bba21ddc83716c11d3a04ae28c0cef4f92649ccf9f88775ebe9c401',
            '03c1f09640d7ac03330876ecda7bcf30afa46431898b0e55d037c01d3bd03ec3da',
            '76a914c021f50982f8901e92ea7f92b3a3d546568fb9e788ac'
          ]
        },
        {
          txid: '1b4135af95b2728fcf6c77df66e83c7dea903d83856b7cb770983bfad8c32719',
          vout: 1,
          witness: [
            '30440220084d33f3184626e745ded7792ef265230465d90308698a3ea858579428c0ac1f0220528224177f1bba21ddc83716c11d3a04ae28c0cef4f92649ccf9f88775ebe9c401',
            '03c1f09640d7ac03330876ecda7bcf30afa46431898b0e55d037c01d3bd03ec3da',
            '76a9149094a9bb2e62972048368ed8a6770f5c8516a0f688ac'
          ]
        }
      ],
      vout: [
        {
          value: 1_250_000_000,
          scriptPubkey: [0, '9094a9bb2e62972048368ed8a6770f5c8516a0f6']
        },
        {
          value: 1_249_999_000,
          scriptPubkey: [0, 'c021f50982f8901e92ea7f92b3a3d546568fb9e7']
        }
      ],
      locktime: 0
    })

    const encoded = Tx.encode(testTx)

    console.log(encoded.hex)

    const decoded = Tx.decode(encoded)

    console.log(decoded)
  </script>
</body>
</html>

================================================
FILE: test/scratch.ts
================================================
import fs       from 'fs/promises'
import { URL }  from 'url'
import { Script, Tx }   from '../src/index.js'

const txtpath = new URL('./txhex.txt', import.meta.url).pathname
const txtdata = await fs.readFile(txtpath) //.then(e => new Uint8Array(e))

const scriptAsHexString = '4e21032cf515e2ae74b6d639c4e91a0b2a7047a0178e628d167989af46d1da474a6951ad21022d6b369d9a95568203b2a51eac49cc8b20ab81930e2d2565df5f0bc8e3bf59d5ac73640380ca00b268'

const script = Script.decode(scriptAsHexString, true)

console.log(script)

//const tx1 = Tx.decode(txtdata.toString())
//console.dir(tx1, { depth : null })


================================================
FILE: test/src/addr/addr.test.ts
================================================
import { Test }        from 'tape'
import { p2pkh_test }  from './p2pkh.test.js'
import { p2sh_test }   from './p2sh.test.js'
import { p2wpkh_test } from './p2wpkh.test.js'
import { p2wsh_test }  from './p2wsh.test.js'
import { p2tr_test }   from './p2tr.test.js'

export default function address_tests(t : Test) {
  p2pkh_test(t)
  p2sh_test(t)
  p2wpkh_test(t)
  p2wsh_test(t)
  p2tr_test(t)
}

================================================
FILE: test/src/addr/p2pkh.test.ts
================================================

import { Test }    from 'tape'
import { Buff }    from '@cmdcode/buff-utils'
import { Address } from '../../../src/index.js'

export function p2pkh_test(t : Test) : void {

  const ref_pubkey  = '037191e9be308354c79d9e0d596e74fce4a98768459a846a073799ad20b4c78770'
  const ref_address = 'msi862KMaLR3jHcdKtAh9QMN2sS8Qcyywy'
  const ref_hexdata = '76a91485be4269276fd45d0b6f7ee963dd073b202d49ed88ac'
  const ref_hash    = '85be4269276fd45d0b6f7ee963dd073b202d49ed'
  const ref_script  = [ 'OP_DUP', 'OP_HASH160', ref_hash, 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]
  const ref_object  = { prefix: 'm', type: 'p2pkh', network: 'testnet', data: Buff.hex(ref_hash) , script: ref_script }

  t.test('P2PKH unit test', t => {
    t.plan(7)

    const addr1 = Address.p2pkh.fromPubKey(ref_pubkey, 'regtest')
    t.equal(addr1, ref_address, 'Pubkey should encode into proper address.')

    const addr2 = Address.p2pkh.encode(ref_hash, 'regtest')
    t.equal(addr2, ref_address, 'Hash should encode into proper address')

    const bytes = Address.p2pkh.decode(ref_address, 'regtest')
    t.equal(bytes.hex, ref_hash, 'Address should decode into proper hash.')

    const asm = Address.p2pkh.scriptPubKey(ref_hash)
    t.deepEqual(asm, ref_script, 'scriptPubKey should match reference script.')

    const data = Address.decode(ref_address)
    t.deepEqual(data, ref_object, 'Address should produce proper AddressData')

    const script = Address.toScriptPubKey(ref_address)
    t.deepEqual(script, ref_script, 'Address should produce proper scriptPubKey.')

    const addr3 = Address.fromScriptPubKey(ref_script, 'regtest')
    t.equal(addr3, ref_address, 'scriptPubKey should produce proper address.')
  })
}

================================================
FILE: test/src/addr/p2sh.test.ts
================================================

import { Test }    from 'tape'
import { Buff }    from '@cmdcode/buff-utils'
import { Address } from '../../../src/index.js'

export function p2sh_test(t : Test) : void {

  const ref_preimg  = '001494d325b4767d23020cec68a9ca75b8fe9264b7af'
  const ref_address = '2NFbT9Fkp7yjp22dvu7tHgikd8Yfy87KnTc'
  const ref_hexdata = 'a914f52611446bdfa1f67da1fb7805dbee74c6d92a5487'
  const ref_hash    = 'f52611446bdfa1f67da1fb7805dbee74c6d92a54'
  const ref_script  = [ 'OP_HASH160', ref_hash, 'OP_EQUAL' ]
  const ref_object  = { prefix: '2', type: 'p2sh', network: 'testnet', data: Buff.hex(ref_hash) , script: ref_script }

  t.test('P2SH unit test', t => {
    t.plan(7)

    const addr1 = Address.p2sh.fromScript(ref_preimg, 'regtest')
    t.equal(addr1, ref_address, 'Script should encode into proper address.')

    const addr2 = Address.p2sh.encode(ref_hash, 'regtest')
    t.equal(addr2, ref_address, 'Hash should encode into proper address')

    const bytes = Address.p2sh.decode(ref_address, 'regtest')
    t.equal(bytes.hex, ref_hash, 'Address should decode into proper hash.')

    const asm = Address.p2sh.scriptPubKey(ref_hash)
    t.deepEqual(asm, ref_script, 'scriptPubKey should match reference script.')

    const data = Address.decode(ref_address)
    t.deepEqual(data, ref_object, 'Address should produce proper AddressData')

    const script = Address.toScriptPubKey(ref_address)
    t.deepEqual(script, ref_script, 'Address should produce proper scriptPubKey.')

    const addr3 = Address.fromScriptPubKey(ref_script, 'regtest')
    t.equal(addr3, ref_address, 'scriptPubKey should produce proper address.')
  })
}

================================================
FILE: test/src/addr/p2tr.test.ts
================================================

import { Test }    from 'tape'
import { Buff }    from '@cmdcode/buff-utils'
import { Address } from '../../../src/index.js'

export function p2tr_test(t : Test) : void {

  const ref_pubkey  = '91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605'
  const ref_address = 'bcrt1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczsqzdc9v'
  const ref_hexdata = '512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605'
  const ref_script  = [ 'OP_1', ref_pubkey ]
  const ref_object  = { prefix: 'bcrt1p', type: 'p2tr', network: 'regtest', data: Buff.hex(ref_pubkey), script: ref_script }

  t.test('P2TR unit test', t => {
    t.plan(7)

    const addr1 = Address.p2tr.fromPubKey(ref_pubkey, 'regtest')
    t.equal(addr1, ref_address, 'Pubkey should encode into proper address.')

    const addr2 = Address.p2tr.encode(ref_pubkey, 'regtest')
    t.equal(addr2, ref_address, 'Pubkey should encode into proper address')

    const bytes = Address.p2tr.decode(ref_address)
    t.equal(bytes.hex, ref_pubkey, 'Address should decode into proper pubkey.')

    const asm = Address.p2tr.scriptPubKey(ref_pubkey)
    t.deepEqual(asm, ref_script, 'scriptPubKey should match reference script.')

    const data = Address.decode(ref_address)
    t.deepEqual(data, ref_object, 'Address should produce proper AddressData')

    const script = Address.toScriptPubKey(ref_address)
    t.deepEqual(script, ref_script, 'Address should produce proper scriptPubKey.')

    const addr3 = Address.fromScriptPubKey(ref_script, 'regtest')
    t.equal(addr3, ref_address, 'scriptPubKey should produce proper address.')
  })
}

================================================
FILE: test/src/addr/p2wpkh.test.ts
================================================

import { Test }    from 'tape'
import { Buff }    from '@cmdcode/buff-utils'
import { Address } from '../../../src/index.js'

export function p2wpkh_test(t : Test) : void {

  const ref_pubkey  = '03d5af2a3e89cb72ff9ca1b36091ca46e4d4399abc5574b13d3e56bca6c0784679'
  const ref_address = 'bcrt1q738hdjlatdx9xmg3679kwq9cwd7fa2c84my9zk'
  const ref_hexdata = '0014f44f76cbfd5b4c536d11d78b6700b8737c9eab07'
  const ref_hash    = 'f44f76cbfd5b4c536d11d78b6700b8737c9eab07'
  const ref_script  = [ 'OP_0', ref_hash ]
  const ref_object  = { prefix: 'bcrt1q', type: 'p2w-pkh', network: 'regtest', data: Buff.hex(ref_hash), script: ref_script }

  t.test('P2WPKH unit test', t => {
    t.plan(7)

    const addr1 = Address.p2wpkh.fromPubKey(ref_pubkey, 'regtest')
    t.equal(addr1, ref_address, 'Pubkey should encode into proper address.')

    const addr2 = Address.p2wpkh.encode(ref_hash, 'regtest')
    t.equal(addr2, ref_address, 'Hash should encode into proper address')

    const bytes = Address.p2wpkh.decode(addr1)
    t.equal(bytes.hex, ref_hash, 'Address should decode into proper hash.')

    const asm = Address.p2wpkh.scriptPubKey(bytes)
    t.deepEqual(asm, ref_script, 'scriptPubKey should match reference script.')

    const data = Address.decode(ref_address)
    t.deepEqual(data, ref_object, 'Address should produce proper AddressData')

    const script = Address.toScriptPubKey(ref_address)
    t.deepEqual(script, ref_script, 'Address should produce proper scriptPubKey.')

    const addr3 = Address.fromScriptPubKey(ref_script, 'regtest')
    t.equal(addr3, ref_address, 'scriptPubKey should produce proper address.')
  })
}

================================================
FILE: test/src/addr/p2wsh.test.ts
================================================

import { Test }    from 'tape'
import { Buff }    from '@cmdcode/buff-utils'
import { Address } from '../../../src/index.js'

export function p2wsh_test(t : Test) : void {

  const ref_preimg  = [ 1, 2, 'OP_ADD', 3, 'OP_EQUAL' ]
  const ref_address = 'bcrt1qetz4my584ckcqd0acdm7h788lkmslz44q5wc0rd3eknmmzc85sjq9sle8n'
  const ref_hexdata = '0020cac55d9287ae2d8035fdc377ebf8e7fdb70f8ab5051d878db1cda7bd8b07a424'
  const ref_hash    = 'cac55d9287ae2d8035fdc377ebf8e7fdb70f8ab5051d878db1cda7bd8b07a424'
  const ref_script  = [ 'OP_0', ref_hash, ]
  const ref_object  = { prefix: 'bcrt1q', type: 'p2w-sh', network: 'regtest', data: Buff.hex(ref_hash) , script: ref_script }

  t.test('P2WSH unit test', t => {
    t.plan(7)

    const addr1 = Address.p2wsh.fromScript(ref_preimg, 'regtest')
    t.equal(addr1, ref_address, 'Script should encode into proper address.')

    const addr2 = Address.p2wsh.encode(ref_hash, 'regtest')
    t.equal(addr2, ref_address, 'Hash should encode into proper address')

    const bytes = Address.p2wsh.decode(ref_address)
    t.equal(bytes.hex, ref_hash, 'Address should decode into proper hash.')

    const asm = Address.p2wsh.scriptPubKey(ref_hash)
    t.deepEqual(asm, ref_script, 'scriptPubKey should match reference script.')

    const data = Address.decode(ref_address)
    t.deepEqual(data, ref_object, 'Address should produce proper AddressData')

    const script = Address.toScriptPubKey(ref_address)
    t.deepEqual(script, ref_script, 'Address should produce proper scriptPubKey.')

    const addr3 = Address.fromScriptPubKey(ref_script, 'regtest')
    t.equal(addr3, ref_address, 'scriptPubKey should produce proper address.')
  })
}

================================================
FILE: test/src/sig/segwit/bip0143.vectors.json
================================================
{
  "rawhex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000",
  "txdata": {
    "version": 1,
    "vin": [
      {
        "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436",
        "vout": 1,
        "scriptSig": "220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54",
        "prevout" : {
          "value": 987654321,
          "scriptPubKey": "a9149993a429037b5d912407a71c252019287b8d27a587"
        },
        "sequence": "ffffffff",
        "witness": []
      }
    ],
    "vout": [
      {
        "value": 900000000,
        "scriptPubKey": "76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac"
      },
      {
        "value": 87000000,
        "scriptPubKey": "76a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac"
      }
    ],
    "locktime": 0
  },
  "hash_vectors": {
    "prevouts": {
      "preimage": "36641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000",
      "digest": "74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0"
    },
    "sequence": {
      "preimage": "ffffffff",
      "digest": "3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044"
    },
    "output_all": {
      "preimage": "00e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac",
      "digest": "bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc"
    },
    "output_single": {
      "preimage": "00e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac",
      "digest": "9efe0c13a6b16c14a41b04ebe6a63f419bdacb2f8705b494a43063ca3cd4f708"
    }
  },
  "redeemScript": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae",
  "sign_vectors": [
    {
      "label"     : "ALL",
      "hashType"  : "01000000",
      "preimage"  : "0100000074afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa03bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504436641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56aeb168de3a00000000ffffffffbc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc0000000001000000",
      "sigHash"   : "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c",
      "pubkey"    : "0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3",
      "seckey"    : "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6",
      "signature" : "304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01"
    },
    {
      "label"     : "NONE",
      "hashType"  : "02000000",
      "preimage"  : "0100000074afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0000000000000000000000000000000000000000000000000000000000000000036641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56aeb168de3a00000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000002000000",
      "sigHash"   : "e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36",
      "pubkey"    : "03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b",
      "seckey"    : "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3",
      "signature" : "3044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502"
    },
    {
      "label"     : "SINGLE",
      "hashType"  : "03000000",
      "preimage"  : "0100000074afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0000000000000000000000000000000000000000000000000000000000000000036641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56aeb168de3a00000000ffffffff9efe0c13a6b16c14a41b04ebe6a63f419bdacb2f8705b494a43063ca3cd4f7080000000003000000",
      "sigHash"   : "1e1f1c303dc025bd664acb72e583e933fae4cff9148bf78c157d1e8f78530aea",
      "pubkey"    : "034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a",
      "seckey"    : "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661",
      "signature" : "3044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403"
    },
    {
      "label"     : "ALL|ANYONECANPAY",
      "hashType"  : "81000000",
      "preimage"  : "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56aeb168de3a00000000ffffffffbc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc0000000081000000",
      "sigHash"   : "2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e",
      "pubkey"    : "033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4",
      "seckey"    : "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49",
      "signature" : "3045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381"
    },
    {
      "label"     : "NONE|ANYONECANPAY",
      "hashType"  : "82000000",
      "preimage"  : "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56aeb168de3a00000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000082000000",
      "sigHash"   : "781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a",
      "pubkey"    : "03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16",
      "seckey"    : "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323",
      "signature" : "3045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a0882"
    },
    {
      "label"     : "SINGLE|ANYONECANPAY",
      "hashType"  : "83000000",
      "preimage"  : "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56aeb168de3a00000000ffffffff9efe0c13a6b16c14a41b04ebe6a63f419bdacb2f8705b494a43063ca3cd4f7080000000083000000",
      "sigHash"   : "511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b",
      "pubkey"    : "02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b",
      "seckey"    : "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890",
      "signature" : "30440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783"
    }
  ],
  "signedtx": "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"
}

================================================
FILE: test/src/sig/segwit/sighash.test.ts
================================================
import { Test }    from 'tape'
import { Buff }    from '@cmdcode/buff-utils'
import { secp256k1 as secp } from '@noble/curves/secp256k1'
import { Signer, TxData }    from '../../../../src/index.js'
import test_data  from './bip0143.vectors.json' assert { type: 'json' }

export async function sighash_vector_test(t :Test) : Promise<void> {
  t.test('Testing segwit sighash vectors.', async t => {
    const { redeemScript, txdata, sign_vectors } = test_data
    t.plan(sign_vectors.length * 4)
    for (const vector of sign_vectors) {
      const { label, hashType, sigHash, } = vector
      const { pubkey, seckey, signature } = vector
      const sigflag = Buff.hex(hashType, 4).reverse().num
      const config  = { sigflag, pubkey, script: redeemScript, throws: true }
      const index   = 0

      t.comment(`Testing ${label}:`)

      // console.log('seckey:', seckey)
      // console.log('pubkey:', pubkey)
      // console.log('hash:', sigHash)
      // console.log('signature:', signature)
    
      try {
        const hash = Signer.segwit.hash(txdata, index, config)
        t.equal(hash.hex, sigHash, 'Sighash should be equal.')
      } catch (err) {
        t.fail(err.message)
      }

      try {
        const txcopy = { ...txdata } as TxData
        const sig = Signer.segwit.sign(seckey, txcopy, index, config)
        t.equal(sig.hex, signature, 'Signatures should be equal.')
        const nobleVerify = secp.verify(sig.slice(0, -1).hex, sigHash, pubkey)
        t.equal(nobleVerify, true, 'Signature should be valid using Noble.')
        txcopy.vin[index].witness = [ sig, pubkey, redeemScript ]
        const signerVerify = Signer.segwit.verify(txcopy, index, config)
        t.equal(signerVerify, true, 'Signature should be valid using Signer.')
      } catch (err) {
        t.fail(err.message)
      }
    }
  })
}


================================================
FILE: test/src/sig/segwit/sighash.vectors.json
================================================
[
	["907c2bc503ade11cc3b04eb2918b6f547b0630ab569273824748c87ea14b0696526c66ba740200000004ab65ababfd1f9bdd4ef073c7afc4ae00da8a66f429c917a0081ad1e1dabce28d373eab81d8628de802000000096aab5253ab52000052ad042b5f25efb33beec9f3364e8a9139e8439d9d7e26529c3c30b6c3fd89f8684cfd68ea0200000009ab53526500636a52ab599ac2fe02a526ed040000000008535300516352515164370e010000000003006300ab2ec229", "", 2, 1864164639, "31af167a6cf3f9d5f6875caa4d31704ceb0eba078d132b78dab52c3b8997317e"],
	["a0aa3126041621a6dea5b800141aa696daf28408959dfb2df96095db9fa425ad3f427f2f6103000000015360290e9c6063fa26912c2e7fb6a0ad80f1c5fea1771d42f12976092e7a85a4229fdb6e890000000001abc109f6e47688ac0e4682988785744602b8c87228fcef0695085edf19088af1a9db126e93000000000665516aac536affffffff8fe53e0806e12dfd05d67ac68f4768fdbe23fc48ace22a5aa8ba04c96d58e2750300000009ac51abac63ab5153650524aa680455ce7b000000000000499e50030000000008636a00ac526563ac5051ee030000000003abacabd2b6fe000000000003516563910fb6b5", "65", 0, -1391424484, "48d6a1bd2cd9eec54eb866fc71209418a950402b5d7e52363bfb75c98e141175"],
	["6e7e9d4b04ce17afa1e8546b627bb8d89a6a7fefd9d892ec8a192d79c2ceafc01694a6a7e7030000000953ac6a51006353636a33bced1544f797f08ceed02f108da22cd24c9e7809a446c61eb3895914508ac91f07053a01000000055163ab516affffffff11dc54eee8f9e4ff0bcf6b1a1a35b1cd10d63389571375501af7444073bcec3c02000000046aab53514a821f0ce3956e235f71e4c69d91abe1e93fb703bd33039ac567249ed339bf0ba0883ef300000000090063ab65000065ac654bec3cc504bcf499020000000005ab6a52abac64eb060100000000076a6a5351650053bbbc130100000000056a6aab53abd6e1380100000000026a51c4e509b8", "acab655151", 0, 479279909, "2a3d95b09237b72034b23f2d2bb29fa32a58ab5c6aa72f6aafdfa178ab1dd01c"],
	["73107cbd025c22ebc8c3e0a47b2a760739216a528de8d4dab5d45cbeb3051cebae73b01ca10200000007ab6353656a636affffffffe26816dffc670841e6a6c8c61c586da401df1261a330a6c6b3dd9f9a0789bc9e000000000800ac6552ac6aac51ffffffff0174a8f0010000000004ac52515100000000", "5163ac63635151ac", 1, 1190874345, "06e328de263a87b09beabe222a21627a6ea5c7f560030da31610c4611f4a46bc"],
	["e93bbf6902be872933cb987fc26ba0f914fcfc2f6ce555258554dd9939d12032a8536c8802030000000453ac5353eabb6451e074e6fef9de211347d6a45900ea5aaf2636ef7967f565dce66fa451805c5cd10000000003525253ffffffff047dc3e6020000000007516565ac656aabec9eea010000000001633e46e600000000000015080a030000000001ab00000000", "5300ac6a53ab6a", 1, -886562767, "f03aa4fc5f97e826323d0daa03343ebf8a34ed67a1ce18631f8b88e5c992e798"],
	["50818f4c01b464538b1e7e7f5ae4ed96ad23c68c830e78da9a845bc19b5c3b0b20bb82e5e9030000000763526a63655352ffffffff023b3f9c040000000008630051516a6a5163a83caf01000000000553ab65510000000000", "6aac", 0, 946795545, "746306f322de2b4b58ffe7faae83f6a72433c22f88062cdde881d4dd8a5a4e2d"],
	["a93e93440250f97012d466a6cc24839f572def241c814fe6ae94442cf58ea33eb0fdd9bcc1030000000600636a0065acffffffff5dee3a6e7e5ad6310dea3e5b3ddda1a56bf8de7d3b75889fc024b5e233ec10f80300000007ac53635253ab53ffffffff0160468b04000000000800526a5300ac526a00000000", "ac00636a53", 1, 1773442520, "5c9d3a2ce9365bb72cfabbaa4579c843bb8abf200944612cf8ae4b56a908bcbd"],
	["ce7d371f0476dda8b811d4bf3b64d5f86204725deeaa3937861869d5b2766ea7d17c57e40b0100000003535265ffffffff7e7e9188f76c34a46d0bbe856bde5cb32f089a07a70ea96e15e92abb37e479a10100000006ab6552ab655225bcab06d1c2896709f364b1e372814d842c9c671356a1aa5ca4e060462c65ae55acc02d0000000006abac0063ac5281b33e332f96beebdbc6a379ebe6aea36af115c067461eb99d22ba1afbf59462b59ae0bd0200000004ab635365be15c23801724a1704000000000965006a65ac00000052ca555572", "53ab530051ab", 1, 2030598449, "c336b2f7d3702fbbdeffc014d106c69e3413c7c71e436ba7562d8a7a2871f181"],
	["d3b7421e011f4de0f1cea9ba7458bf3486bee722519efab711a963fa8c100970cf7488b7bb0200000003525352dcd61b300148be5d05000000000000000000", "535251536aac536a", 0, -1960128125, "29aa6d2d752d3310eba20442770ad345b7f6a35f96161ede5f07b33e92053e2a"],
	["04bac8c5033460235919a9c63c42b2db884c7c8f2ed8fcd69ff683a0a2cccd9796346a04050200000003655351fcad3a2c5a7cbadeb4ec7acc9836c3f5c3e776e5c566220f7f965cf194f8ef98efb5e3530200000007526a006552526526a2f55ba5f69699ece76692552b399ba908301907c5763d28a15b08581b23179cb01eac03000000075363ab6a516351073942c2025aa98a05000000000765006aabac65abd7ffa6030000000004516a655200000000", "53ac6365ac526a", 1, 764174870, "bf5fdc314ded2372a0ad078568d76c5064bf2affbde0764c335009e56634481b"],
	["c363a70c01ab174230bbe4afe0c3efa2d7f2feaf179431359adedccf30d1f69efe0c86ed390200000002ab51558648fe0231318b04000000000151662170000000000008ac5300006a63acac00000000", "", 0, 2146479410, "191ab180b0d753763671717d051f138d4866b7cb0d1d4811472e64de595d2c70"],
	["8d437a7304d8772210a923fd81187c425fc28c17a5052571501db05c7e89b11448b36618cd02000000026a6340fec14ad2c9298fde1477f1e8325e5747b61b7e2ff2a549f3d132689560ab6c45dd43c3010000000963ac00ac000051516a447ed907a7efffebeb103988bf5f947fc688aab2c6a7914f48238cf92c337fad4a79348102000000085352ac526a5152517436edf2d80e3ef06725227c970a816b25d0b58d2cd3c187a7af2cea66d6b27ba69bf33a0300000007000063ab526553f3f0d6140386815d030000000003ab6300de138f00000000000900525153515265abac1f87040300000000036aac6500000000", "51", 3, -315779667, "b6632ac53578a741ae8c36d8b69e79f39b89913a2c781cdf1bf47a8c29d997a5"],
	["fd878840031e82fdbe1ad1d745d1185622b0060ac56638290ec4f66b1beef4450817114a2c0000000009516a63ab53650051abffffffff37b7a10322b5418bfd64fb09cd8a27ddf57731aeb1f1f920ffde7cb2dfb6cdb70300000008536a5365ac53515369ecc034f1594690dbe189094dc816d6d57ea75917de764cbf8eccce4632cbabe7e116cd0100000003515352ffffffff035777fc000000000003515200abe9140300000000050063005165bed6d10200000000076300536363ab65195e9110", "635265", 0, 1729787658, "6e3735d37a4b28c45919543aabcb732e7a3e1874db5315abb7cc6b143d62ff10"],
	["f40a750702af06efff3ea68e5d56e42bc41cdb8b6065c98f1221fe04a325a898cb61f3d7ee030000000363acacffffffffb5788174aef79788716f96af779d7959147a0c2e0e5bfb6c2dba2df5b4b97894030000000965510065535163ac6affffffff0445e6fd0200000000096aac536365526a526aa6546b000000000008acab656a6552535141a0fd010000000000c897ea030000000008526500ab526a6a631b39dba3", "00abab5163ac", 1, -1778064747, "d76d0fc0abfa72d646df888bce08db957e627f72962647016eeae5a8412354cf"],
	["a63bc673049c75211aa2c09ecc38e360eaa571435fedd2af1116b5c1fa3d0629c269ecccbf0000000008ac65ab516352ac52ffffffffbf1a76fdda7f451a5f0baff0f9ccd0fe9136444c094bb8c544b1af0fa2774b06010000000463535253ffffffff13d6b7c3ddceef255d680d87181e100864eeb11a5bb6a3528cb0d70d7ee2bbbc02000000056a0052abab951241809623313b198bb520645c15ec96bfcc74a2b0f3db7ad61d455cc32db04afc5cc702000000016309c9ae25014d9473020000000004abab6aac3bb1e803", "", 3, -232881718, "6e48f3da3a4ac07eb4043a232df9f84e110485d7c7669dd114f679c27d15b97e"],
	["4c565efe04e7d32bac03ae358d63140c1cfe95de15e30c5b84f31bb0b65bb542d637f49e0f010000000551abab536348ae32b31c7d3132030a510a1b1aacf7b7c3f19ce8dc49944ef93e5fa5fe2d356b4a73a00100000009abac635163ac00ab514c8bc57b6b844e04555c0a4f4fb426df139475cd2396ae418bc7015820e852f711519bc202000000086a00510000abac52488ff4aec72cbcfcc98759c58e20a8d2d9725aa4a80f83964e69bc4e793a4ff25cd75dc701000000086a52ac6aac5351532ec6b10802463e0200000000000553005265523e08680100000000002f39a6b0", "", 3, 70712784, "c6076b6a45e6fcfba14d3df47a34f6aadbacfba107e95621d8d7c9c0e40518ed"],
	["1233d5e703403b3b8b4dae84510ddfc126b4838dcb47d3b23df815c0b3a07b55bf3098110e010000000163c5c55528041f480f40cf68a8762d6ed3efe2bd402795d5233e5d94bf5ddee71665144898030000000965525165655151656affffffff6381667e78bb74d0880625993bec0ea3bd41396f2bcccc3cc097b240e5e92d6a01000000096363acac6a63536365ffffffff04610ad60200000000065251ab65ab52e90d680200000000046351516ae30e98010000000008abab52520063656a671856010000000004ac6aac514c84e383", "6aabab636300", 1, -114996813, "aeb8c5a62e8a0b572c28f2029db32854c0b614dbecef0eaa726abebb42eebb8d"],
	["0c69702103b25ceaed43122cc2672de84a3b9aa49872f2a5bb458e19a52f8cc75973abb9f102000000055365656aacffffffff3ffb1cf0f76d9e3397de0942038c856b0ebbea355dc9d8f2b06036e19044b0450100000000ffffffff4b7793f4169617c54b734f2cd905ed65f1ce3d396ecd15b6c426a677186ca0620200000008655263526551006a181a25b703240cce0100000000046352ab53dee22903000000000865526a6a516a51005e121602000000000852ab52ababac655200000000", "6a516aab63", 1, -2040012771, "a6e6cb69f409ec14e10dd476f39167c29e586e99bfac93a37ed2c230fcc1dbbe"],
	["fd22692802db8ae6ab095aeae3867305a954278f7c076c542f0344b2591789e7e33e4d29f4020000000151ffffffffb9409129cfed9d3226f3b6bab7a2c83f99f48d039100eeb5796f00903b0e5e5e0100000006656552ac63abd226abac0403e649000000000007abab51ac5100ac8035f10000000000095165006a63526a52510d42db030000000007635365ac6a63ab24ef5901000000000453ab6a0000000000", "536a52516aac6a", 1, 309309168, "7ca0f75e6530ec9f80d031fc3513ca4ecd67f20cb38b4dacc6a1d825c3cdbfdb"],
	["a43f85f701ffa54a3cc57177510f3ea28ecb6db0d4431fc79171cad708a6054f6e5b4f89170000000008ac6a006a536551652bebeaa2013e779c05000000000665ac5363635100000000", "ac", 0, 2028978692, "58294f0d7f2e68fe1fd30c01764fe1619bcc7961d68968944a0e263af6550437"],
	["c2b0b99001acfecf7da736de0ffaef8134a9676811602a6299ba5a2563a23bb09e8cbedf9300000000026300ffffffff042997c50300000000045252536a272437030000000007655353ab6363ac663752030000000002ab6a6d5c900000000000066a6a5265abab00000000", "52ac525163515251", 0, -894181723, "8b300032a1915a4ac05cea2f7d44c26f2a08d109a71602636f15866563eaafdc"],
	["82f9f10304c17a9d954cf3380db817814a8c738d2c811f0412284b2c791ec75515f38c4f8c020000000265ab5729ca7db1b79abee66c8a757221f29280d0681355cb522149525f36da760548dbd7080a0100000001510b477bd9ce9ad5bb81c0306273a3a7d051e053f04ecf3a1dbeda543e20601a5755c0cfae030000000451ac656affffffff71141a04134f6c292c2e0d415e6705dfd8dcee892b0d0807828d5aeb7d11f5ef0300000001520b6c6dc802a6f3dd0000000000056aab515163bfb6800300000000015300000000", "", 3, -635779440, "d55ed1e6c53510f2608716c12132a11fb5e662ec67421a513c074537eeccc34b"],
	["8edcf5a1014b604e53f0d12fe143cf4284f86dc79a634a9f17d7e9f8725f7beb95e8ffcd2403000000046aabac52ffffffff01c402b5040000000005ab6a63525100000000", "6351525251acabab6a", 0, 1520147826, "2765bbdcd3ebb8b1a316c04656b28d637f80bffbe9b040661481d3dc83eea6d6"],
	["2074bad5011847f14df5ea7b4afd80cd56b02b99634893c6e3d5aaad41ca7c8ee8e5098df003000000026a6affffffff018ad59700000000000900ac656a526551635300000000", "65635265", 0, -1804671183, "663c999a52288c9999bff36c9da2f8b78d5c61b8347538f76c164ccba9868d0a"],
	["7100b11302e554d4ef249ee416e7510a485e43b2ba4b8812d8fe5529fe33ea75f36d392c4403000000020000ffffffff3d01a37e075e9a7715a657ae1bdf1e44b46e236ad16fd2f4c74eb9bf370368810000000007636553ac536365ffffffff01db696a0400000000065200ac656aac00000000", "63005151", 0, -1210499507, "b9c3aee8515a4a3b439de1ffc9c156824bda12cb75bfe5bc863164e8fd31bd7a"],
	["02c1017802091d1cb08fec512db7b012fe4220d57a5f15f9e7676358b012786e1209bcff950100000004acab6352ffffffff799bc282724a970a6fea1828984d0aeb0f16b67776fa213cbdc4838a2f1961a3010000000951516a536552ab6aabffffffff016c7b4b03000000000865abac5253ac5352b70195ad", "65655200516a", 0, -241626954, "be567cb47170b34ff81c66c1142cb9d27f9b6898a384d6dfc4fce16b75b6cb14"],
	["cb3178520136cd294568b83bb2520f78fecc507898f4a2db2674560d72fd69b9858f75b3b502000000066aac00515100ffffffff03ab005a01000000000563526363006e3836030000000001abfbda3200000000000665ab0065006500000000", "ab516a0063006a5300", 0, 1182109299, "2149e79c3f4513da4e4378608e497dcfdfc7f27c21a826868f728abd2b8a637a"],
	["18a4b0c004702cf0e39686ac98aab78ad788308f1d484b1ddfe70dc1997148ba0e28515c310300000000ffffffff05275a52a23c59da91129093364e275da5616c4070d8a05b96df5a2080ef259500000000096aac51656a6aac53ab66e64966b3b36a07dd2bb40242dd4a3743d3026e7e1e0d9e9e18f11d068464b989661321030000000265ac383339c4fae63379cafb63b0bab2eca70e1f5fc7d857eb5c88ccd6c0465093924bba8b2a000000000300636ab5e0545402bc2c4c010000000000cd41c002000000000000000000", "abac635253656a00", 3, 2052372230, "32db877b6b1ca556c9e859442329406f0f8246706522369839979a9f7a235a32"],
	["1d9c5df20139904c582285e1ea63dec934251c0f9cf5c47e86abfb2b394ebc57417a81f67c010000000353515222ba722504800d3402000000000353656a3c0b4a0200000000000fb8d20500000000076300ab005200516462f30400000000015200000000", "ab65", 0, -210854112, "edf73e2396694e58f6b619f68595b0c1cdcb56a9b3147845b6d6afdb5a80b736"],
	["4504cb1904c7a4acf375ddae431a74de72d5436efc73312cf8e9921f431267ea6852f9714a01000000066a656a656553a2fbd587c098b3a1c5bd1d6480f730a0d6d9b537966e20efc0e352d971576d0f87df0d6d01000000016321aeec3c4dcc819f1290edb463a737118f39ab5765800547522708c425306ebfca3f396603000000055300ac656a1d09281d05bfac57b5eb17eb3fa81ffcedfbcd3a917f1be0985c944d473d2c34d245eb350300000007656a51525152ac263078d9032f470f0500000000066aac00000052e12da60200000000003488410200000000076365006300ab539981e432", "52536a52526a", 1, -31909119, "f0a2deee7fd8a3a9fad6927e763ded11c940ee47e9e6d410f94fda5001f82e0c"],
	["14bc7c3e03322ec0f1311f4327e93059c996275302554473104f3f7b46ca179bfac9ef753503000000016affffffff9d405eaeffa1ca54d9a05441a296e5cc3a3e32bb8307afaf167f7b57190b07e00300000008abab51ab5263abab45533aa242c61bca90dd15d46079a0ab0841d85df67b29ba87f2393cd764a6997c372b55030000000452005263ffffffff0250f40e02000000000651516a0063630e95ab0000000000046a5151ac00000000", "6a65005151", 0, -1460947095, "aa418d096929394c9147be8818d8c9dafe6d105945ab9cd7ec682df537b5dd79"],
	["2b3bd0dd04a1832f893bf49a776cd567ec4b43945934f4786b615d6cb850dfc0349b33301a000000000565ac000051cf80c670f6ddafab63411adb4d91a69c11d9ac588898cbfb4cb16061821cc104325c895103000000025163ffffffffa9e2d7506d2d7d53b882bd377bbcc941f7a0f23fd15d2edbef3cd9df8a4c39d10200000009ac63006a52526a5265ffffffff44c099cdf10b10ce87d4b38658d002fd6ea17ae4a970053c05401d86d6e75f99000000000963ab53526a5252ab63ffffffff035af69c01000000000100ba9b8b0400000000004cead10500000000026a520b77d667", "ab52abac526553", 3, -1955078165, "eb9ceecc3b401224cb79a44d23aa8f428e29f1405daf69b4e01910b848ef1523"],
	["35df11f004a48ba439aba878fe9df20cc935b4a761c262b1b707e6f2b33e2bb7565cd68b130000000000ffffffffb2a2f99abf64163bb57ca900500b863f40c02632dfd9ea2590854c5fb4811da90200000006ac006363636affffffffaf9d89b2a8d2670ca37c8f7c140600b81259f2e037cb4590578ec6e37af8bf200000000005abac6a655270a4751eb551f058a93301ffeda2e252b6614a1fdd0e283e1d9fe53c96c5bbaafaac57b8030000000153ffffffff020d9f3b02000000000100ed7008030000000004abac000000000000", "abac", 3, 593793071, "88fdee1c2d4aeead71d62396e28dc4d00e5a23498eea66844b9f5d26d1f21042"],
	["a08ff466049fb7619e25502ec22fedfb229eaa1fe275aa0b5a23154b318441bf547989d0510000000005ab5363636affffffff2b0e335cb5383886751cdbd993dc0720817745a6b1c9b8ab3d15547fc9aafd03000000000965656a536a52656a532b53d10584c290d3ac1ab74ab0a19201a4a039cb59dc58719821c024f6bf2eb26322b33f010000000965ac6aac0053ab6353ffffffff048decba6ebbd2db81e416e39dde1f821ba69329725e702bcdea20c5cc0ecc6402000000086363ab5351ac6551466e377b0468c0fa00000000000651ab53ac6a513461c6010000000008636a636365535100eeb3dc010000000006526a52ac516a43f362010000000005000063536500000000", "0063516a", 1, -1158911348, "f6a1ecb50bd7c2594ebecea5a1aa23c905087553e40486dade793c2f127fdfae"],
	["5ac2f17d03bc902e2bac2469907ec7d01a62b5729340bc58c343b7145b66e6b97d434b30fa000000000163ffffffff44028aa674192caa0d0b4ebfeb969c284cb16b80c312d096efd80c6c6b094cca000000000763acabac516a52ffffffff10c809106e04b10f9b43085855521270fb48ab579266e7474657c6c625062d2d030000000351636595a0a97004a1b69603000000000465ab005352ad68010000000008636a5263acac5100da7105010000000002acab90325200000000000000000000", "6a6aab516a63526353", 2, 1518400956, "f7efb74b1dcc49d316b49c632301bc46f98d333c427e55338be60c7ef0d953be"],
	["aeb2e11902dc3770c218b97f0b1960d6ee70459ecb6a95eff3f05295dc1ef4a0884f10ba460300000005516352526393e9b1b3e6ae834102d699ddd3845a1e159aa7cf7635edb5c02003f7830fee3788b795f20100000009ab006a526553ac006ad8809c570469290e0400000000050000abab00b10fd5040000000008ab655263abac53ab630b180300000000009d9993040000000002516300000000", "5351ababac6a65", 0, 1084852870, "f2286001af0b0170cbdad92693d0a5ebaa8262a4a9d66e002f6d79a8c94026d1"],
	["9860ca9a0294ff4812534def8c3a3e3db35b817e1a2ddb7f0bf673f70eab71bb79e90a2f3100000000086a636551acac5165ffffffffed4d6d3cd9ff9b2d490e0c089739121161a1445844c3e204296816ab06e0a83702000000035100ac88d0db5201c3b59a050000000005ac6a0051ab00000000", "535263ab006a526aab", 1, -962088116, "30df2473e1403e2b8e637e576825f785528d998af127d501556e5f7f5ed89a2a"],
	["4ddaa680026ec4d8060640304b86823f1ac760c260cef81d85bd847952863d629a3002b54b0200000008526365636a656aab65457861fc6c24bdc760c8b2e906b6656edaf9ed22b5f50e1fb29ec076ceadd9e8ebcb6b000000000152ffffffff033ff04f00000000000551526a00657a1d900300000000002153af040000000003006a6300000000", "ab526a53acabab", 0, 1055317633, "7f21b62267ed52462e371a917eb3542569a4049b9dfca2de3c75872b39510b26"],
	["01e76dcd02ad54cbc8c71d68eaf3fa7c883b65d74217b30ba81f1f5144ef80b706c0dc82ca000000000352ab6a078ec18bcd0514825feced2e8b8ea1ccb34429fae41c70cc0b73a2799e85603613c6870002000000086363ab6365536a53ffffffff043acea90000000000016ad20e1803000000000100fa00830200000000056352515351e864ee00000000000865535253ab6a6551d0c46672", "6a6365abacab", 0, -1420559003, "8af0b4cbdbc011be848edf4dbd2cde96f0578d662cfebc42252495387114224a"],
	["fa00b26402670b97906203434aa967ce1559d9bd097d56dbe760469e6032e7ab61accb54160100000006635163630052fffffffffe0d3f4f0f808fd9cfb162e9f0c004601acf725cd7ea5683bbdc9a9a433ef15a0200000005ab52536563d09c7bef049040f305000000000153a7c7b9020000000004ac63ab52847a2503000000000553ab00655390ed80010000000005006553ab52860671
Download .txt
gitextract_1jxgzyz8/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── rollup.config.ts
├── src/
│   ├── class/
│   │   ├── Signature/
│   │   │   └── index.ts
│   │   └── Transaction/
│   │       ├── Transaction.ts
│   │       ├── TxInput.ts
│   │       ├── TxLocktime.ts
│   │       ├── TxOutput.ts
│   │       ├── TxScript.ts
│   │       ├── TxSequence.ts
│   │       ├── TxWitness.ts
│   │       └── index.ts
│   ├── index.ts
│   ├── lib/
│   │   ├── addr/
│   │   │   ├── hash.ts
│   │   │   ├── index.ts
│   │   │   ├── p2pkh.ts
│   │   │   ├── p2sh.ts
│   │   │   ├── p2tr.ts
│   │   │   ├── p2w-pkh.ts
│   │   │   ├── p2w-sh.ts
│   │   │   ├── schema.ts
│   │   │   └── utils.ts
│   │   ├── check.ts
│   │   ├── script/
│   │   │   ├── decode.ts
│   │   │   ├── encode.ts
│   │   │   ├── format.ts
│   │   │   ├── index.ts
│   │   │   └── words.ts
│   │   ├── sig/
│   │   │   ├── index.ts
│   │   │   ├── segwit/
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── sign.ts
│   │   │   │   └── verify.ts
│   │   │   ├── taproot/
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── sign.ts
│   │   │   │   └── verify.ts
│   │   │   └── types.ts
│   │   ├── tap/
│   │   │   ├── index.ts
│   │   │   ├── key.ts
│   │   │   ├── tree.ts
│   │   │   ├── tweak.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   ├── tx/
│   │   │   ├── create.ts
│   │   │   ├── decode.ts
│   │   │   ├── encode.ts
│   │   │   ├── format.ts
│   │   │   ├── index.ts
│   │   │   └── parse.ts
│   │   └── utils.ts
│   └── schema/
│       ├── check.ts
│       └── types.ts
├── test/
│   ├── bin/
│   │   ├── bitcoin-cli
│   │   └── bitcoind
│   ├── bitcoin.conf
│   ├── core.ts
│   ├── example/
│   │   ├── ex_test.ts
│   │   └── taproot/
│   │       ├── inscribe.test.ts
│   │       ├── keyspend.test.ts
│   │       ├── tapscript.test.ts
│   │       └── taptree.test.ts
│   ├── index.html
│   ├── scratch.ts
│   ├── src/
│   │   ├── addr/
│   │   │   ├── addr.test.ts
│   │   │   ├── p2pkh.test.ts
│   │   │   ├── p2sh.test.ts
│   │   │   ├── p2tr.test.ts
│   │   │   ├── p2wpkh.test.ts
│   │   │   └── p2wsh.test.ts
│   │   ├── sig/
│   │   │   ├── segwit/
│   │   │   │   ├── bip0143.vectors.json
│   │   │   │   ├── sighash.test.ts
│   │   │   │   ├── sighash.vectors.json
│   │   │   │   └── utils.ts
│   │   │   ├── sig.test.ts
│   │   │   └── taproot/
│   │   │       ├── sig.test.ts
│   │   │       ├── sig.vectors.json
│   │   │       ├── tx.test.ts
│   │   │       └── tx.vectors.json
│   │   ├── tap/
│   │   │   ├── tree.test.ts
│   │   │   ├── tree.vectors.json
│   │   │   ├── unit.test.ts
│   │   │   └── unit.vectors.json
│   │   └── tx/
│   │       ├── segwit/
│   │       │   ├── segwit.test.ts
│   │       │   ├── utils.ts
│   │       │   └── valid.vectors.json
│   │       └── tx.test.ts
│   ├── tape.ts
│   ├── tsconfig.json
│   └── utils.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (289 symbols across 61 files)

FILE: src/class/Transaction/Transaction.ts
  class Transaction (line 14) | class Transaction {
    method constructor (line 17) | constructor (
    method data (line 32) | get data () : TxData {
    method version (line 36) | get version () : number {
    method vin (line 40) | get vin () : TxInput[] {
    method vout (line 44) | get vout () : TxOutput[] {
    method locktime (line 48) | get locktime () : TxLocktime {
    method base (line 52) | get base () : Buff {
    method buff (line 56) | get buff () : Buff {
    method raw (line 60) | get raw () : Uint8Array {
    method hex (line 64) | get hex () : string {
    method size (line 68) | get size () : number {
    method bsize (line 72) | get bsize () : number {
    method weight (line 76) | get weight () : number {
    method vsize (line 80) | get vsize () : number {
    method hash (line 85) | get hash () : string {
    method txid (line 90) | get txid () : string {
    method export (line 95) | async export () : Promise<object> {

FILE: src/class/Transaction/TxInput.ts
  class TxInput (line 11) | class TxInput {
    method constructor (line 15) | constructor (txdata : TxData, index : number) {
    method data (line 20) | get data () : InputData {
    method txid (line 24) | get txid () : string {
    method vout (line 28) | get vout () : number {
    method prevout (line 32) | get prevout () : TxOutput | undefined {
    method scriptSig (line 38) | get scriptSig () : TxScript {
    method sequence (line 42) | get sequence () : TxSequence {
    method witness (line 46) | get witness () : TxWitness {
    method type (line 50) | get type () : InputType {
    method sign (line 71) | sign (seckey : Bytes, config : HashConfig) {

FILE: src/class/Transaction/TxLocktime.ts
  constant LOCKTIME_THRESHOLD (line 4) | const LOCKTIME_THRESHOLD = 500000000
  class TxLocktime (line 6) | class TxLocktime {
    method constructor (line 9) | constructor (value : LockData = 0) {
    method isTimelock (line 13) | get isTimelock () : boolean {
    method timestamp (line 17) | get timestamp () : number {
    method timestamp (line 23) | set timestamp (value : number) {
    method blockheight (line 27) | get blockheight () : number {
    method blockheight (line 33) | set blockheight (value : number) {
    method estDate (line 37) | get estDate () : Date {
    method estDate (line 43) | set estDate (date : Date) {
    method toJSON (line 47) | toJSON () : number {

FILE: src/class/Transaction/TxOutput.ts
  class TxOutput (line 5) | class TxOutput {
    method constructor (line 9) | constructor (txout : OutputData) {
    method type (line 14) | get type () : OutputType {

FILE: src/class/Transaction/TxScript.ts
  type ScriptFormat (line 8) | type ScriptFormat = 'p2sh' | 'p2w' | 'p2tr'
  class TxScript (line 10) | class TxScript {
    method constructor (line 13) | constructor(
    method raw (line 19) | get raw () : Uint8Array {
    method hex (line 23) | get hex () : string {
    method asm (line 27) | get asm () : string[] {
    method getHash (line 31) | getHash (format : ScriptFormat, version ?: number) : string {
    method toJSON (line 44) | toJSON() : string[] {

FILE: src/class/Transaction/TxSequence.ts
  constant MAX_VAL (line 3) | const MAX_VAL     = 0xFFFFFFFF
  constant NO_LOCK (line 4) | const NO_LOCK     = (1 << 31)
  constant TIME_MOD (line 5) | const TIME_MOD    = 512
  constant LOCK_TYPE (line 6) | const LOCK_TYPE   = (1 << 22)
  class TxSequence (line 12) | class TxSequence {
    method constructor (line 15) | constructor (value : SequenceData) {
    method isReplaceable (line 23) | get isReplaceable () : boolean {
    method isLocked (line 27) | get isLocked () : boolean {
    method isTimelock (line 31) | get isTimelock () : boolean {
    method timestamp (line 35) | get timestamp () : number {
    method timestamp (line 43) | set timestamp (value : number) {
    method blockheight (line 49) | get blockheight () : number {
    method blockheight (line 57) | set blockheight (value : number) {
    method estDate (line 62) | get estDate () : Date {
    method estDate (line 68) | set estDate (date : Date) {
    method toJSON (line 76) | toJSON () : number {

FILE: src/class/Transaction/TxWitness.ts
  class TxWitness (line 6) | class TxWitness {
    method constructor (line 11) | constructor (
    method length (line 20) | get length () : number {
    method annex (line 24) | get annex () : string | undefined {
    method cblock (line 31) | get cblock () : string | undefined {
    method script (line 38) | get script () : ScriptData | undefined {
    method params (line 45) | get params () : Bytes[] {
    method toJSON (line 49) | toJSON () : ScriptData[] {

FILE: src/lib/addr/hash.ts
  function hash160pkh (line 7) | function hash160pkh (pubkey : Bytes) : Buff {
  function hash160sh (line 13) | function hash160sh (script : ScriptData) : Buff {
  function sha256sh (line 18) | function sha256sh (script : ScriptData) : Buff {

FILE: src/lib/addr/p2pkh.ts
  function check (line 6) | function check (
  function encode (line 19) | function encode (
  function decode (line 29) | function decode (
  function scriptPubKey (line 39) | function scriptPubKey (input : Bytes) : string[] {
  function fromPubKey (line 45) | function fromPubKey (
  constant P2PKH (line 53) | const P2PKH = { check, encode, decode, hash: hash160pkh, scriptPubKey, f...

FILE: src/lib/addr/p2sh.ts
  function check (line 6) | function check (
  function encode (line 20) | function encode (
  function decode (line 30) | function decode (
  function scriptPubKey (line 40) | function scriptPubKey (input : Bytes) : string[] {
  function fromScript (line 45) | function fromScript (
  constant P2SH (line 53) | const P2SH = { check, encode, decode, hash: hash160sh, scriptPubKey, fro...

FILE: src/lib/addr/p2tr.ts
  constant VALID_PREFIXES (line 7) | const VALID_PREFIXES = [ 'bc1p', 'tb1p', 'bcrt1p' ]
  function check (line 9) | function check (address : string) : boolean {
  function encode (line 18) | function encode (
  function decode (line 28) | function decode (address : string) : Buff {
  function scriptPubKey (line 35) | function scriptPubKey (input : Bytes) : string[] {
  function fromPubKey (line 41) | function fromPubKey (
  constant P2TR (line 49) | const P2TR = { check, encode, decode, scriptPubKey, fromPubKey }

FILE: src/lib/addr/p2w-pkh.ts
  constant VALID_PREFIXES (line 7) | const VALID_PREFIXES = [ 'bc1q', 'tb1q', 'bcrt1q' ]
  function check (line 9) | function check (address : string) : boolean {
  function encode (line 18) | function encode (
  function decode (line 28) | function decode (address : string) : Buff {
  function scriptPubKey (line 35) | function scriptPubKey (input : Bytes) : string[] {
  function fromPubKey (line 41) | function fromPubKey (
  constant P2WPKH (line 49) | const P2WPKH = { check, encode, decode, hash: hash160pkh, scriptPubKey, ...

FILE: src/lib/addr/p2w-sh.ts
  constant VALID_PREFIXES (line 7) | const VALID_PREFIXES = [ 'bc1q', 'tb1q', 'bcrt1q' ]
  function check (line 9) | function check (address : string) : boolean {
  function encode (line 18) | function encode (
  function decode (line 28) | function decode (address : string) : Buff {
  function scriptPubKey (line 35) | function scriptPubKey (input : Bytes) : string[] {
  function fromScript (line 41) | function fromScript (
  constant P2WSH (line 49) | const P2WSH = { check, encode, decode, hash: sha256sh, scriptPubKey, fro...

FILE: src/lib/addr/schema.ts
  type AddressType (line 6) | type AddressType = [
  type AddressData (line 14) | interface AddressData {
  type AddressTool (line 22) | interface AddressTool {
  type AddrKeyTool (line 29) | interface AddrKeyTool extends AddressTool {
  type AddrScriptTool (line 33) | interface AddrScriptTool extends AddressTool {
  constant BECH32_PREFIXES (line 37) | const BECH32_PREFIXES : Record<Networks, string> = {

FILE: src/lib/addr/utils.ts
  constant ADDRESS_TYPES (line 20) | const ADDRESS_TYPES : AddressType[] = [
  function decodeFormat (line 37) | function decodeFormat (address : string, format : string) : Buff {
  function getData (line 46) | function getData (address : string) : AddressType {
  function getTool (line 59) | function getTool (type : OutputType) : AddressTool | AddrScriptTool {
  function decodeAddress (line 70) | function decodeAddress (
  function fromScriptPubKey (line 80) | function fromScriptPubKey (
  function toScriptPubKey (line 89) | function toScriptPubKey (address : string) : ScriptData {

FILE: src/lib/check.ts
  function isHex (line 3) | function isHex<T> (value : T) : value is Extract<T, string> {
  function isBytes (line 11) | function isBytes<T> (value : T) : value is Extract<T, Bytes> {
  function isValidAnnex (line 15) | function isValidAnnex (annex : any) : boolean {

FILE: src/lib/script/decode.ts
  function decodeScript (line 9) | function decodeScript (
  function decodeWords (line 25) | function decodeWords (

FILE: src/lib/script/encode.ts
  constant MAX_WORD_SIZE (line 6) | const MAX_WORD_SIZE = 0x208
  function encodeScript (line 8) | function encodeScript (
  function encodeWords (line 33) | function encodeWords (
  function encodeWord (line 45) | function encodeWord (
  function encodeSize (line 98) | function encodeSize (size : number) : Uint8Array {
  function splitWord (line 114) | function splitWord (word : Uint8Array) : Word[] {

FILE: src/lib/script/format.ts
  function toAsm (line 7) | function toAsm (
  function toBytes (line 23) | function toBytes (
  function toParam (line 37) | function toParam (

FILE: src/lib/script/words.ts
  constant OPCODE_MAP (line 1) | const OPCODE_MAP = {
  function getOpLabel (line 182) | function getOpLabel (num : number) : string {
  function getOpCode (line 192) | function getOpCode (string : string) : number {
  function getWordType (line 199) | function getWordType (word : number) : string {
  function isValidWord (line 218) | function isValidWord (word : number) : boolean {

FILE: src/lib/sig/segwit/hash.ts
  constant VALID_HASH_TYPES (line 15) | const VALID_HASH_TYPES = [ 0x01, 0x02, 0x03 ]
  function hashTx (line 17) | function hashTx (
  function hashPrevouts (line 82) | function hashPrevouts (
  function hashSequence (line 100) | function hashSequence (
  function hashOutputs (line 117) | function hashOutputs (

FILE: src/lib/sig/segwit/sign.ts
  function signTx (line 7) | function signTx (

FILE: src/lib/sig/segwit/verify.ts
  function verifyTx (line 10) | function verifyTx (

FILE: src/lib/sig/taproot/hash.ts
  constant VALID_HASH_TYPES (line 16) | const VALID_HASH_TYPES = [ 0x00, 0x01, 0x02, 0x03, 0x81, 0x82, 0x83 ]
  function hashTx (line 18) | function hashTx (
  function hashOutpoints (line 140) | function hashOutpoints (
  function hashSequence (line 151) | function hashSequence (
  function hashAmounts (line 161) | function hashAmounts (
  function hashScripts (line 171) | function hashScripts (
  function hashOutputs (line 181) | function hashOutputs (
  function hashOutput (line 192) | function hashOutput (
  function getAnnexData (line 201) | function getAnnexData (
  function getPrevout (line 227) | function getPrevout (vin : InputData) : OutputData {

FILE: src/lib/sig/taproot/sign.ts
  constant FIELD_SIZE (line 9) | const FIELD_SIZE  = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF...
  constant CURVE_ORDER (line 10) | const CURVE_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBF...
  function signTx (line 12) | function signTx (
  function sign (line 30) | function sign (
  function verify (line 67) | function verify (

FILE: src/lib/sig/taproot/verify.ts
  function verifyTx (line 12) | function verifyTx (

FILE: src/lib/sig/types.ts
  type HashConfig (line 3) | interface HashConfig {

FILE: src/lib/tap/key.ts
  constant DEFAULT_VERSION (line 10) | const DEFAULT_VERSION = 0xc0
  function getTapSecKey (line 12) | function getTapSecKey (
  function getTapPubKey (line 19) | function getTapPubKey (
  function getTapKey (line 26) | function getTapKey (
  function checkPath (line 87) | function checkPath (
  function readCtrlBlock (line 122) | function readCtrlBlock (cblock : Bytes) : CtrlBlock {
  function readParityBit (line 139) | function readParityBit (parity : number | string = 0x02) : number {

FILE: src/lib/tap/tree.ts
  constant DEFAULT_VERSION (line 6) | const DEFAULT_VERSION = 0xc0
  function getTapTag (line 8) | function getTapTag (tag : string) : Buff {
  function getTapLeaf (line 13) | function getTapLeaf (
  function getTapScript (line 24) | function getTapScript (
  function getTapBranch (line 31) | function getTapBranch (
  function getTapRoot (line 48) | function getTapRoot (
  function merkleize (line 55) | function merkleize (
  function getVersion (line 117) | function getVersion (version = 0xc0) : number {

FILE: src/lib/tap/tweak.ts
  function getTapTweak (line 7) | function getTapTweak (
  function getTweakedKey (line 18) | function getTweakedKey (
  function getTweakedPub (line 37) | function getTweakedPub (
  function getTweakedSec (line 44) | function getTweakedSec (
  function tweakSecKey (line 51) | function tweakSecKey (
  function tweakPubKey (line 62) | function tweakPubKey (
  function getScriptOnlyPubkey (line 72) | function getScriptOnlyPubkey () : Buff {
  constant SCRIPT_PUBKEY (line 79) | const SCRIPT_PUBKEY = getScriptOnlyPubkey()

FILE: src/lib/tap/types.ts
  type TapKey (line 4) | type TapKey = [
  type TapConfig (line 9) | interface TapConfig {
  type CtrlBlock (line 20) | interface CtrlBlock {
  type TapTree (line 27) | type TapTree = Array<string | string[]>
  type TapProof (line 29) | type TapProof = [

FILE: src/lib/tap/utils.ts
  function xOnlyPub (line 3) | function xOnlyPub (key : Bytes) : Buff {

FILE: src/lib/tx/create.ts
  constant DEFAULT_TX (line 3) | const DEFAULT_TX = {
  constant DEFAULT_VIN (line 10) | const DEFAULT_VIN = {
  constant DEFAULT_VOUT (line 16) | const DEFAULT_VOUT = {
  function createTx (line 21) | function createTx (template : TxTemplate) : TxData {

FILE: src/lib/tx/decode.ts
  function decodeTx (line 9) | function decodeTx (bytes : string | Uint8Array) : TxData {
  function readVersion (line 42) | function readVersion (stream : Stream) : number {
  function checkWitnessFlag (line 46) | function checkWitnessFlag (stream : Stream) : boolean {
  function readInputs (line 59) | function readInputs (stream : Stream) : InputData[] {
  function readInput (line 68) | function readInput (stream : Stream) : InputData {
  function readOutputs (line 79) | function readOutputs (stream : Stream) : OutputData[] {
  function readOutput (line 88) | function readOutput (stream : Stream) : OutputData {
  function readWitness (line 96) | function readWitness (stream : Stream) : string[] {
  function readData (line 106) | function readData (
  function readScript (line 118) | function readScript (
  function readLocktime (line 126) | function readLocktime (stream : Stream) : number {

FILE: src/lib/tx/encode.ts
  function encodeTx (line 16) | function encodeTx (
  function checkForWitness (line 47) | function checkForWitness (vin : InputData[]) : boolean {
  function encodeVersion (line 62) | function encodeVersion (num : number) : Uint8Array {
  function encodeTxid (line 66) | function encodeTxid (txid : string) : Uint8Array {
  function encodePrevOut (line 70) | function encodePrevOut (vout : number) : Uint8Array {
  function encodeSequence (line 74) | function encodeSequence (
  function encodeInputs (line 86) | function encodeInputs (arr : InputData[]) : Uint8Array {
  function encodeValue (line 98) | function encodeValue (
  function encodeOutputs (line 110) | function encodeOutputs (arr : OutputData[]) : Uint8Array {
  function encodeOutput (line 118) | function encodeOutput (
  function encodeWitness (line 128) | function encodeWitness (
  function encodeData (line 142) | function encodeData (data : ScriptData) : Buff {
  function isEmpty (line 148) | function isEmpty (data : ScriptData) : boolean {
  function encodeLocktime (line 159) | function encodeLocktime (locktime : LockData) : Uint8Array {

FILE: src/lib/tx/format.ts
  function toJson (line 9) | function toJson (
  function toBytes (line 25) | function toBytes (

FILE: src/lib/tx/parse.ts
  type TxSizeData (line 18) | interface TxSizeData {
  constant OUTPUT_TYPES (line 25) | const OUTPUT_TYPES : Array<[ string, RegExp ]> = [
  constant LEAF_VERSIONS (line 33) | const LEAF_VERSIONS = [
  function parseAnnex (line 42) | function parseAnnex (
  function parseBlock (line 63) | function parseBlock (
  function parseWitScript (line 85) | function parseWitScript (
  function parseParams (line 101) | function parseParams (
  function readWitness (line 118) | function readWitness (
  function readScriptPubKey (line 129) | function readScriptPubKey (
  function getTxid (line 144) | function getTxid (txdata : TxData | Bytes) : string {
  function getTxSize (line 150) | function getTxSize (txdata : TxData | Bytes) : TxSizeData {

FILE: src/lib/utils.ts
  function checkSize (line 3) | function checkSize (input : Bytes, size : number) : void {
  function safeThrow (line 10) | function safeThrow (
  function hashTag (line 19) | function hashTag (

FILE: src/schema/types.ts
  type Networks (line 3) | type Networks   = 'main' | 'testnet' | 'signet' | 'regtest'
  type InputType (line 5) | type InputType  = 'p2pkh'   | 'p2sh'   | 'p2w-p2pkh' | 'p2w-p2sh' |
  type OutputType (line 8) | type OutputType = 'p2pkh'  | 'p2sh'  | 'p2w-pkh' | 'p2w-sh' | 'p2tr' | '...
  type TxTemplate (line 10) | interface TxTemplate {
  type TxData (line 27) | interface TxData {
  type InputData (line 34) | interface InputData {
  type OutputData (line 43) | interface OutputData {
  type ScriptPubKeyData (line 48) | interface ScriptPubKeyData {
  type WitnessData (line 53) | interface WitnessData {
  type SequenceData (line 60) | type SequenceData = string | number
  type LockData (line 61) | type LockData     = string | number
  type ValueData (line 62) | type ValueData    = number | bigint
  type ScriptData (line 63) | type ScriptData   = Bytes  | Word[]
  type Bytes (line 64) | type Bytes        = string | Uint8Array
  type Word (line 65) | type Word         = string | number | Uint8Array

FILE: test/core.ts
  constant DEFAULT_CONFIG (line 3) | const DEFAULT_CONFIG = {
  function get_client (line 17) | function get_client (

FILE: test/example/ex_test.ts
  function example_tests (line 13) | async function example_tests (t : Test) : Promise<void> {

FILE: test/example/taproot/inscribe.test.ts
  function inscription (line 11) | async function inscription (t : Test, wallet : CoreWallet) : Promise<voi...

FILE: test/example/taproot/keyspend.test.ts
  function key_spend (line 8) | async function key_spend (t : Test, wallet : CoreWallet) : Promise<void> {

FILE: test/example/taproot/tapscript.test.ts
  function script_spend (line 7) | async function script_spend (t : Test, wallet : CoreWallet) : Promise<vo...

FILE: test/example/taproot/taptree.test.ts
  function tree_spend (line 7) | async function tree_spend (t : Test, wallet : CoreWallet) : Promise<void> {

FILE: test/src/addr/addr.test.ts
  function address_tests (line 8) | function address_tests(t : Test) {

FILE: test/src/addr/p2pkh.test.ts
  function p2pkh_test (line 6) | function p2pkh_test(t : Test) : void {

FILE: test/src/addr/p2sh.test.ts
  function p2sh_test (line 6) | function p2sh_test(t : Test) : void {

FILE: test/src/addr/p2tr.test.ts
  function p2tr_test (line 6) | function p2tr_test(t : Test) : void {

FILE: test/src/addr/p2wpkh.test.ts
  function p2wpkh_test (line 6) | function p2wpkh_test(t : Test) : void {

FILE: test/src/addr/p2wsh.test.ts
  function p2wsh_test (line 6) | function p2wsh_test(t : Test) : void {

FILE: test/src/sig/segwit/sighash.test.ts
  function sighash_vector_test (line 7) | async function sighash_vector_test(t :Test) : Promise<void> {

FILE: test/src/sig/segwit/utils.ts
  type TestInput (line 4) | type TestInput = [
  type TestData (line 11) | type TestData = [
  type TestVector (line 17) | interface TestVector {
  function parseVectors (line 23) | function parseVectors(vectors : any) : TestVector[] {

FILE: test/src/sig/sig.test.ts
  function sig_tests (line 12) | async function sig_tests (t : Test) : Promise<void> {

FILE: test/src/sig/taproot/sig.test.ts
  function test_computehash (line 17) | function test_computehash(t : Test) : void {
  function test_signatures (line 33) | async function test_signatures(t : Test) : Promise<void> {

FILE: test/src/tap/tree.test.ts
  type Vector (line 7) | interface Vector {
  function flattenArray (line 19) | function flattenArray (
  function tweak_test (line 31) | function tweak_test(t : Test) : void {

FILE: test/src/tap/unit.test.ts
  function unit_tests (line 8) | async function unit_tests(t : Test) : Promise<void> {

FILE: test/src/tx/segwit/segwit.test.ts
  function segwit_vector_test (line 6) | function segwit_vector_test(t :Test) : void {

FILE: test/src/tx/segwit/utils.ts
  type TestInput (line 4) | type TestInput = [
  type TestData (line 11) | type TestData = [
  type TestVector (line 17) | interface TestVector {
  function parseVectors (line 23) | function parseVectors(vectors : any) : TestVector[] {

FILE: test/src/tx/tx.test.ts
  function tx_tests (line 4) | function tx_tests(t : Test) : void {

FILE: test/utils.ts
  class TxTest (line 1) | class TxTest {
    method constructor (line 2) | constructor () {
Condensed preview — 94 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (529K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 137,
    "preview": "# These are supported funding model platforms\n\nbitcoin: bitcoin:bc1qnx8julmud2vqdqngxw9rmuv3js67kq9rzvtmm5\nlnurl: lnurl:"
  },
  {
    "path": ".gitignore",
    "chars": 392,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# nyc\ncoverage\n.nyc_output\n\n# Dependency directories\nn"
  },
  {
    "path": "LICENSE",
    "chars": 7048,
    "preview": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n"
  },
  {
    "path": "README.md",
    "chars": 36664,
    "preview": "# Tapscript\n\nA basic library for working with Taproot, Schnorr Signatures, and Bitcoin transactions.\n\n> Note: For nodejs"
  },
  {
    "path": "package.json",
    "chars": 1987,
    "preview": "{\n  \"name\": \"@cmdcode/tapscript\",\n  \"version\": \"1.5.3\",\n  \"description\": \"A basic library for working with Tapscript, si"
  },
  {
    "path": "rollup.config.ts",
    "chars": 1671,
    "preview": "// rollup.config.ts\nimport typescript  from '@rollup/plugin-typescript'\nimport nodeResolve from '@rollup/plugin-node-res"
  },
  {
    "path": "src/class/Signature/index.ts",
    "chars": 9,
    "preview": "export {}"
  },
  {
    "path": "src/class/Transaction/Transaction.ts",
    "chars": 2104,
    "preview": "import { Buff }     from '@cmdcode/buff-utils'\nimport { hash256 }  from '@cmdcode/crypto-utils'\nimport TxInput      from"
  },
  {
    "path": "src/class/Transaction/TxInput.ts",
    "chars": 2261,
    "preview": "// import getSigHash from \"../../sighash.js\"\nimport TxScript       from './TxScript.js'\nimport TxSequence     from './Tx"
  },
  {
    "path": "src/class/Transaction/TxLocktime.ts",
    "chars": 1027,
    "preview": "import { Buff } from '@cmdcode/buff-utils'\nimport { LockData }  from '../../schema/types.js'\n\nconst LOCKTIME_THRESHOLD ="
  },
  {
    "path": "src/class/Transaction/TxOutput.ts",
    "chars": 482,
    "preview": "import TxScript from './TxScript.js'\nimport { readScriptPubKey } from '../../lib/tx/parse.js'\nimport { OutputData, Outpu"
  },
  {
    "path": "src/class/Transaction/TxScript.ts",
    "chars": 1126,
    "preview": "import { Buff }         from '@cmdcode/buff-utils'\nimport { encodeScript } from '../../lib/script/encode.js'\nimport { de"
  },
  {
    "path": "src/class/Transaction/TxSequence.ts",
    "chars": 3077,
    "preview": "import { SequenceData } from '../../schema/types.js'\n\nconst MAX_VAL     = 0xFFFFFFFF\nconst NO_LOCK     = (1 << 31)\nconst"
  },
  {
    "path": "src/class/Transaction/TxWitness.ts",
    "chars": 1187,
    "preview": "import { Buff }        from '@cmdcode/buff-utils'\nimport { readWitness } from '../../lib/tx/parse.js'\nimport { Script } "
  },
  {
    "path": "src/class/Transaction/index.ts",
    "chars": 313,
    "preview": "import Transaction from './Transaction.js'\nimport Input     from './TxInput.js'\nimport Output    from './TxOutput.js'\nim"
  },
  {
    "path": "src/index.ts",
    "chars": 308,
    "preview": "export { Address } from './lib/addr/index.js'\nexport { Script }  from './lib/script/index.js'\nexport { Signer }  from '."
  },
  {
    "path": "src/lib/addr/hash.ts",
    "chars": 653,
    "preview": "import { Buff, Bytes } from '@cmdcode/buff-utils'\nimport { Script }      from '../script/index.js'\nimport { checkSize } "
  },
  {
    "path": "src/lib/addr/index.ts",
    "chars": 492,
    "preview": "// https://en.bitcoin.it/wiki/Invoice_address\n\nimport { P2PKH }  from './p2pkh.js'\nimport { P2SH }   from './p2sh.js'\nim"
  },
  {
    "path": "src/lib/addr/p2pkh.ts",
    "chars": 1390,
    "preview": "import { Buff }            from '@cmdcode/buff-utils'\nimport { Bytes, Networks } from '../../schema/types.js'\nimport { c"
  },
  {
    "path": "src/lib/addr/p2sh.ts",
    "chars": 1374,
    "preview": "import { Buff }      from '@cmdcode/buff-utils'\nimport { checkSize } from '../utils.js'\nimport { hash160sh } from './has"
  },
  {
    "path": "src/lib/addr/p2tr.ts",
    "chars": 1223,
    "preview": "import { Buff } from '@cmdcode/buff-utils'\nimport { Bytes, Networks } from '../../schema/types.js'\nimport { xOnlyPub } f"
  },
  {
    "path": "src/lib/addr/p2w-pkh.ts",
    "chars": 1257,
    "preview": "import { Buff }            from '@cmdcode/buff-utils'\nimport { Bytes, Networks } from '../../schema/types.js'\nimport { c"
  },
  {
    "path": "src/lib/addr/p2w-sh.ts",
    "chars": 1267,
    "preview": "import { Buff }            from '@cmdcode/buff-utils'\nimport { checkSize }       from '../utils.js'\nimport { BECH32_PREF"
  },
  {
    "path": "src/lib/addr/schema.ts",
    "chars": 1093,
    "preview": "/* eslint-disable quote-props */\n\nimport { Buff } from '@cmdcode/buff-utils'\nimport { Bytes, Networks, OutputType, Scrip"
  },
  {
    "path": "src/lib/addr/utils.ts",
    "chars": 2963,
    "preview": "\nimport { Buff }      from '@cmdcode/buff-utils'\nimport { Script }    from '../script/index.js'\nimport { P2PKH }     fro"
  },
  {
    "path": "src/lib/check.ts",
    "chars": 484,
    "preview": "import { Bytes } from '../schema/types'\n\nexport function isHex<T> (value : T) : value is Extract<T, string> {\n  return ("
  },
  {
    "path": "src/lib/script/decode.ts",
    "chars": 1755,
    "preview": "import { Buff, Stream } from '@cmdcode/buff-utils'\n\nimport {\n  getOpLabel,\n  getWordType,\n  isValidWord\n} from './words."
  },
  {
    "path": "src/lib/script/encode.ts",
    "chars": 3255,
    "preview": "import { Buff, Stream } from '@cmdcode/buff-utils'\nimport { getOpCode }    from './words.js'\nimport { isHex }        fro"
  },
  {
    "path": "src/lib/script/format.ts",
    "chars": 1126,
    "preview": "import { Buff }         from '@cmdcode/buff-utils'\nimport { isHex }        from '../check.js'\nimport { decodeScript } fr"
  },
  {
    "path": "src/lib/script/index.ts",
    "chars": 227,
    "preview": "import { encodeScript } from './encode.js'\nimport { decodeScript } from './decode.js'\nimport { FmtScript }    from './fo"
  },
  {
    "path": "src/lib/script/words.ts",
    "chars": 7120,
    "preview": "export const OPCODE_MAP = {\n  OP_0                   : 0,\n  OP_PUSHDATA1           : 76,\n  OP_PUSHDATA2           : 77,\n"
  },
  {
    "path": "src/lib/sig/index.ts",
    "chars": 161,
    "preview": "import { SWSigner } from './segwit/index.js'\nimport { TRSigner } from './taproot/index.js'\n\nexport const Signer = {\n  se"
  },
  {
    "path": "src/lib/sig/segwit/hash.ts",
    "chars": 3814,
    "preview": "import { Buff }       from '@cmdcode/buff-utils'\nimport * as ENC       from '../../tx/encode.js'\nimport { Script }     f"
  },
  {
    "path": "src/lib/sig/segwit/index.ts",
    "chars": 200,
    "preview": "import { hashTx }   from './hash.js'\nimport { signTx }   from './sign.js'\nimport { verifyTx } from './verify.js'\n\nexport"
  },
  {
    "path": "src/lib/sig/segwit/sign.ts",
    "chars": 583,
    "preview": "import { Buff }       from '@cmdcode/buff-utils'\nimport { noble }      from '@cmdcode/crypto-utils'\nimport { hashTx }   "
  },
  {
    "path": "src/lib/sig/segwit/verify.ts",
    "chars": 1673,
    "preview": "import { Buff }       from '@cmdcode/buff-utils'\nimport { noble }      from '@cmdcode/crypto-utils'\nimport { safeThrow }"
  },
  {
    "path": "src/lib/sig/taproot/hash.ts",
    "chars": 6976,
    "preview": "import { Buff }         from '@cmdcode/buff-utils'\nimport * as ENC         from '../../tx/encode.js'\nimport { Tx }      "
  },
  {
    "path": "src/lib/sig/taproot/index.ts",
    "chars": 200,
    "preview": "import { hashTx }   from './hash.js'\nimport { signTx }   from './sign.js'\nimport { verifyTx } from './verify.js'\n\nexport"
  },
  {
    "path": "src/lib/sig/taproot/sign.ts",
    "chars": 4151,
    "preview": "import { Buff, Bytes }  from '@cmdcode/buff-utils'\nimport { Field, Point } from '@cmdcode/crypto-utils'\nimport { hashTx "
  },
  {
    "path": "src/lib/sig/taproot/verify.ts",
    "chars": 2419,
    "preview": "import { Buff, Stream }  from '@cmdcode/buff-utils'\nimport { checkPath }     from '../../tap/key.js'\nimport { verify }  "
  },
  {
    "path": "src/lib/sig/types.ts",
    "chars": 682,
    "preview": "import { Bytes } from '../../schema/types.js'\n\nexport interface HashConfig {\n  extension     ?: Bytes    // Include a ta"
  },
  {
    "path": "src/lib/tap/index.ts",
    "chars": 831,
    "preview": "import * as SCR  from './tree.js'\nimport * as TWK  from './tweak.js'\nimport * as CHK  from './key.js'\n\nexport const TapT"
  },
  {
    "path": "src/lib/tap/key.ts",
    "chars": 4253,
    "preview": "import { Buff, Stream }      from '@cmdcode/buff-utils'\nimport { util }              from '@cmdcode/crypto-utils'\nimport"
  },
  {
    "path": "src/lib/tap/tree.ts",
    "chars": 3002,
    "preview": "import { Buff }              from '@cmdcode/buff-utils'\nimport { TapTree, TapProof } from './types.js'\nimport { Bytes, S"
  },
  {
    "path": "src/lib/tap/tweak.ts",
    "chars": 2006,
    "preview": "import { Buff }         from '@cmdcode/buff-utils'\nimport { Field, Point } from '@cmdcode/crypto-utils'\nimport { getTapT"
  },
  {
    "path": "src/lib/tap/types.ts",
    "chars": 630,
    "preview": "import { Buff } from '@cmdcode/buff-utils'\nimport { Bytes, ScriptData } from '../../schema/types.js'\n\nexport type TapKey"
  },
  {
    "path": "src/lib/tap/utils.ts",
    "chars": 191,
    "preview": "import { Buff, Bytes } from '@cmdcode/buff-utils'\n\nexport function xOnlyPub (key : Bytes) : Buff {\n  const bytes = Buff."
  },
  {
    "path": "src/lib/tx/create.ts",
    "chars": 580,
    "preview": "import { TxData, TxTemplate } from '../../schema/types.js'\n\nconst DEFAULT_TX = {\n  version  : 2,\n  vin      : [],\n  vout"
  },
  {
    "path": "src/lib/tx/decode.ts",
    "chars": 2939,
    "preview": "import { Buff, Stream } from '@cmdcode/buff-utils'\n\nimport {\n  TxData,\n  InputData,\n  OutputData\n} from '../../schema/ty"
  },
  {
    "path": "src/lib/tx/encode.ts",
    "chars": 4037,
    "preview": "import { Buff }         from '@cmdcode/buff-utils'\nimport { encodeScript } from '../script/encode.js'\nimport { createTx "
  },
  {
    "path": "src/lib/tx/format.ts",
    "chars": 942,
    "preview": "import { Buff }     from '@cmdcode/buff-utils'\nimport { isBytes }  from '../check.js'\nimport { decodeTx } from './decode"
  },
  {
    "path": "src/lib/tx/index.ts",
    "chars": 431,
    "preview": "import { encodeTx } from './encode.js'\nimport { decodeTx } from './decode.js'\nimport { TxFmt }    from './format.js'\nimp"
  },
  {
    "path": "src/lib/tx/parse.ts",
    "chars": 3616,
    "preview": "import { Buff }     from '@cmdcode/buff-utils'\nimport { isHex }    from '../check.js'\nimport { Script }   from '../scrip"
  },
  {
    "path": "src/lib/utils.ts",
    "chars": 645,
    "preview": "import { Buff, Bytes } from '@cmdcode/buff-utils'\n\nexport function checkSize (input : Bytes, size : number) : void {\n  c"
  },
  {
    "path": "src/schema/check.ts",
    "chars": 922,
    "preview": "import { z } from 'zod'\n\nconst hexstr  = z.string().regex(/^[a-fA-F0-9]$/)\nconst hash    = z.string().regex(/^[a-fA-F0-9"
  },
  {
    "path": "src/schema/types.ts",
    "chars": 1524,
    "preview": "import { Buff } from '@cmdcode/buff-utils'\n\nexport type Networks   = 'main' | 'testnet' | 'signet' | 'regtest'\n\nexport t"
  },
  {
    "path": "test/bitcoin.conf",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/core.ts",
    "chars": 604,
    "preview": "import { CoreClient, CoreConfig, CoreDaemon } from '@cmdcode/core-cmd'\n\nconst DEFAULT_CONFIG = {\n  core_params : [ '-txi"
  },
  {
    "path": "test/example/ex_test.ts",
    "chars": 717,
    "preview": "import { Test } from 'tape'\nimport { key_spend }    from './taproot/keyspend.test.js'\nimport { script_spend } from './ta"
  },
  {
    "path": "test/example/taproot/inscribe.test.ts",
    "chars": 4388,
    "preview": "import { Test }       from 'tape'\nimport { CoreWallet } from '@cmdcode/core-cmd'\nimport { Buff }       from '@cmdcode/bu"
  },
  {
    "path": "test/example/taproot/keyspend.test.ts",
    "chars": 2824,
    "preview": "\nimport { Test }       from 'tape'\nimport { util }       from '@cmdcode/crypto-utils'\nimport { CoreWallet } from '@cmdco"
  },
  {
    "path": "test/example/taproot/tapscript.test.ts",
    "chars": 3328,
    "preview": "import { Test }       from 'tape'\nimport { util }       from '@cmdcode/crypto-utils'\nimport { CoreWallet } from '@cmdcod"
  },
  {
    "path": "test/example/taproot/taptree.test.ts",
    "chars": 4006,
    "preview": "import { Test }       from 'tape'\nimport { util }       from '@cmdcode/crypto-utils'\nimport { CoreWallet } from '@cmdcod"
  },
  {
    "path": "test/index.html",
    "chars": 1805,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
  },
  {
    "path": "test/scratch.ts",
    "chars": 596,
    "preview": "import fs       from 'fs/promises'\nimport { URL }  from 'url'\nimport { Script, Tx }   from '../src/index.js'\n\nconst txtp"
  },
  {
    "path": "test/src/addr/addr.test.ts",
    "chars": 395,
    "preview": "import { Test }        from 'tape'\nimport { p2pkh_test }  from './p2pkh.test.js'\nimport { p2sh_test }   from './p2sh.tes"
  },
  {
    "path": "test/src/addr/p2pkh.test.ts",
    "chars": 1695,
    "preview": "\nimport { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { Address } from '../../../src/inde"
  },
  {
    "path": "test/src/addr/p2sh.test.ts",
    "chars": 1632,
    "preview": "\nimport { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { Address } from '../../../src/inde"
  },
  {
    "path": "test/src/addr/p2tr.test.ts",
    "chars": 1627,
    "preview": "\nimport { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { Address } from '../../../src/inde"
  },
  {
    "path": "test/src/addr/p2wpkh.test.ts",
    "chars": 1642,
    "preview": "\nimport { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { Address } from '../../../src/inde"
  },
  {
    "path": "test/src/addr/p2wsh.test.ts",
    "chars": 1679,
    "preview": "\nimport { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { Address } from '../../../src/inde"
  },
  {
    "path": "test/src/sig/segwit/bip0143.vectors.json",
    "chars": 11494,
    "preview": "{\n  \"rawhex\": \"010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000"
  },
  {
    "path": "test/src/sig/segwit/sighash.test.ts",
    "chars": 1844,
    "preview": "import { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { secp256k1 as secp } from '@noble/c"
  },
  {
    "path": "test/src/sig/segwit/sighash.vectors.json",
    "chars": 210404,
    "preview": "[\n\t[\"907c2bc503ade11cc3b04eb2918b6f547b0630ab569273824748c87ea14b0696526c66ba740200000004ab65ababfd1f9bdd4ef073c7afc4ae0"
  },
  {
    "path": "test/src/sig/segwit/utils.ts",
    "chars": 1248,
    "preview": "import { Buff } from '@cmdcode/buff-utils'\nimport { InputData } from '../../../../src'\n\ntype TestInput = [\n  prev_hash :"
  },
  {
    "path": "test/src/sig/sig.test.ts",
    "chars": 438,
    "preview": "import { Test } from 'tape'\n\nimport {\n  sighash_vector_test\n} from './segwit/sighash.test.js'\n\nimport {\n  test_computeha"
  },
  {
    "path": "test/src/sig/taproot/sig.test.ts",
    "chars": 4350,
    "preview": "import { Test }    from 'tape'\nimport { Buff }    from '@cmdcode/buff-utils'\nimport { util }    from '@cmdcode/crypto-ut"
  },
  {
    "path": "test/src/sig/taproot/sig.vectors.json",
    "chars": 15306,
    "preview": "{\n    \"txhex\": \"02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b13"
  },
  {
    "path": "test/src/sig/taproot/tx.test.ts",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/src/sig/taproot/tx.vectors.json",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/src/tap/tree.test.ts",
    "chars": 2656,
    "preview": "import { Test } from 'tape'\nimport { Buff } from '@cmdcode/buff-utils'\nimport { Tap }  from '../../../src/index.js'\nimpo"
  },
  {
    "path": "test/src/tap/tree.vectors.json",
    "chars": 8024,
    "preview": "{\n  \"vectors\": [\n    {\n      \"internalPubkey\": \"d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d\",\n     "
  },
  {
    "path": "test/src/tap/unit.test.ts",
    "chars": 2266,
    "preview": "import { Test }         from 'tape'\nimport { Buff }         from '@cmdcode/buff-utils'\nimport test_vectors     from './u"
  },
  {
    "path": "test/src/tap/unit.vectors.json",
    "chars": 2313,
    "preview": "{\n  \"tapleaf\": [\n    [\n      \"029000b275209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803beac\",\n      \"c8"
  },
  {
    "path": "test/src/tx/segwit/segwit.test.ts",
    "chars": 936,
    "preview": "import { Test }  from 'tape'\nimport { Tx }    from '../../../../src/index.js'\nimport test_data from './valid.vectors.jso"
  },
  {
    "path": "test/src/tx/segwit/utils.ts",
    "chars": 1248,
    "preview": "import { Buff } from '@cmdcode/buff-utils'\nimport { InputData } from '../../../../src'\n\ntype TestInput = [\n  prev_hash :"
  },
  {
    "path": "test/src/tx/segwit/valid.vectors.json",
    "chars": 85554,
    "preview": "[\n[\"The following are deserialized transactions which are valid.\"],\n[\"They are in the form\"],\n[\"[[[prevout hash, prevout"
  },
  {
    "path": "test/src/tx/tx.test.ts",
    "chars": 169,
    "preview": "import { Test } from 'tape'\nimport { segwit_vector_test } from './segwit/segwit.test.js'\n\nexport default function tx_tes"
  },
  {
    "path": "test/tape.ts",
    "chars": 683,
    "preview": "import tape from 'tape'\n\nimport address_tests  from './src/addr/addr.test.js'\nimport sig_tests      from './src/sig/sig."
  },
  {
    "path": "test/tsconfig.json",
    "chars": 798,
    "preview": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"..\",\n    \"paths\": {\n      \"@/*\" : [ \"src/*\" ],\n    },\n    \"module\": \"NodeNext\","
  },
  {
    "path": "test/utils.ts",
    "chars": 51,
    "preview": "export class TxTest {\n  constructor () {\n    \n  }\n}"
  },
  {
    "path": "tsconfig.json",
    "chars": 861,
    "preview": "{\n  \"compilerOptions\": {\n    \"module\": \"ES2022\",\n    \"target\": \"ES2022\",\n    \"moduleResolution\": \"Node\",\n    \"rootDir\": "
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the cmdruid/tapscript GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 94 files (15.1 MB), approximately 214.7k tokens, and a symbol index with 289 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!