Skip to content

Commit c4cacda

Browse files
committed
Final version to CC
1 parent a157b07 commit c4cacda

File tree

1 file changed

+110
-51
lines changed

1 file changed

+110
-51
lines changed

src/app/bitcoin/CoinCointrol.ts

Lines changed: 110 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,153 @@
1-
import * as bitcoin from "bitcoinjs-lib";
1+
import * as bitcoin from 'bitcoinjs-lib';
2+
import { ECPairInterface, ECPairFactory } from 'ecpair';
3+
import * as tinysecp from '@bitcoin-js/tiny-secp256k1-asmjs';
24

5+
// Initialize ECPair with tinysecp256k1-asmjs
6+
const ECPair = ECPairFactory(tinysecp);
7+
8+
// Define interfaces
39
interface UTXO {
410
txid: string;
5-
index: number;
6-
amount: number;
7-
txout: bitcoin.TxOutput;
8-
address: bitcoin.Address;
11+
vout: number;
12+
value: number;
13+
scriptPubKey: Buffer;
14+
address: string;
915
}
1016

1117
interface Wallet {
1218
utxos: UTXO[];
13-
privateKeys: Map<string, bitcoin.ECPairInterface>;
19+
privateKeys: Map<string, ECPairInterface>;
1420
}
1521

22+
// Create a new wallet
1623
function createWallet(): Wallet {
1724
return {
1825
utxos: [],
1926
privateKeys: new Map(),
2027
};
2128
}
2229

23-
function addUTXO(wallet: Wallet, txid: string, index: number, txout: bitcoin.TxOutput, address: bitcoin.Address) {
24-
wallet.utxos.push({ txid, index, txout, address });
25-
}
26-
27-
function removeUTXO(wallet: Wallet, txid: string) {
28-
wallet.utxos = wallet.utxos.filter(utxo => utxo.txid !== txid);
30+
// Add a UTXO to the wallet
31+
function addUTXO(
32+
wallet: Wallet,
33+
txid: string,
34+
vout: number,
35+
value: number,
36+
scriptPubKey: Buffer,
37+
address: string
38+
) {
39+
wallet.utxos.push({ txid, vout, value, scriptPubKey, address });
2940
}
3041

42+
// Select UTXOs sufficient to cover the amount
3143
function selectUTXOs(wallet: Wallet, amount: number): UTXO[] {
32-
let selectedUTXOs: UTXO[] = [];
33-
let totalAmount = 0;
44+
const selectedUTXOs: UTXO[] = [];
45+
let totalValue = 0;
3446

3547
for (const utxo of wallet.utxos) {
36-
totalAmount += utxo.txout.value;
37-
48+
totalValue += utxo.value;
3849
selectedUTXOs.push(utxo);
3950

40-
if (totalAmount >= amount) {
51+
if (totalValue >= amount) {
4152
break;
4253
}
4354
}
4455

56+
if (totalValue < amount) {
57+
throw new Error('Insufficient funds');
58+
}
59+
4560
return selectedUTXOs;
4661
}
4762

48-
function createTransaction(selectedUTXOs: UTXO[], outputs: { address: bitcoin.Address; amount: number }[]): bitcoin.Transaction {
49-
const txb = new bitcoin.TransactionBuilder();
63+
// Create a transaction
64+
function createTransaction(
65+
selectedUTXOs: UTXO[],
66+
outputs: { address: string; value: number }[],
67+
network: bitcoin.Network = bitcoin.networks.bitcoin
68+
): bitcoin.Psbt {
69+
const psbt = new bitcoin.Psbt({ network });
70+
71+
// Add inputs
5072
for (const utxo of selectedUTXOs) {
51-
txb.addInput(utxo.txid, utxo.index);
73+
psbt.addInput({
74+
hash: utxo.txid,
75+
index: utxo.vout,
76+
nonWitnessUtxo: Buffer.from(utxo.scriptPubKey), // Simplified for example
77+
});
5278
}
5379

54-
for (const { address, amount } of outputs) {
55-
txb.addOutput(address, amount);
80+
// Add outputs
81+
for (const output of outputs) {
82+
psbt.addOutput({
83+
address: output.address,
84+
value: output.value,
85+
});
5686
}
5787

58-
return txb.buildIncomplete();
88+
return psbt;
5989
}
6090

61-
function signTransaction(wallet: Wallet, transaction: bitcoin.Transaction, selectedUTXOs: UTXO[]) {
62-
for (let i = 0; i < selectedUTXOs.length; i++) {
63-
const utxo = selectedUTXOs[i];
64-
const key = wallet.privateKeys.get(utxo.address.toString());
65-
if (key) {
66-
transaction.sign(i, key);
91+
// Sign the transaction
92+
function signTransaction(
93+
wallet: Wallet,
94+
psbt: bitcoin.Psbt,
95+
selectedUTXOs: UTXO[]
96+
) {
97+
selectedUTXOs.forEach((utxo, index) => {
98+
const keyPair = wallet.privateKeys.get(utxo.address);
99+
if (!keyPair) {
100+
throw new Error(`No private key found for address: ${utxo.address}`);
67101
}
68-
}
102+
103+
psbt.signInput(index, {
104+
publicKey: Buffer.from(keyPair.publicKey),
105+
sign: (hash: Buffer) => Buffer.from(keyPair.sign(hash))
106+
});
107+
});
108+
psbt.finalizeAllInputs();
109+
return psbt.extractTransaction();
69110
}
70111

71112
// Example usage
72113
const wallet: Wallet = createWallet();
73114

74-
// Add UTXOs to the wallet
75-
// addUTXO(wallet, ...);
76-
77-
// Specify the amount and recipient address
78-
const amount = 10000000; // satoshis
79-
const recipientAddress = "recipient_address"; // should be a valid bitcoin.Address
80-
81-
// Select UTXOs for the transaction
82-
const selectedUTXOs = selectUTXOs(wallet, amount);
83-
84-
// Create transaction outputs
85-
const outputs = [{ address: recipientAddress, amount }];
86-
87-
// Create the transaction
88-
const transaction = createTransaction(selectedUTXOs, outputs);
89-
90-
// Sign the transaction
91-
signTransaction(wallet, transaction, selectedUTXOs);
92-
93-
// Broadcast the transaction
94-
// broadcastTransaction(transaction);
115+
// Generate a key pair for testing
116+
const keyPair = ECPair.makeRandom();
117+
const { address } = bitcoin.payments.p2pkh({ pubkey: Buffer.from(keyPair.publicKey) });
118+
// Add a test private key
119+
wallet.privateKeys.set(address!, keyPair);
120+
121+
// Add a test UTXO
122+
addUTXO(
123+
wallet,
124+
'0000000000000000000000000000000000000000000000000000000000000000',
125+
0,
126+
15000000, // 0.15 BTC in satoshis
127+
Buffer.from('76a914' + '0'.repeat(40) + '88ac', 'hex'), // Dummy scriptPubKey
128+
address!
129+
);
130+
131+
const amount = 10000000; // 0.1 BTC in satoshis
132+
const recipientAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'; // Example recipient (Satoshi's address)
133+
134+
try {
135+
// Select UTXOs
136+
const selectedUTXOs = selectUTXOs(wallet, amount);
137+
138+
// Create transaction outputs
139+
const outputs = [{ address: recipientAddress, value: amount }];
140+
141+
// Create and sign transaction
142+
const psbt = createTransaction(selectedUTXOs, outputs);
143+
const signedTx = signTransaction(wallet, psbt, selectedUTXOs);
144+
145+
// Get the transaction hex
146+
const txHex = signedTx.toHex();
147+
console.log('Transaction hex:', txHex);
148+
149+
// Note: To broadcast, you'd need to use a Bitcoin node API or service
150+
// await broadcastTransaction(txHex);
151+
} catch (error) {
152+
console.error('Transaction failed:', error);
153+
}

0 commit comments

Comments
 (0)