Activities/Wallet Refactor
This commit is contained in:
@@ -1,29 +1,21 @@
|
||||
import { BaseWallet } from './base-wallet.js';
|
||||
import { type WalletTransactions, type TransactionTemplate } from './types.js';
|
||||
import { type TransactionTemplate, type WalletDependencies } from './types.js';
|
||||
import { TransactionBuilder } from './transaction-builder.js';
|
||||
import { type WalletServices } from './types.js';
|
||||
|
||||
import { type BaseBlockchain, type BlockchainTransaction } from '../blockchain';
|
||||
import { type BaseStore, Activities, StoreInMemory } from '../storage';
|
||||
|
||||
import {
|
||||
type BaseBlockchain,
|
||||
type BlockchainTransaction,
|
||||
type BlockchainUTXOs,
|
||||
} from '../blockchain';
|
||||
import { type BaseStore, StoreInMemory } from '../storage';
|
||||
|
||||
import {
|
||||
Address,
|
||||
ExtMap,
|
||||
PrivateKey,
|
||||
calculateBalanceSats,
|
||||
calculateBalanceTokens,
|
||||
encodeExtendedJson,
|
||||
} from '@xocash/primitives';
|
||||
|
||||
import {
|
||||
type Input,
|
||||
binToHex,
|
||||
hash256,
|
||||
sha256,
|
||||
utf8ToBin,
|
||||
walletTemplateP2pkhNonHd,
|
||||
walletTemplateToCompilerBCH,
|
||||
@@ -34,12 +26,6 @@ export type WalletP2PKHGenesisData = {
|
||||
privateKey: string;
|
||||
};
|
||||
|
||||
export type WalletP2PKHEvents = {
|
||||
transactionsUpdated: WalletTransactions;
|
||||
unspentsUpdated: BlockchainUTXOs;
|
||||
stateUpdated: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @example
|
||||
* ```ts
|
||||
@@ -50,58 +36,28 @@ export type WalletP2PKHEvents = {
|
||||
* await walletP2PKH.startMonitoring();
|
||||
* ```
|
||||
*/
|
||||
export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
// Internal State.
|
||||
public isStarted = false;
|
||||
public status: string | null = null;
|
||||
|
||||
export class WalletP2PKH extends BaseWallet {
|
||||
static async from<T extends typeof WalletP2PKH>(
|
||||
this: T,
|
||||
services: WalletServices,
|
||||
genesisData?: WalletP2PKHGenesisData,
|
||||
dependencies: WalletDependencies,
|
||||
genesisData: WalletP2PKHGenesisData,
|
||||
) {
|
||||
// Assign in memory stores as defaults in case otherwise not provided.
|
||||
const servicesFinal = {
|
||||
const dependenciesFinal = {
|
||||
...{
|
||||
store: new StoreInMemory(),
|
||||
activities: await Activities.fromStores(StoreInMemory.from()),
|
||||
cache: new StoreInMemory(),
|
||||
},
|
||||
...services,
|
||||
...dependencies,
|
||||
};
|
||||
|
||||
// If we are attempting to create a new wallet...
|
||||
if (genesisData) {
|
||||
// Ensure that the provided store does not already contain data.
|
||||
if (await servicesFinal.store.get('genesis')) {
|
||||
throw new Error(`Provided store already contains data`);
|
||||
}
|
||||
|
||||
// Store the wallet genesis data.
|
||||
await servicesFinal.store.set<WalletP2PKHGenesisData>(
|
||||
'genesis',
|
||||
genesisData,
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve our genesis data and extract the private key.
|
||||
const retrievedGenesisData =
|
||||
await servicesFinal.store.getOrFail<WalletP2PKHGenesisData>('genesis');
|
||||
|
||||
// Get the Private Key as hex.
|
||||
const privateKey = PrivateKey.fromHex(retrievedGenesisData.privateKey);
|
||||
|
||||
// Return a new instance of Wallet P2PKH.
|
||||
return new this(
|
||||
servicesFinal.blockchain,
|
||||
servicesFinal.store,
|
||||
servicesFinal.cache,
|
||||
privateKey,
|
||||
) as InstanceType<T>;
|
||||
return new this(dependenciesFinal, genesisData) as InstanceType<T>;
|
||||
}
|
||||
|
||||
static fromBytes<T extends typeof WalletP2PKH>(
|
||||
this: T,
|
||||
services: WalletServices,
|
||||
services: WalletDependencies,
|
||||
privateKeyBytes: Uint8Array,
|
||||
) {
|
||||
// Create a Private Key entity from the provided bytes.
|
||||
@@ -116,7 +72,7 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
|
||||
static fromHex<T extends typeof WalletP2PKH>(
|
||||
this: T,
|
||||
services: WalletServices,
|
||||
services: WalletDependencies,
|
||||
privateKeyHex: string,
|
||||
) {
|
||||
// Create a Private Key entity from the provided bytes.
|
||||
@@ -131,7 +87,7 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
|
||||
static fromWIF<T extends typeof WalletP2PKH>(
|
||||
this: T,
|
||||
services: WalletServices,
|
||||
services: WalletDependencies,
|
||||
privateKeyWif: string,
|
||||
) {
|
||||
// Create a Private Key entity from the provided bytes.
|
||||
@@ -146,7 +102,7 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
|
||||
static generateRandom<T extends typeof WalletP2PKH>(
|
||||
this: T,
|
||||
services: WalletServices,
|
||||
services: WalletDependencies,
|
||||
) {
|
||||
// Create a Private Key entity generated from random bytes.
|
||||
const privateKey = PrivateKey.generateRandom();
|
||||
@@ -158,17 +114,37 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
});
|
||||
}
|
||||
|
||||
// Dependencies.
|
||||
public readonly activities: Activities;
|
||||
public readonly blockchain: BaseBlockchain;
|
||||
public readonly cache: BaseStore;
|
||||
|
||||
// Genesis Data.
|
||||
public readonly genesisData: WalletP2PKHGenesisData;
|
||||
|
||||
// Optimizations/Simplifications.
|
||||
public readonly privateKey: PrivateKey;
|
||||
|
||||
// Mutable State.
|
||||
public isStarted = false;
|
||||
public status: string | null = null;
|
||||
|
||||
constructor(
|
||||
// Dependencies.
|
||||
public blockchain: BaseBlockchain,
|
||||
// Persistent Storage.
|
||||
public store: BaseStore,
|
||||
// Cache.
|
||||
public cache: BaseStore,
|
||||
// Properties.
|
||||
public privateKey: PrivateKey,
|
||||
dependencies: Required<WalletDependencies>,
|
||||
genesisData: WalletP2PKHGenesisData,
|
||||
) {
|
||||
super();
|
||||
|
||||
// Assign our dependencies.
|
||||
this.activities = dependencies.activities;
|
||||
this.blockchain = dependencies.blockchain;
|
||||
this.cache = dependencies.cache;
|
||||
|
||||
// Assign our Genesis Data.
|
||||
this.genesisData = genesisData;
|
||||
|
||||
// Optimizations/Simplifications.
|
||||
this.privateKey = PrivateKey.fromHex(genesisData.privateKey);
|
||||
}
|
||||
|
||||
async getId() {
|
||||
@@ -180,6 +156,17 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
);
|
||||
}
|
||||
|
||||
async deriveKey(keyName: string): Promise<Uint8Array> {
|
||||
// Hash the genesis data using Hash256 and the key ("WalletID") for this derivation using Hash256.
|
||||
const genesisDataHashed = hash256(
|
||||
utf8ToBin(JSON.stringify(this.genesisData)),
|
||||
);
|
||||
const keyHashed = hash256(utf8ToBin(keyName));
|
||||
|
||||
// Combine the hashes and do one final hash for good measure.
|
||||
return hash256(new Uint8Array([...genesisDataHashed, ...keyHashed]));
|
||||
}
|
||||
|
||||
async getEncryptionKey() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return await this.cache.getOrSet('encryptionKey', async () =>
|
||||
@@ -194,6 +181,10 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
);
|
||||
}
|
||||
|
||||
async getActivities(): Promise<Activities> {
|
||||
return this.activities;
|
||||
}
|
||||
|
||||
async start() {
|
||||
// Do nothing if already started.
|
||||
if (this.isStarted) {
|
||||
@@ -396,22 +387,15 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
// Fetch unspents that are usable by this wallet.
|
||||
const unspents = await this.getUnspentDirectives();
|
||||
|
||||
// Create an instance of our transaction builder.
|
||||
const txBuilder = new TransactionBuilder(txTemplateFinal, unspents);
|
||||
|
||||
// Append necessary unspents to satisfy amount requirements.
|
||||
// NOTE: The order here is very important.
|
||||
// We must deal with tokens first and satoshis last.
|
||||
// The reason is that the change output will be determined based on transaction fee.
|
||||
// TODO: Consider replacing this with just a single util function.
|
||||
txBuilder
|
||||
.addTokensFromUnspents()
|
||||
.addTokenChangeOutputs(changeLockingBytecode)
|
||||
.addSatoshisFromUnspents()
|
||||
.addSatoshiChangeOutput(changeLockingBytecode);
|
||||
// Build the transaction.
|
||||
const tx = TransactionBuilder.build(
|
||||
txTemplateFinal,
|
||||
unspents,
|
||||
changeLockingBytecode,
|
||||
);
|
||||
|
||||
// Generate the transaction.
|
||||
const generatedTxBytes = txBuilder.toBytes();
|
||||
const generatedTxBytes = tx.toBytes();
|
||||
|
||||
return generatedTxBytes;
|
||||
}
|
||||
@@ -434,34 +418,6 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
this.emit('stateUpdated', this.status);
|
||||
}
|
||||
}
|
||||
|
||||
async export() {
|
||||
const allData = await this.store.all();
|
||||
const asJson = encodeExtendedJson(allData);
|
||||
return asJson;
|
||||
}
|
||||
|
||||
async addActivity<T>(activity: T) {
|
||||
// Generate an Activity ID.
|
||||
// TODO: Look more into how LibAuth intends to do this.
|
||||
const activityId = Date.now().toString();
|
||||
|
||||
// Store the data.
|
||||
await this.store.set<T>(activityId, activity);
|
||||
}
|
||||
|
||||
private async deriveKey(keyName: string): Promise<Uint8Array> {
|
||||
// Get the genesis data for this wallet.
|
||||
// TODO: This needs to change!
|
||||
const genesisData = await this.store.getOrFail('genesis');
|
||||
|
||||
// Hash the genesis data using Hash256 and the key ("WalletID") for this derivation using Hash256.
|
||||
const genesisDataHashed = hash256(utf8ToBin(JSON.stringify(genesisData)));
|
||||
const keyHashed = hash256(utf8ToBin(keyName));
|
||||
|
||||
// Combine the hashes and do one final hash for good measure.
|
||||
return hash256(new Uint8Array([...genesisDataHashed, ...keyHashed]));
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -469,13 +425,13 @@ export class WalletP2PKH extends BaseWallet<WalletP2PKHEvents> {
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
export type WalletP2PKHFactory<T extends WalletP2PKH = WalletP2PKH> = (
|
||||
services: WalletServices,
|
||||
genesisData?: WalletP2PKHGenesisData,
|
||||
dependencies: WalletDependencies,
|
||||
genesisData: WalletP2PKHGenesisData,
|
||||
) => Promise<T>;
|
||||
|
||||
export async function useWalletP2PKH<T extends WalletP2PKH = WalletP2PKH>(
|
||||
services: WalletServices,
|
||||
genesisData?: WalletP2PKHGenesisData,
|
||||
dependencies: WalletDependencies,
|
||||
genesisData: WalletP2PKHGenesisData,
|
||||
): Promise<T> {
|
||||
return (await WalletP2PKH.from(services, genesisData)) as T;
|
||||
return (await WalletP2PKH.from(dependencies, genesisData)) as T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user