Update to latest engine. Add sync-v2. Various fixes.
This commit is contained in:
+70
-26
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
ElectrumBlockchainProvider,
|
||||
Engine,
|
||||
type XOEngineOptions,
|
||||
computeAccountHash,
|
||||
// This is temporary. Will likely be moved to where we import templates in the cli. I think that makes more sense as this is a library thing
|
||||
generateTemplateIdentifier,
|
||||
} from "@xo-cash/engine";
|
||||
@@ -19,11 +21,12 @@ import { EventEmitter } from "../utils/event-emitter.js";
|
||||
// TODO: Remove this. Exists to hash the seed for database namespace.
|
||||
import { createHash } from "crypto";
|
||||
import { hexToBin } from "@bitauth/libauth";
|
||||
import { parseTemplate } from "@xo-cash/engine";
|
||||
import { parseTemplate, BlockchainMonitor } from "@xo-cash/engine";
|
||||
|
||||
import { p2pkhTemplate } from "@xo-cash/templates";
|
||||
import { vendingMachineTemplate } from "../templates/vending-machine.js";
|
||||
import { wrapBCHTemplate } from "../templates/wrap-template.js";
|
||||
import { createStorageAdapter, State, StorageType } from "@xo-cash/state";
|
||||
|
||||
export type AppEventMap = {
|
||||
"invitation-added": Invitation;
|
||||
@@ -54,6 +57,7 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
public electrum: BlockchainService;
|
||||
public rates: RatesService;
|
||||
public settings: SettingsService;
|
||||
public state: State;
|
||||
|
||||
public invitations: Invitation[] = [];
|
||||
/**
|
||||
@@ -79,16 +83,45 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
// Because of a bug that lets wallets read the unspents of other wallets, we are going to manually namespace the storage paths for the app.
|
||||
// We are going to do this by computing a hash of the seed and prefixing the storage paths with it.
|
||||
const seedHash = createHash("sha256").update(seed).digest("hex");
|
||||
const accountHash = computeAccountHash(seed);
|
||||
|
||||
// The v2 client derives an isolated signing key from this key for each invitation resource.
|
||||
const syncPrivateKey = hexToBin(seedHash);
|
||||
|
||||
// We want to only prefix the file name
|
||||
const prefixedStoragePath = `${seedHash.slice(0, 8)}-${config.engineConfig.databaseFilename}`;
|
||||
|
||||
// Create the engine
|
||||
const engine = await Engine.create(seed, {
|
||||
...config.engineConfig,
|
||||
databaseFilename: prefixedStoragePath,
|
||||
// Initialize the blockchain provider using Electrum
|
||||
const blockchainProvider = new ElectrumBlockchainProvider({
|
||||
applicationIdentifier: config.electrumApplicationIdentifier ?? 'XO-CLI',
|
||||
electrumOptions: config.engineConfig,
|
||||
});
|
||||
|
||||
// Start the blockchain provider (do this async, otherwise we block startup - the data is event-based so we dont need to await it)
|
||||
blockchainProvider.initialize();
|
||||
|
||||
// Create the storage adapter
|
||||
const storageAdapter = await createStorageAdapter({
|
||||
storageType: config.engineConfig.storageType ?? StorageType.INDEXEDDB,
|
||||
databasePath: config.engineConfig.databasePath,
|
||||
databaseFilename: prefixedStoragePath,
|
||||
accountHash,
|
||||
});
|
||||
|
||||
// Create the state instance
|
||||
const state = new State(storageAdapter);
|
||||
|
||||
// Initialize the blockchain monitor, event listeners for electrum application events
|
||||
const blockchainMonitor = new BlockchainMonitor(state, blockchainProvider);
|
||||
blockchainMonitor.initializeEventListeners();
|
||||
|
||||
// Create the engine
|
||||
// TODO: Remove this type assertion. Make Engine constructor public OR allow full pre-defined dependency injection to `.create()`
|
||||
const engine = new Engine(seed, state, blockchainProvider, blockchainMonitor);
|
||||
|
||||
// Initialize the state sync server.
|
||||
await engine.initializeStateSync();
|
||||
|
||||
// TODO: We *technically* dont want this here, but we also need some initial templates for the wallet, so im doing it here
|
||||
// Import the default P2PKH template
|
||||
await engine.importTemplate(p2pkhTemplate);
|
||||
@@ -100,14 +133,15 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
const updateTemplates = async () => {
|
||||
const templates = await engine.listImportedTemplates();
|
||||
|
||||
templates.forEach(async (template) => {
|
||||
engine.updateUnspentOutputsForTemplate(
|
||||
generateTemplateIdentifier(template),
|
||||
);
|
||||
engine.subscribeToScriptHashForTemplate(
|
||||
generateTemplateIdentifier(template),
|
||||
);
|
||||
});
|
||||
// maaan, I have no clue if this is required still
|
||||
// templates.forEach(async (template) => {
|
||||
// engine.updateUnspentOutputsForTemplate(
|
||||
// generateTemplateIdentifier(template),
|
||||
// );
|
||||
// engine.subscribeToScriptHashForTemplate(
|
||||
// generateTemplateIdentifier(template),
|
||||
// );
|
||||
// });
|
||||
};
|
||||
|
||||
updateTemplates();
|
||||
@@ -115,11 +149,11 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
// Set default locking parameters for P2PKH
|
||||
// To my knowledge, this doesnt generate any lockscript, so discovery of funds will not work automatically.
|
||||
// TODO: Add discovery for funds in the first index? Or until we return 0 TXs?
|
||||
await engine.setDefaultLockingParameters(
|
||||
generateTemplateIdentifier(parseTemplate(p2pkhTemplate)),
|
||||
"receiveOutput",
|
||||
"receiver",
|
||||
);
|
||||
await engine.updateFallbackLockingParameters({
|
||||
templateIdentifier: generateTemplateIdentifier(parseTemplate(p2pkhTemplate)),
|
||||
outputIdentifier: "receiveOutput",
|
||||
roleIdentifier: "receiver",
|
||||
});
|
||||
|
||||
// Create our own storage for the invitations
|
||||
const storage = await Storage.create(config.invitationStoragePath);
|
||||
@@ -139,6 +173,8 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
electrum,
|
||||
rates,
|
||||
settings,
|
||||
state,
|
||||
syncPrivateKey,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,6 +185,8 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
electrum: BlockchainService,
|
||||
rates: RatesService,
|
||||
settings: SettingsService,
|
||||
state: State,
|
||||
private readonly syncPrivateKey: Uint8Array,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -158,7 +196,8 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
this.electrum = electrum;
|
||||
this.rates = rates;
|
||||
this.settings = settings;
|
||||
this.history = new HistoryService(engine, this.invitations);
|
||||
this.state = state;
|
||||
this.history = new HistoryService(engine, this.invitations, state);
|
||||
}
|
||||
|
||||
async createInvitation(
|
||||
@@ -171,6 +210,7 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
typeof invitation === "string"
|
||||
? invitation
|
||||
: invitation.invitationIdentifier,
|
||||
this.syncPrivateKey,
|
||||
);
|
||||
|
||||
const deps = {
|
||||
@@ -307,14 +347,18 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
byInvitation.set(output.reservedBy!, existing);
|
||||
}
|
||||
|
||||
// console.error('Unreserving resources is not currently supported by the engine')
|
||||
for (const [invitationIdentifier, outputs] of byInvitation) {
|
||||
await this.engine.unreserveResources(
|
||||
outputs.map((o) => ({
|
||||
outpointTransactionHash: hexToBin(o.outpointTransactionHash),
|
||||
outpointIndex: o.outpointIndex,
|
||||
})),
|
||||
invitationIdentifier,
|
||||
);
|
||||
// Remove them directly from state
|
||||
this.state.archiveInvitation(invitationIdentifier);
|
||||
|
||||
// await this.engine.unreserveResources(
|
||||
// outputs.map((o) => ({
|
||||
// outpointTransactionHash: hexToBin(o.outpointTransactionHash),
|
||||
// outpointIndex: o.outpointIndex,
|
||||
// })),
|
||||
// invitationIdentifier,
|
||||
// );
|
||||
}
|
||||
|
||||
return reserved.length;
|
||||
|
||||
Reference in New Issue
Block a user