Resolve "WIP: UTXO Selection and Storage"

This commit is contained in:
Jim Hamill
2025-01-11 08:39:24 +00:00
parent f934216d68
commit 33fd9bac4d
156 changed files with 13304 additions and 4595 deletions
+6
View File
@@ -0,0 +1,6 @@
<script lang="ts" setup>
</script>
<!-- Resume Markdown parsing -->
TODO: Once we have other tools, we might want to turn this into a page like that on Quick Start that presents a "box" of each Tool Group.
@@ -0,0 +1,605 @@
import {
Address,
BlockHeader,
Bytes,
encodeExtendedJson,
decodeExtendedJson,
Hasher,
HDPrivateNode,
HDPublicNode,
Mnemonic,
PrivateKey,
PublicKey,
Satoshis,
Script,
Transaction,
} from '@xocash/primitives';
export const address = {
from: [
{
label: 'From',
inputs: ['CashAddr or Legacy Address'],
models: [''],
callback: (args: [string]) => Address.from(...args),
extendedInput: true,
},
{
label: 'From CashAddr',
inputs: ['CashAddr'],
models: [''],
callback: (args: [string]) => Address.fromCashAddr(...args),
extendedInput: true,
},
{
label: 'From Legacy',
inputs: ['Legacy Address'],
models: [''],
callback: (args: [string]) => Address.fromLegacy(...args),
extendedInput: true,
},
{
label: 'From Redeem Script Hex (P2SH20)',
inputs: ['RedeemScript Hex'],
models: [''],
callback: (args: [string]) =>
Address.fromRedeemScriptHex(...args, 'P2SH20'),
extendedInput: true,
},
{
label: 'From Redeem Script Hex (P2SH32)',
inputs: ['RedeemScript Hex'],
models: [''],
callback: (args: [string]) =>
Address.fromRedeemScriptHex(...args, 'P2SH32'),
extendedInput: true,
},
{
label: 'From Lockscript Hex',
inputs: ['Lockscript Hex'],
models: [''],
callback: (args: [string]) => Address.fromLockscriptHex(...args),
extendedInput: true,
},
{
label: 'From Hash160 Hex',
inputs: ['Hash160 Hex'],
models: [''],
callback: (args: [string]) => Address.fromHash160Hex(...args),
extendedInput: true,
},
],
to: [
{
label: 'To CashAddr (mainnet)',
callback: (address: Address) => address.toCashAddr('bitcoincash'),
},
{
label: 'To CashAddr (testnet)',
callback: (address: Address) => address.toCashAddr('bchtest'),
},
{
label: 'To CashAddr (regtest)',
callback: (address: Address) => address.toCashAddr('bchreg'),
},
{
label: 'To TokenAware CashAddr (mainnet)',
callback: (address: Address) => address.toTokenAddress('bitcoincash'),
},
{
label: 'To TokenAware CashAddr (testnet)',
callback: (address: Address) => address.toTokenAddress('bchtest'),
},
{
label: 'To TokenAware CashAddr (regtest)',
callback: (address: Address) => address.toTokenAddress('bchreg'),
},
{
label: 'To Legacy',
callback: (address: Address) => address.toLegacy(),
},
{
label: 'To Hash160/RIPEMD Hex',
callback: (address: Address) => address.toHash160Hex(),
},
{
label: 'To Lockscript Hex',
callback: (address: Address) => address.toLockscriptHex(),
},
{
label: 'To Lockscript ASM',
callback: (address: Address) => address.toLockscriptAsm(),
},
],
};
export const blockHeader = {
from: [
{
label: 'From Hex',
inputs: ['UTF-8'],
models: [''],
callback: (args: [string]) => BlockHeader.fromHex(...args),
},
],
to: [
{
label: 'To Raw',
callback: (blockHeader: BlockHeader) => blockHeader.toRaw(),
},
{
label: 'Version',
callback: (blockHeader: BlockHeader) => blockHeader.getVersion(),
},
{
label: 'Previous Block Hash',
callback: (blockHeader: BlockHeader) =>
blockHeader.getPreviousBlockHashHex(),
},
{
label: 'Merkle Root',
callback: (blockHeader: BlockHeader) => blockHeader.getMerkleRootHex(),
},
{
label: 'Timestamp',
callback: (blockHeader: BlockHeader) =>
blockHeader.getTimestamp().toString(),
},
{
label: 'Bits',
callback: (blockHeader: BlockHeader) => blockHeader.getBits().toString(),
},
{
label: 'Nonce',
callback: (blockHeader: BlockHeader) => blockHeader.getNonce().toString(),
},
],
};
export const bytes = {
from: [
{
label: 'From Base64',
inputs: ['Base64'],
models: [''],
callback: (args: [string]) => Bytes.fromBase64(...args),
},
{
label: 'From Hex',
inputs: ['Hex'],
models: [''],
callback: (args: [string]) => Bytes.fromHex(...args),
},
{
label: 'From UTF-8',
inputs: ['UTF-8'],
models: [''],
callback: (args: [string]) => Bytes.fromUtf8(...args),
},
],
to: [
{
label: 'To Base64',
callback: (bytes: Bytes) => bytes.toBase64(),
},
{
label: 'To Hex',
callback: (bytes: Bytes) => bytes.toHex(),
},
{
label: 'To UTF-8',
callback: (bytes: Bytes) => bytes.toUtf8(),
},
],
};
export const hasher = {
from: [
{
label: 'From Hex',
inputs: ['Hex'],
models: [''],
callback: (args: [string]) => Hasher.fromHex(...args),
},
{
label: 'From Base64',
inputs: ['Base64'],
models: [''],
callback: (args: [string]) => Hasher.fromBase64(...args),
},
{
label: 'From UTF-8',
inputs: ['UTF-8'],
models: [''],
callback: (args: [string]) => Hasher.fromUTF8(...args),
},
],
to: [
{
label: 'To Sha256 Hex',
callback: (hasher: Hasher) => hasher.toSHA256Hex(),
},
{
label: 'To RipeMD160 Hex',
callback: (hasher: Hasher) => hasher.toRIPEMD160Hex(),
},
{
label: 'To Hash160 Hex',
callback: (hasher: Hasher) => hasher.toHash160Hex(),
},
{
label: 'To Hash256 Hex',
callback: (hasher: Hasher) => hasher.toHash256Hex(),
},
],
};
export const hdPrivateNode = {
from: [
{
label: 'Generate Random',
inputs: ['Derivation Path'],
models: ["m/44'/145'/0'/0/1"],
callback: () => HDPrivateNode.generateRandom(),
},
{
label: 'From Mnemonic',
inputs: ['Mnemonic', 'Derivation Path'],
models: ['', "m/44'/145'/0'/0/1"],
callback: (args: [string]) => HDPrivateNode.fromMnemonic(...args),
},
{
label: 'From XPriv Key',
inputs: ['XPriv Key', 'Derivation Path'],
models: ['', "m/44'/145'/0'/0/1"],
callback: (args: [string]) => HDPrivateNode.fromXPriv(...args),
},
{
label: 'From Raw',
inputs: ['Raw (LibAuth Format/ExtJSON Encoded)'],
models: [''],
callback: (args: [string]) =>
HDPrivateNode.fromRaw(decodeExtendedJson(args.join(''))),
extendedInput: true,
},
],
to: [
{
label: 'To XPriv Key',
callback: (hdPrivateNode: HDPrivateNode) => hdPrivateNode.toXPriv(),
},
{
label: 'To String',
callback: (hdPrivateNode: HDPrivateNode) => hdPrivateNode.toString(),
},
{
label: 'To Raw (LibAuth Format/ExtJSON Encoded)',
callback: (hdPrivateNode: HDPrivateNode) =>
encodeExtendedJson(hdPrivateNode.toRaw(), 2),
},
{
label: 'To XPub Key',
callback: (hdPrivateNode: HDPrivateNode) =>
hdPrivateNode.deriveHDPublicNode().toXPub(),
},
],
};
export const hdPublicNode = {
from: [
{
label: 'From XPub Key',
inputs: ['XPub Key'],
models: [''],
callback: (args: [string]) => HDPublicNode.fromXPub(...args),
},
{
label: 'From Raw',
inputs: ['Raw (LibAuth Format/ExtJSON Encoded)'],
models: [''],
callback: (args: [string]) =>
HDPublicNode.fromRaw(decodeExtendedJson(args.join(''))),
extendedInput: true,
},
],
to: [
{
label: 'To XPub Key',
callback: (hdPublicNode: HDPublicNode) => hdPublicNode.toXPub(),
},
{
label: 'To String',
callback: (hdPublicNode: HDPublicNode) => hdPublicNode.toString(),
},
{
label: 'To Raw (LibAuth Format/ExtJSON Encoded)',
callback: (hdPublicNode: HDPublicNode) =>
encodeExtendedJson(hdPublicNode.toRaw(), 2),
},
],
};
export const mnemonic = {
from: [
{
label: 'Generate Random',
callback: () => Mnemonic.generateRandom(),
},
{
label: 'From String',
inputs: ['String'],
models: [''],
callback: (args: [string]) => Mnemonic.fromString(...args),
extendedInput: true,
},
],
to: [
{
label: 'To String (without passphrase)',
callback: (mnemonic: Mnemonic) => mnemonic.toString(false),
},
{
label: 'To String (with passphrase)',
callback: (mnemonic: Mnemonic) => mnemonic.toString(true),
},
{
label: 'Words',
callback: (mnemonic: Mnemonic) => mnemonic.toWords(),
},
{
label: 'Passphrase',
callback: (mnemonic: Mnemonic) => mnemonic.getPassphrase(),
},
],
};
// TODO: Message Signing outputs. Maybe add an `extension` flag to the `to` that allows another input field and uses that to generate the outputs
export const privateKey = {
from: [
{
label: 'Generate Random',
callback: () => PrivateKey.generateRandom(),
},
{
label: 'From Hex',
inputs: ['Hex'],
models: [''],
callback: (args: [string]) => PrivateKey.fromHex(...args),
},
{
label: 'From WIF',
inputs: ['WIF'],
models: [''],
callback: (args: [string]) => PrivateKey.fromWIF(...args),
},
],
to: [
{
label: 'To Hex',
callback: (privateKey: PrivateKey) => privateKey.toHex(),
},
{
label: 'To WIF',
callback: (privateKey: PrivateKey) => privateKey.toWif(),
},
{
label: 'To String',
callback: (privateKey: PrivateKey) => privateKey.toString(),
},
{
label: 'Derived Public Key (Hex)',
callback: (privateKey: PrivateKey) =>
privateKey.derivePublicKey().toString(),
},
{
label: 'Derived BCH Address (CashAddr)',
callback: (privateKey: PrivateKey) =>
privateKey.derivePublicKey().deriveAddress().toCashAddr('bitcoincash'),
},
],
methods: [
{
label: 'Sign Message (Schnorr)',
inputs: ['UTF8'],
models: ['Your text here'],
callback: (privateKey: PrivateKey, args: [string]) =>
Bytes.fromBytes(privateKey.signMessageSchnorr(...args)).toHex(),
},
{
label: 'Sign Message (DER)',
inputs: ['UTF8'],
models: ['Your text here'],
callback: (privateKey: PrivateKey, args: [string]) =>
Bytes.fromBytes(privateKey.signMessageDER(...args)).toHex(),
},
],
};
export const publicKey = {
from: [
{
label: 'From Hex',
inputs: ['Hex'],
models: [''],
callback: (args: [string]) => PublicKey.fromHex(...args),
},
{
label: 'From WIF',
inputs: ['WIF'],
models: [''],
callback: (args: [string]) =>
PrivateKey.fromWIF(...args).derivePublicKey(),
},
],
to: [
{
label: 'Derived Hash160 Hex',
callback: (publicKey: PublicKey) => publicKey.deriveHash160Hex(),
},
{
label: 'Derived Address',
callback: (publicKey: PublicKey) =>
publicKey.deriveAddress().toCashAddr(),
},
{
label: 'To Hex',
callback: (publicKey: PublicKey) => publicKey.toHex(),
},
{
label: 'To String',
callback: (publicKey: PublicKey) => publicKey.toString(),
},
],
methods: [
{
label: 'Verify Message (Schnorr)',
inputs: ['Message (UTF8)', 'Signature (Hex)'],
models: ['Your text here'],
callback: (publicKey: PublicKey, args: [string, string]) =>
publicKey.verifyMessageSchnorr(...args).toString(),
},
{
label: 'Verify Message (DER)',
inputs: ['Message (UTF8)', 'Signature (Hex)'],
models: ['Your text here'],
callback: (publicKey: PublicKey, args: [string, string]) =>
publicKey.verifyMessageDER(...args).toString(),
},
],
};
export const satoshis = {
from: [
{
label: 'From Sats',
inputs: ['Sats'],
models: [''],
callback: (args: [number]) => Satoshis.fromSats(...args),
},
{
label: 'From Bits',
inputs: ['Bits'],
models: [''],
callback: (args: [number]) => Satoshis.fromBits(...args),
},
{
label: 'From MBCH',
inputs: ['MBCH'],
models: [''],
callback: (args: [number]) => Satoshis.fromMBCH(...args),
},
{
label: 'From BCH',
inputs: ['BCH'],
models: [''],
callback: (args: [number]) => Satoshis.fromBCH(...args),
},
],
to: [
{
label: 'To Sats',
callback: (satoshis: Satoshis) => satoshis.toSats().toString(),
},
{
label: 'To Bits',
callback: (satoshis: Satoshis) => satoshis.toBits().toString(),
},
{
label: 'To MBCH',
callback: (satoshis: Satoshis) => satoshis.toMBCH().toString(),
},
{
label: 'To BCH',
callback: (satoshis: Satoshis) => satoshis.toBCH().toString(),
},
],
};
export const script = {
from: [
{
label: 'From ASM',
inputs: ['ASM'],
models: [''],
callback: (args: [string]) => Script.fromASM(...args),
},
{
label: 'From Hex',
inputs: ['Hex'],
models: [''],
callback: (args: [string]) => Script.fromHex(...args),
},
],
to: [
{
label: 'To ASM',
callback: (script: Script) => script.toASM(),
},
{
label: 'To Hex',
callback: (script: Script) => script.toHex(),
},
],
};
export const transaction = {
from: [
{
label: 'From Hex',
inputs: ['Hex'],
models: [''],
callback: (args: [string]) => Transaction.fromHex(...args),
extendedInput: true,
},
{
label: 'From Raw',
inputs: ['Raw (LibAuth Format/ExtJSON Encoded)'],
models: [''],
callback: (args: [...string[]]) =>
Transaction.fromRaw(decodeExtendedJson(args.join(''))),
extendedInput: true,
},
],
to: [
{
label: 'To Transaction Hash Hex',
callback: (transaction: Transaction) => transaction.toHashHex(),
},
{
label: 'To Raw (LibAuth Format/ExtJSON Encoded)',
callback: (transaction: Transaction) =>
encodeExtendedJson(transaction.toRaw(), 2),
},
{
label: 'To Hex',
callback: (transaction: Transaction) => transaction.toHex(),
},
{
label: 'Version',
callback: (transaction: Transaction) =>
transaction.getVersion().toString(),
},
{
label: 'Inputs',
callback: (transaction: Transaction) => {
return encodeExtendedJson(transaction.getInputs(), 2);
},
},
{
label: 'Outputs',
callback: (transaction: Transaction) => {
return encodeExtendedJson(transaction.getOutputs(), 2);
},
},
{
label: 'Locktime',
callback: (transaction: Transaction) =>
transaction.getLocktime().toString(),
},
{
label: 'Is Coinbase',
callback: (transaction: Transaction) =>
transaction.isCoinbase().toString(),
},
],
};
+146
View File
@@ -0,0 +1,146 @@
<script lang="ts" setup>
import PrimitiveToolVue, { type FromFunction, type ToFunction } from '@theme/components/PrimitiveTool.vue'
// Import our tool functions
import {
address,
blockHeader,
bytes,
hasher,
hdPrivateNode,
hdPublicNode,
mnemonic,
privateKey,
publicKey,
satoshis,
script,
transactionHash,
transaction,
} from './primitive-functions.ts'
</script>
<!-- Resume Markdown parsing -->
# Primitive Tools
## Address
<div class="tool">
<p>A Utility tool for converting between different address formats.</p>
<PrimitiveToolVue
:fromFunctions="address.from"
:toFunctions="address.to"
/>
</div>
## Block Header
<div class="tool">
<p>A utility tool for converting between different byte representations.</p>
<PrimitiveToolVue
:fromFunctions="blockHeader.from"
:toFunctions="blockHeader.to"
/>
</div>
## Bytes
<div class="tool">
<p>A utility tool for converting between different byte representations.</p>
<PrimitiveToolVue
:fromFunctions="bytes.from"
:toFunctions="bytes.to"
/>
</div>
## Hasher
<div class="tool">
<p>A utility tool for computing various cryptographic hashes commonly used in Bitcoin Cash.</p>
<PrimitiveToolVue
:fromFunctions="hasher.from"
:toFunctions="hasher.to"
/>
</div>
## HD Private Node
<div class="tool">
<p>A utility tool for generating and manipulating HD Private Nodes.</p>
<PrimitiveToolVue
:fromFunctions="hdPrivateNode.from"
:toFunctions="hdPrivateNode.to"
/>
</div>
## HD Public Node
<div class="tool">
<p>A utility tool for generating and manipulating HD Public Nodes.</p>
<PrimitiveToolVue
:fromFunctions="hdPublicNode.from"
:toFunctions="hdPublicNode.to"
/>
</div>
## Mnemonic
<div class="tool">
<p>A utility tool for generating and validating BIP39 Mnemonics. Note that mnemonics will be normalized by removing redundant whitespace and will also be validated against the BIP39 English Word List (other word-lists can be specified via the API)</p>
<PrimitiveToolVue
:fromFunctions="mnemonic.from"
:toFunctions="mnemonic.to"
/>
</div>
## Private Key
<div class="tool">
<p>A utility tool for generating and manipulating Private Keys.</p>
<PrimitiveToolVue
:fromFunctions="privateKey.from"
:toFunctions="privateKey.to"
:methods="privateKey.methods"
/>
</div>
## Public Key
<div class="tool">
<p>A utility tool for generating and manipulating Public Keys.</p>
<PrimitiveToolVue
:fromFunctions="publicKey.from"
:toFunctions="publicKey.to"
:methods="publicKey.methods"
/>
</div>
## Satoshis
<div class="tool">
<p>A utility tool for converting between Bitcoin Cash value units.</p>
<PrimitiveToolVue
:fromFunctions="satoshis.from"
:toFunctions="satoshis.to"
/>
</div>
## Script
<div class="tool">
<p>A utility tool for generating and manipulating Script objects.</p>
<PrimitiveToolVue
:fromFunctions="script.from"
:toFunctions="script.to"
/>
</div>
## Transaction
<div class="tool">
<p>A utility tool for generating and manipulating Transaction objects.</p>
<PrimitiveToolVue
:fromFunctions="transaction.from"
:toFunctions="transaction.to"
/>
</div>
@@ -0,0 +1,34 @@
import { Bytes } from '@xocash/primitives';
import { Encryption } from '@xocash/stack';
export const encryption = {
from: [
{
label: 'From Passphrase',
inputs: ['Passphrase'],
models: [''],
callback: (args: [string]) => Encryption.fromPassphrase(...args),
extendedInput: true,
},
],
to: [],
methods: [
{
label: 'Encrypt',
inputs: ['UTF8'],
models: ['Your text here'],
callback: async (encryption: Encryption, args: [string]) =>
Bytes.fromBytes(await encryption.encrypt(...args)).toHex(),
},
{
label: 'Decrypt',
inputs: ['Hex'],
models: ['Encrypted Data (hex)'],
callback: async (encryption: Encryption, args: [string]) =>
Bytes.fromBytes(
await encryption.decrypt(Bytes.fromHex(...args).toBytes()),
).toUtf8(),
},
],
};
+23
View File
@@ -0,0 +1,23 @@
<script lang="ts" setup>
import PrimitiveToolVue, { type FromFunction, type ToFunction } from '@theme/components/PrimitiveTool.vue'
// Import our tool functions
import {
encryption
} from './utils-functions.ts'
</script>
<!-- Resume Markdown parsing -->
# Utils
## Encryption
<div class="tool">
<p>A Utility tool for converting between different address formats.</p>
<PrimitiveToolVue
:fromFunctions="encryption.from"
:toFunctions="encryption.to"
:methods="encryption.methods"
/>
</div>