XO Wallet Integration Part IV
This commit is contained in:
+58
-2
@@ -35,6 +35,62 @@ A table of common prefixes (conventions) is below:
|
||||
| revert | Reverts a previous commit |
|
||||
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
These guidelines represent best practices. While exceptions may occasionally be necessary as "the lesser evil," strive to adhere to these principles whenever possible.
|
||||
|
||||
1. Try to minimize state
|
||||
|
||||
State is a complexity multiplier. Keep mutable class members to an absolute minimum, as each stateful property exponentially increases cognitive load and potential for bugs. When designing classes or components, first attempt a stateless approach, adding mutable properties only when demonstrably necessary.
|
||||
|
||||
2. Try to avoid `any` types
|
||||
|
||||
Use `unknown` instead, as it provides better type safety while maintaining flexibility when needed.
|
||||
|
||||
3. Prefer immutable patterns
|
||||
|
||||
Use immutable data structures where possible to improve code predictability and testability.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
// GOOD:
|
||||
const mappedData = originalData.map((item) => item.field);
|
||||
|
||||
// AVOID:
|
||||
const mappedData = [];
|
||||
|
||||
for(let i = 0; i < originalData.length; i++) {
|
||||
mappedData.push(originalData[i].field);
|
||||
}
|
||||
```
|
||||
|
||||
Do note that there are some cases where it may make more sense to mutate data inline (e.g. as an optimization for very large data structures).
|
||||
|
||||
Use your best judgement, but treat immutability as the general rule.
|
||||
|
||||
4. Avoid Typescript, Linter and Formatter Directives
|
||||
|
||||
These exceptional cases should be minimized as they:
|
||||
- Create inconsistency in code standards
|
||||
- Make maintenance more difficult
|
||||
- Often mask underlying issues that should be properly resolved
|
||||
- Can lead to unintended side-effects throughout the codebase
|
||||
|
||||
4. Avoid additional dependencies.
|
||||
|
||||
Try to avoid pulling in additional packages. Not only can this bloat the project, but can also cause compatibility issues between environments (Web, Node, ESM, etc). We want `@xocash/stack` to "just work" without devs having to tailor build configurations.
|
||||
|
||||
Obviously this is an ideal: Sometimes it is simply not feasible or sensible to rewrite complex functionality that can be better done by a well-built package. However, please try to adhere to the following:
|
||||
|
||||
- Ensure the package is of high quality (well-written, well-tested).
|
||||
- Does not drag in additional dependencies itself (note that `devDependencies` are fine. These will not be installed when leveraged from our packages).
|
||||
- If the package being used **is heavy** and we have no other choice but to use it, consider whether we should package it separately to avoid problems. For example, if we build a `BlockchainBCHN` adapter, we will likely want to package this on its own under `@xocash/blockchain-bchn` as it only works in NodeJS environments.
|
||||
|
||||
5. "When in Rome..."
|
||||
|
||||
For conventions not explicitly mentioned, follow the "when in Rome" principle. Try to identify and use the patterns and conventions that are already established elsewhere in the codebase.
|
||||
|
||||
## Patterns
|
||||
|
||||
### Dependency Injection
|
||||
@@ -77,7 +133,7 @@ class SomeClass {
|
||||
|
||||
### Factory Patterns
|
||||
|
||||
When creating classes, create static `from(...)` (or `fromSpecificThing(...)`) methods.
|
||||
When creating classes, create static `from(...)` (or `fromSpecificThing(...)`) methods and place these at the top of the class (so that they appear first in the generated documentation).
|
||||
|
||||
In Javascript, a `constructor` cannot be asynchronous and we want to ensure that we can `await` class instantiation to mitigate against stateful errors.
|
||||
|
||||
@@ -223,7 +279,7 @@ Avoid revalidation at every code-path: This is not only inefficient, but a giant
|
||||
|
||||
```ts
|
||||
// You can accept other types (this is a nice convenience).
|
||||
broadcastTransaction(transaction: Transaction | Uint8Array): Transaction {
|
||||
broadcastTransaction(transaction: Transaction | Uint8Array | string): Transaction {
|
||||
// ... but always cast to the Primitive (to ensure correctness).
|
||||
if(!(transaction instanceof Transaction) {
|
||||
transaction = Transaction.from(transaction);
|
||||
|
||||
@@ -4,11 +4,13 @@ import typedocSidebar from '../api/typedoc-sidebar.json';
|
||||
|
||||
// Special cases map for acronyms and exceptions
|
||||
const SPECIAL_CASES = {
|
||||
cryptocompare: 'CryptoCompare',
|
||||
hd: 'HD',
|
||||
http: 'HTTP',
|
||||
indexeddb: 'IndexedDB',
|
||||
inmemory: 'InMemory',
|
||||
json: 'JSON',
|
||||
localstorage: 'LocalStorage',
|
||||
p2p: 'P2P',
|
||||
p2pkh: 'P2PKH',
|
||||
p2sh: 'P2SH',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { BaseBlockchain } from './base-blockchain.js';
|
||||
import {
|
||||
type AddressStatusPayload,
|
||||
type BlockHeightPayload,
|
||||
type BlockchainCallback,
|
||||
type BlockchainTransaction,
|
||||
type BlockchainUTXO,
|
||||
@@ -54,16 +56,6 @@ export type BlockchainElectrumParams = {
|
||||
opts?: Partial<BlockchainElectrumOpts>;
|
||||
};
|
||||
|
||||
export type AddressStatusPayload = {
|
||||
type: 'address-status';
|
||||
status: string | null;
|
||||
};
|
||||
|
||||
export type BlockHeightPayload = {
|
||||
type: 'block-height';
|
||||
height: number | null;
|
||||
};
|
||||
|
||||
export class BlockchainElectrum extends BaseBlockchain<
|
||||
AddressStatusPayload,
|
||||
BlockHeightPayload
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// NOTE: Do not export specific providers in this file.
|
||||
// Do this in the parent index.ts file instead (otherwise, we mingle interfaces and providers, which can create circular dependencies)..
|
||||
// The exception to this rule is with the InMemory and Composite providers.
|
||||
// These are considered built-ins and do not have any other interface dependencies.
|
||||
export * from './base-blockchain.js';
|
||||
export * from './blockchain-electrum.js';
|
||||
export * from './blockchain-inmemory.js';
|
||||
export * from './traits.js';
|
||||
export * from './types.js';
|
||||
|
||||
@@ -32,3 +32,14 @@ export type BlockchainUTXO = {
|
||||
|
||||
// NOTE: To minimize refactors later, use this name elsewhere; not the `ExtMap<Type>` form.
|
||||
export type BlockchainUTXOs = ExtMap<BlockchainUTXO>;
|
||||
|
||||
// NOTE: These payloads are used by Electrum.
|
||||
export type AddressStatusPayload = {
|
||||
type: 'address-status';
|
||||
status: string | null;
|
||||
};
|
||||
|
||||
export type BlockHeightPayload = {
|
||||
type: 'block-height';
|
||||
height: number | null;
|
||||
};
|
||||
|
||||
@@ -7,3 +7,16 @@ export * from './rates/index.js';
|
||||
export * from './storage/index.js';
|
||||
export * from './utils/index.js';
|
||||
export * from './wallet/index.js';
|
||||
|
||||
// Export providers that work across both Browser and NodeJS.
|
||||
// NOTE: For browser-only providers, export these from index.browser.ts.
|
||||
// For node-only providers, export these from index.node.ts.
|
||||
export * from './blockchain/blockchain-electrum.js';
|
||||
export * from './storage/store-remote-http.js';
|
||||
export * from './rates/rates-bitpay.js';
|
||||
export * from './rates/rates-coinbase.js';
|
||||
export * from './rates/rates-cryptocompare.js';
|
||||
export * from './wallet/wallet-hd.js';
|
||||
export * from './wallet/wallet-p2pkh.js';
|
||||
export * from './wallet/wallet-p2sh20.js';
|
||||
export * from './wallet/wallet-p2sh32.js';
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Export all of our stack-specific modules.
|
||||
// NOTE: This file exists as an entry point for generating our documentation.
|
||||
// In our documentation, we want to include both Browser and Node-specific providers.
|
||||
// TODO: For clarity, consider whether we should rename this to index.docs.ts.
|
||||
export * from './index.browser.js';
|
||||
export * from './index.node.js';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// NOTE: Do not export specific providers in this file.
|
||||
// Do this in the parent index.ts file instead (otherwise, we mingle interfaces and providers, which can create circular dependencies)..
|
||||
// The exception to this rule is with the InMemory and Composite providers.
|
||||
// These are considered built-ins and do not have any other interface dependencies.
|
||||
export * from './base-rates.js';
|
||||
export * from './rates-bitpay.js';
|
||||
export * from './rates-coinbase.js';
|
||||
export * from './rates-cryptocompare.js';
|
||||
export * from './rates-composite.js';
|
||||
export * from './traits.js';
|
||||
|
||||
@@ -23,18 +23,22 @@ export interface Activity {
|
||||
}
|
||||
|
||||
// Define the snapshot structure
|
||||
export type SnapshotByType = {
|
||||
[type: string]: {
|
||||
export type SnapshotState = {
|
||||
[key: string]: {
|
||||
[entityId: string]: Activity;
|
||||
};
|
||||
};
|
||||
|
||||
export type ActivitiesIndexedByKey<T extends Activity = Activity> = {
|
||||
[entityId: string]: T;
|
||||
export type SnapshotData<T extends Activity = Activity> = {
|
||||
[keyOrEntityId: string]: T;
|
||||
};
|
||||
|
||||
export type ActivitiesIndexedByEntityId<T extends Activity = Activity> = {
|
||||
[key: string]: T;
|
||||
export type SnapshotIndexedByKey<T extends Activity = Activity> = {
|
||||
[key: string]: { [entityId: string]: T };
|
||||
};
|
||||
|
||||
export type SnapshotIndexedByEntityId<T extends Activity = Activity> = {
|
||||
[entityId: string]: { [key: string]: T };
|
||||
};
|
||||
|
||||
// Policy type definition
|
||||
@@ -45,6 +49,7 @@ export type PolicyFunction = (
|
||||
|
||||
export type ActivitiesEvents = {
|
||||
snapshotUpdated: undefined;
|
||||
updated: { activity: Activity };
|
||||
[key: `updated:${string}`]: { activity: Activity };
|
||||
};
|
||||
|
||||
@@ -79,9 +84,15 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
// TODO: If you can refactor this around your new Storage mechanism, things become a lot simpler:
|
||||
// 1. You don't need to define such a clunky snapshot format because you have indexes.
|
||||
// 2. Caching of snapshots likely becomes possible (because you can use a storage adapter).
|
||||
public snapshot: SnapshotByType = {};
|
||||
private policies = new Map<string, PolicyFunction>();
|
||||
private storeKeyMap = new Map<string, string>();
|
||||
public snapshot: SnapshotState = {};
|
||||
|
||||
// Indexes.
|
||||
// TODO: We want to be able to remove these in favor of the TODO note above.
|
||||
// public snapshotIndexedByEntity: SnapshotIndexedByEntityId = {};
|
||||
// public snapshotIndexedByKey: SnapshotIndexedByKey = {};
|
||||
|
||||
protected policies = new Map<string, PolicyFunction>();
|
||||
protected storeKeyMap = new Map<string, string>();
|
||||
|
||||
/**
|
||||
* Create an Activities instance from the given composite store.
|
||||
@@ -93,7 +104,7 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
return activities;
|
||||
}
|
||||
|
||||
private constructor(store: StoreComposite) {
|
||||
protected constructor(store: StoreComposite) {
|
||||
super();
|
||||
|
||||
// Assign our dependencies.
|
||||
@@ -167,6 +178,10 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
return removedActivity;
|
||||
}
|
||||
|
||||
public getSnapshot() {
|
||||
return this.snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific activity by type and id with type inference
|
||||
*/
|
||||
@@ -178,12 +193,23 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
return activity as T | undefined;
|
||||
}
|
||||
|
||||
public getByEntity(entityId: string = '') {
|
||||
return Object.keys(this.snapshot).reduce(
|
||||
(result, key) => {
|
||||
const activity = this.snapshot[key][entityId];
|
||||
if (activity) {
|
||||
result[key] = activity;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
{} as Record<string, Activity>,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all activities of a specific type
|
||||
*/
|
||||
public getByKey<T extends Activity>(
|
||||
key: T['key'],
|
||||
): ActivitiesIndexedByKey<T> {
|
||||
public getByKey<T extends Activity>(key: T['key']): SnapshotData<T> {
|
||||
const activities = this.snapshot[key];
|
||||
if (!activities) {
|
||||
return {};
|
||||
@@ -191,9 +217,25 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
|
||||
// NOTE: We want to return a new object, not a reference to our current object.
|
||||
// But we only need to "clone" the first level as each activity itself is immutable.
|
||||
return { ...activities } as ActivitiesIndexedByKey<T>;
|
||||
return { ...activities } as SnapshotData<T>;
|
||||
}
|
||||
|
||||
/*
|
||||
public getByEntityId<T extends Activity>(
|
||||
entityId: string = '',
|
||||
): SnapshotData<T> {
|
||||
const activities = this.snapshotIndexedByEntity[entityId];
|
||||
|
||||
if (!activities) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// NOTE: We want to return a new object, not a reference to our current object.
|
||||
// But we only need to "clone" the first level as each activity itself is immutable.
|
||||
return { ...activities } as SnapshotData<T>;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a policy for handling conflicts between activities of the same type
|
||||
* @param type Activity type to register policy for
|
||||
@@ -239,6 +281,9 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
}
|
||||
this.storeKeyMap.set(snapshotKey, storeKey);
|
||||
|
||||
// Emit the updated activity.
|
||||
this.emit('updated', { activity });
|
||||
|
||||
// Emit the updated activity.
|
||||
this.emit(`updated:${activity.key}`, { activity });
|
||||
|
||||
@@ -254,7 +299,7 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
* Compile a full snapshot from scratch
|
||||
* This happens automatically on initialization
|
||||
*/
|
||||
private async compileSnapshot(): Promise<SnapshotByType> {
|
||||
private async compileSnapshot(): Promise<SnapshotState> {
|
||||
// Clear existing snapshot
|
||||
this.snapshot = {};
|
||||
this.storeKeyMap.clear();
|
||||
@@ -297,11 +342,33 @@ export class Activities extends EventEmitter<ActivitiesEvents> {
|
||||
* Set an activity in the snapshot, creating necessary object structure
|
||||
*/
|
||||
private setActivityInSnapshot(activity: Activity): void {
|
||||
if (!this.snapshot[activity.key]) {
|
||||
this.snapshot[activity.key] = {};
|
||||
// Assign the key for convenience.
|
||||
const key = activity.key;
|
||||
|
||||
// Assign the entityId to the provided entityId - or to the root ("") entity if none provided.
|
||||
const entityId = activity.entityId || '';
|
||||
|
||||
if (!this.snapshot[key]) {
|
||||
this.snapshot[key] = {};
|
||||
}
|
||||
|
||||
this.snapshot[activity.key][activity.entityId || ''] = activity;
|
||||
this.snapshot[key][entityId] = activity;
|
||||
|
||||
/*
|
||||
// Index the activity by entityId:key.
|
||||
if (!this.snapshotIndexedByEntity[entityId]) {
|
||||
this.snapshot[entityId] = {};
|
||||
}
|
||||
|
||||
this.snapshotIndexedByEntity[entityId][key] = activity;
|
||||
|
||||
// Index the activity by key:entityId.
|
||||
if (!this.snapshotIndexedByKey[key]) {
|
||||
this.snapshot[key] = {};
|
||||
}
|
||||
|
||||
this.snapshotIndexedByKey[key][entityId] = activity;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,9 @@ export type StorageOpts = {
|
||||
encryption?: Encrypter;
|
||||
};
|
||||
|
||||
// TODO: Do we extend from this at store construction or at `all()`?
|
||||
export type StoreRecords = Record<string, unknown>;
|
||||
|
||||
export type StoreEvents = {
|
||||
[key: `set:${string}`]: any;
|
||||
set: { key: string; value: any };
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// NOTE: Do not export specific providers in this file.
|
||||
// Do this in the parent index.ts file instead (otherwise, we mingle interfaces and providers, which can create circular dependencies)..
|
||||
// The exception to this rule is with the InMemory and Composite providers.
|
||||
// These are considered built-ins and do not have any other interface dependencies.
|
||||
export * from './activities.js';
|
||||
export * from './base-store.js';
|
||||
export * from './store-composite.js';
|
||||
export * from './store-inmemory.js';
|
||||
export * from './store-inmemory-synced.js';
|
||||
export * from './store-remote-http.js';
|
||||
|
||||
@@ -11,7 +11,15 @@ import { type BlockchainUTXOs } from '../blockchain/index.js';
|
||||
import { type Activities, type BaseStore } from '../storage/index.js';
|
||||
import { EventEmitter } from '../utils/index.js';
|
||||
|
||||
import { type TokenBalances, type Address } from '@xocash/primitives';
|
||||
import {
|
||||
type TokenBalances,
|
||||
type Address,
|
||||
Bytes,
|
||||
Hasher,
|
||||
encodeExtendedJson,
|
||||
} from '@xocash/primitives';
|
||||
|
||||
export type WalletGenesisData = Record<string, unknown>;
|
||||
|
||||
export type WalletEvents = {
|
||||
shouldMonitorUpdated: boolean;
|
||||
@@ -33,32 +41,69 @@ export type WalletEvents = {
|
||||
export abstract class BaseWallet<
|
||||
WalletEventsType extends WalletEvents = WalletEvents,
|
||||
> extends EventEmitter<WalletEventsType> {
|
||||
static readonly deriveKey: (keyName: string) => Promise<Uint8Array>;
|
||||
/**
|
||||
* Derive a key to use based on the given Genesis Data.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* These keys can be used for Wallet Id's, Signing Keys, Encryption Keys and for Dapp Samdboxes.
|
||||
*/
|
||||
static deriveKey(genesisData: WalletGenesisData, keyName: string) {
|
||||
// Hash the genesis data using Hash256 and the key ("WalletID") for this derivation using Hash256.
|
||||
const genesisDataHashed = Hasher.fromUTF8(
|
||||
encodeExtendedJson(genesisData),
|
||||
).toHash256Bytes();
|
||||
const keyHashed = Hasher.fromUTF8(keyName).toHash256Bytes();
|
||||
|
||||
// Combine the hashes and do one final hash for good measure.
|
||||
return Hasher.fromBytes(
|
||||
new Uint8Array([...genesisDataHashed, ...keyHashed]),
|
||||
).toHash256Bytes();
|
||||
}
|
||||
|
||||
static deriveId(genesisData: WalletGenesisData) {
|
||||
return BaseWallet.deriveKey(genesisData, 'walletId');
|
||||
}
|
||||
|
||||
// Dependencies.
|
||||
abstract readonly activities: Activities;
|
||||
abstract readonly blockchain: WalletBlockchain;
|
||||
abstract readonly cache: BaseStore;
|
||||
|
||||
// Genesis Data.
|
||||
abstract readonly genesisData: WalletGenesisData;
|
||||
|
||||
/**
|
||||
* Get the ID for this wallet.
|
||||
*/
|
||||
abstract getId(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Derive a key using secret data from this wallet.
|
||||
*/
|
||||
abstract deriveKey(keyName: string): Promise<Uint8Array>;
|
||||
async getId() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return Bytes.from(
|
||||
await this.cache.getOrSet('walletId', async () =>
|
||||
BaseWallet.deriveKey(this.genesisData, 'walletId'),
|
||||
),
|
||||
).toHex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encryption key to use for this wallet.
|
||||
*/
|
||||
abstract getEncryptionKey(): Promise<Uint8Array>;
|
||||
async getEncryptionKey() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return await this.cache.getOrSet('encryptionKey', async () =>
|
||||
BaseWallet.deriveKey(this.genesisData, 'encryptionKey'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signing key to use for this wallet.
|
||||
*/
|
||||
abstract getSigningKey(): Promise<Uint8Array>;
|
||||
async getSigningKey() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return this.cache.getOrSet('signingKey', async () =>
|
||||
BaseWallet.deriveKey(this.genesisData, 'signingKey'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start monitoring this wallet for any transaction activity.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// NOTE: Do not export specific providers in this file.
|
||||
// Do this in the parent index.ts file instead (otherwise, we mingle interfaces and providers, which can create circular dependencies)..
|
||||
// The exception to this rule is with the InMemory and Composite providers.
|
||||
// These are considered built-ins and do not have any other interface dependencies.
|
||||
export * from './activities.js';
|
||||
export * from './base-wallet.js';
|
||||
export * from './transaction-builder.js';
|
||||
export * from './types.js';
|
||||
export * from './wallet-hd.js';
|
||||
export * from './wallet-p2pkh.js';
|
||||
export * from './wallet-p2sh20.js';
|
||||
export * from './wallet-p2sh32.js';
|
||||
|
||||
@@ -13,11 +13,8 @@ import {
|
||||
Mnemonic,
|
||||
calculateBalanceSats,
|
||||
calculateBalanceTokens,
|
||||
encodeExtendedJson,
|
||||
} from '@xocash/primitives';
|
||||
|
||||
import { hash256, binToHex, hexToBin, utf8ToBin } from '@bitauth/libauth';
|
||||
|
||||
export const DERIVATION_PATH = "m/44'/145'/0'";
|
||||
export const ADDRESS_GAP = 20;
|
||||
|
||||
@@ -112,7 +109,7 @@ export class WalletHD extends BaseWallet {
|
||||
|
||||
// Instantiate the HDPrivateNode from the type specified in our genesis data.
|
||||
if ('seed' in genesisData) {
|
||||
this.hdPrivateNode = HDPrivateNode.fromSeed(hexToBin(genesisData.seed));
|
||||
this.hdPrivateNode = HDPrivateNode.fromSeed(genesisData.seed);
|
||||
} else if ('mnemonic' in genesisData) {
|
||||
this.hdPrivateNode = HDPrivateNode.fromMnemonic(
|
||||
Mnemonic.fromPhrase(genesisData.mnemonic),
|
||||
@@ -126,40 +123,6 @@ export class WalletHD extends BaseWallet {
|
||||
this.wallets[CHAIN_INTERNAL] = [];
|
||||
}
|
||||
|
||||
async getId() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return binToHex(
|
||||
await this.cache.getOrSet('walletId', async () =>
|
||||
this.deriveKey('walletId'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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(encodeExtendedJson(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 () =>
|
||||
this.deriveKey('encryptionKey'),
|
||||
);
|
||||
}
|
||||
|
||||
async getSigningKey() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return this.cache.getOrSet('signingKey', async () =>
|
||||
this.deriveKey('signingKey'),
|
||||
);
|
||||
}
|
||||
|
||||
async start() {
|
||||
// Get the address gap.
|
||||
// TODO: This should come from activities.
|
||||
|
||||
@@ -22,8 +22,6 @@ import {
|
||||
import {
|
||||
type Input,
|
||||
binToHex,
|
||||
hash256,
|
||||
utf8ToBin,
|
||||
walletTemplateP2pkhNonHd,
|
||||
walletTemplateToCompilerBCH,
|
||||
} from '@bitauth/libauth';
|
||||
@@ -161,40 +159,6 @@ export class WalletP2PKH extends BaseWallet {
|
||||
this.privateKey = PrivateKey.fromHex(genesisData.privateKey);
|
||||
}
|
||||
|
||||
async getId() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return binToHex(
|
||||
await this.cache.getOrSet('walletId', async () =>
|
||||
this.deriveKey('walletId'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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 () =>
|
||||
this.deriveKey('encryptionKey'),
|
||||
);
|
||||
}
|
||||
|
||||
async getSigningKey() {
|
||||
// NOTE: Use the cache to optimize future calls.
|
||||
return this.cache.getOrSet('signingKey', async () =>
|
||||
this.deriveKey('signingKey'),
|
||||
);
|
||||
}
|
||||
|
||||
async start() {
|
||||
// Do nothing if already started.
|
||||
if (this.isStarted) {
|
||||
|
||||
Reference in New Issue
Block a user