Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 + AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui (React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and push.sh deploy script for digibyte.x/web.
107 lines
4.2 KiB
TypeScript
107 lines
4.2 KiB
TypeScript
// End-to-end demo:
|
|
// seed → derive key → build PSBT spending a mock UTXO
|
|
// → sign → finalize → print raw tx hex + txid
|
|
//
|
|
// The mock UTXO cannot be broadcast (its txid doesn't exist on-chain),
|
|
// but every other step is real: the signature is cryptographically
|
|
// valid for the constructed input/output pair. This proves the
|
|
// PSBT machinery works end-to-end against DGB network params.
|
|
|
|
import {
|
|
seedFromPhrase,
|
|
rootFromSeed,
|
|
accountNode,
|
|
addressNode,
|
|
p2wpkhAddress,
|
|
digibyte,
|
|
} from '@dgb-wallet/core';
|
|
import { payments } from 'bitcoinjs-lib';
|
|
import { ECPairFactory } from 'ecpair';
|
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
import { buildPsbt, signAllInputs, finalizeAndExtract, feeSats, feeRateSatsPerByte, type Utxo } from '../src/index.js';
|
|
|
|
const ECPair = ECPairFactory(ecc);
|
|
|
|
// Public BIP39 test vector — safe to hardcode anywhere.
|
|
const TEST_SEED = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
|
|
console.log('\n=== DigiByte PSBT End-to-End Demo ===\n');
|
|
console.log(`Test seed (public BIP39 vector): "${TEST_SEED}"\n`);
|
|
|
|
const seed = await seedFromPhrase(TEST_SEED);
|
|
const root = rootFromSeed(seed);
|
|
|
|
// BIP84 native-SegWit account, first receive address = sender.
|
|
const account = accountNode(root, 84, 0);
|
|
const senderNode = addressNode(account, 0, 0);
|
|
const senderAddress = p2wpkhAddress(senderNode);
|
|
|
|
// Get the signing key. We need an ECPair with the WIF for signing;
|
|
// build one from the BIP32 private key.
|
|
if (!senderNode.privateKey) throw new Error('missing private key on derived node');
|
|
const keyPair = ECPair.fromPrivateKey(Buffer.from(senderNode.privateKey), { network: digibyte });
|
|
|
|
// Recipient — next receive address in the same account (chain=0, index=1).
|
|
const recipientNode = addressNode(account, 0, 1);
|
|
const recipientAddress = p2wpkhAddress(recipientNode);
|
|
|
|
// Change back to sender for simplicity in the demo (real wallet would
|
|
// use a fresh change address on chain=1).
|
|
const changeAddress = senderAddress;
|
|
|
|
// Compute the scriptPubKey for the sender address; needed as
|
|
// witnessUtxo.script for a SegWit input.
|
|
const senderP2wpkh = payments.p2wpkh({
|
|
pubkey: Buffer.from(senderNode.publicKey),
|
|
network: digibyte,
|
|
});
|
|
if (!senderP2wpkh.output) throw new Error('failed to derive sender scriptPubKey');
|
|
const senderScriptHex = senderP2wpkh.output.toString('hex');
|
|
|
|
// Mock UTXO: pretend the sender has a 1.0 DGB UTXO at txid ...42, vout 0.
|
|
const MOCK_TXID = '0000000000000000000000000000000000000000000000000000000000000042';
|
|
const utxo: Utxo = {
|
|
txid: MOCK_TXID,
|
|
vout: 0,
|
|
value: 100_000_000, // 1.0 DGB in sats
|
|
address: senderAddress,
|
|
scriptPubKey: senderScriptHex,
|
|
witness: { scriptHex: senderScriptHex, value: 100_000_000 },
|
|
};
|
|
|
|
// Spend plan: send 0.5 DGB to recipient, keep 0.4999 DGB as change
|
|
// (0.0001 DGB fee = 10,000 sats).
|
|
const SEND_AMOUNT = 50_000_000;
|
|
const CHANGE_AMOUNT = 49_990_000;
|
|
const FEE = utxo.value - SEND_AMOUNT - CHANGE_AMOUNT;
|
|
|
|
console.log('Inputs:');
|
|
console.log(` ${senderAddress}`);
|
|
console.log(` ${MOCK_TXID}:${utxo.vout} = ${utxo.value / 1e8} DGB (mock)`);
|
|
console.log('\nOutputs:');
|
|
console.log(` recipient ${recipientAddress} ${SEND_AMOUNT / 1e8} DGB`);
|
|
console.log(` change ${changeAddress} ${CHANGE_AMOUNT / 1e8} DGB`);
|
|
console.log(` fee ${FEE / 1e8} DGB\n`);
|
|
|
|
// Build → sign → finalize.
|
|
const psbt = buildPsbt({
|
|
inputs: [utxo],
|
|
outputs: [
|
|
{ address: recipientAddress, value: SEND_AMOUNT },
|
|
{ address: changeAddress, value: CHANGE_AMOUNT },
|
|
],
|
|
});
|
|
|
|
signAllInputs(psbt, keyPair);
|
|
const { hex, txid } = finalizeAndExtract(psbt);
|
|
|
|
console.log('Signed transaction:');
|
|
console.log(` txid: ${txid}`);
|
|
console.log(` fee: ${feeSats(psbt)} sats (${feeRateSatsPerByte(psbt).toFixed(2)} sat/vB)`);
|
|
console.log(` hex: ${hex}\n`);
|
|
|
|
console.log('Notes:');
|
|
console.log(' - The UTXO is synthetic; this tx cannot be broadcast.');
|
|
console.log(' - Signature IS cryptographically valid for the input/output pair.');
|
|
console.log(' - Feed a real UTXO (from scantxoutset, Blockbook, or Electrum-DGB)');
|
|
console.log(' and this exact code path produces a broadcastable transaction.\n');
|