Signed-off-by: James Zuccon <zuccon@gmail.com>
This commit is contained in:
James Zuccon
2024-09-06 15:54:50 +10:00
parent 9aa43d6e97
commit f66de86eb3
18 changed files with 149 additions and 37 deletions
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

+8 -1
View File
@@ -1,3 +1,10 @@
# React
TODO
## Install
```sh
npm install @xo/react
```
## Usage
+8 -1
View File
@@ -1,3 +1,10 @@
# Svelte
TODO
## Install
```sh
npm install @xo/svelte
```
## Usage
+7 -1
View File
@@ -1,3 +1,9 @@
# Vue
TODO
## Install
```sh
npm install @xo/vue
```
## Usage
+9 -18
View File
@@ -1,3 +1,4 @@
import { Transaction } from '@xo/primitives';
import { Blockchain, WalletHD } from '@xo/wallet';
const blockchain = new Blockchain(
@@ -8,24 +9,6 @@ const blockchain = new Blockchain(
await blockchain.start();
console.log(
await blockchain.request(
'blockchain.address.get_history',
'bitcoincash:qqyy3mss5vmthgnu0m5sm39pcfq8z799ku2nxernca',
861_606,
861_607
)
);
/*
console.log(
await blockchain.request(
'blockchain.transaction.get',
'da776b9c273ec406f7ecd488950216aa17a04018201298f263e2fcdb0b903d29'
)
);
*/
const wallet = WalletHD.fromMnemonic(
'harsh gospel job ordinary garden clay eight cactus replace general prison permit',
blockchain
@@ -33,4 +16,12 @@ const wallet = WalletHD.fromMnemonic(
await wallet.scan();
const transactions = wallet.getTransactions();
transactions.forEach((tx) => {
blockchain.broadcastTransaction(tx);
});
console.log(blockchain.pendingTransactions.deriveUtxoState());
console.log(wallet.privateKey());
+1 -1
View File
@@ -10,7 +10,7 @@
"dist"
],
"scripts": {
"build": "tsc --build --clean",
"build": "tsc --build --clean && tsc --build",
"prepublishOnly": "npm run build"
},
"author": "",
@@ -22,6 +22,35 @@ export class TransactionGraph {
this.transactions.delete(txHash);
}
removeDependentTransactions(txHash: string): string[] {
const removedTxids: string[] = [];
const toRemove: Set<string> = new Set([txHash]);
const findDependents = (id: string) => {
for (const [otherTxid, tx] of this.transactions) {
if (
tx
.getInputs()
.some((input) => binToHex(input.outpointTransactionHash) === id)
) {
toRemove.add(otherTxid);
findDependents(otherTxid);
}
}
};
findDependents(txHash);
for (const id of toRemove) {
if (this.transactions.has(id)) {
this.transactions.delete(id);
removedTxids.push(id);
}
}
return removedTxids;
}
getTransaction(txHash: string): Transaction | undefined {
return this.transactions.get(txHash);
}
@@ -122,4 +151,10 @@ export class TransactionGraph {
return order;
}
getNextTransactionToBroadcast(): Transaction | undefined {
// This is pretty inefficient in the sense we recompile the graph each time.
// But I'm not sure if that would really have much real-world performance impact.
return this.getBroadcastOrder()[0] ?? undefined;
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
"dist"
],
"scripts": {
"build": "tsc --build --clean",
"build": "tsc --build --clean && tsc --build",
"prepublishOnly": "npm run build"
},
"author": "",
+1 -1
View File
@@ -10,7 +10,7 @@
"dist"
],
"scripts": {
"build": "tsc --build --clean",
"build": "tsc --build --clean && tsc --build",
"prepublishOnly": "npm run build"
},
"author": "",
+19 -11
View File
@@ -88,11 +88,8 @@ export class Blockchain {
transaction: Uint8Array | Transaction | string,
opts: { waitUntilSeen: boolean } = { waitUntilSeen: true }
) {
if (typeof transaction === 'string') {
transaction = Transaction.fromHex(transaction);
}
if (transaction instanceof Uint8Array) {
transaction = Transaction.fromBytes(transaction);
if (!(transaction instanceof Transaction)) {
transaction = Transaction.fromAny(transaction);
}
this.pendingTransactions.addTransaction(transaction);
@@ -107,16 +104,27 @@ export class Blockchain {
transaction: Uint8Array | Transaction | string,
opts: { waitUntilSeen: boolean } = { waitUntilSeen: true }
) {
if (typeof transaction === 'string') {
transaction = Transaction.fromHex(transaction);
}
if (transaction instanceof Uint8Array) {
transaction = Transaction.fromBytes(transaction);
if (!(transaction instanceof Transaction)) {
transaction = Transaction.fromAny(transaction);
}
this.pendingTransactions.addTransaction(transaction);
// TODO: Broadcast the transaction.
// Broadcast any pending transaction.
// TODO: Use while loop instead.
for (let tx of this.pendingTransactions.getBroadcastOrder()) {
try {
console.log('broadcasting', tx);
this.pendingTransactions.removeTransaction(tx.toHashHex());
} catch (error) {
this.pendingTransactions.removeDependentTransactions(tx.toHashHex());
throw new Error(
`Broadcast failed for ${transaction.getHashHex()}. Removing dependent transactions.`
);
}
}
// TODO: Wait for transaction to be seen on network.
console.log(opts);
+3
View File
@@ -3,3 +3,6 @@ export * from './blockchain-types.js';
export * from './storage.js';
export * from './wallet-hd.js';
export * from './wallet-p2pkh.js';
export * from './wallet-p2sh20.js';
export * from './wallet-p2sh32.js';
export * from './wallet-set.js';
+8
View File
@@ -6,6 +6,14 @@
* We don't want to extend this - we want to compose it.
* Reason being that we want to support multiple storage adapters.
* And we should allow the possibility of having non-critical storage adapters fail (e.g. remote vs local).
*
* Additionally, consider how we'll store data for WalletSets.
* There will be child wallets. These will have their own data.
* And each child may then have its own wallet. And on and on.
* So, it's a tree hierarchy.
* We can probably emit a "storageUpdated" event that is registered by each parent wallet.
* The parent then "wraps" its own state into this and propagates it up to its parent.
* Then, when it reaches root, the storage adapter stashes all of the propagated data into actual storage.
*/
export class Storage {
constructor() {
+9 -1
View File
@@ -1,5 +1,5 @@
// Import the Stamp class to derive the private keys
import { HDPrivateNode } from '@xo/primitives';
import { HDPrivateNode, Transaction } from '@xo/primitives';
import { EventEmitter } from '../utils/index.js';
import { Blockchain } from './blockchain.js';
import {
@@ -232,6 +232,14 @@ export class WalletHD<
return encodedTransaction;
}
getTransactions() {
// NOTE: We use a set so that we don't end up with duplicate transactions.
return this.wallets.reduce((txs, wallet) => {
wallet.transactions.forEach((tx) => txs.add(tx));
return txs; // Make sure to return the accumulator
}, new Set<Transaction>());
}
}
//-----------------------------------------------------------------------------
@@ -0,0 +1,11 @@
/**
* Placeholder.
*
* @remarks
* TODO Use AddressData
*/
export class WalletP2SH20 {
constructor() {
console.log('TODO');
}
}
@@ -0,0 +1,11 @@
/**
* Placeholder.
*
* @remarks
* TODO Use AddressData
*/
export class WalletP2SH32 {
constructor() {
console.log('TODO');
}
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Placeholder.
*
* @remarks
* One difficult thing here is going to be storage of the subwallets.
* We're going to have child wallets and we need to propagate their storage to the root WalletSet.
* See remarks on Storage - it's semi-related and I'm still playing with approaches.
*/
export class WalletSet {
constructor() {
console.log('TODO');
}
}
+3 -1
View File
@@ -14,13 +14,15 @@
"autoConfiguration": true,
"format": "vitepress",
"pretty": true,
"collapsed": false
"collapsed": true
},
"indexFormat": "table",
"parametersFormat": "table",
"interfacePropertiesFormat": "table",
"classPropertiesFormat": "table",
"propertyMembersFormat": "table",
"typeDeclarationFormat": "table",
"enumMembersFormat": "table",
"useCodeBlocks": true,
"sort": [
"static-first"