Skip to content

Commit

Permalink
Merge pull request #210 from ulixee/payment
Browse files Browse the repository at this point in the history
Renames and changing where Payment Comes From
  • Loading branch information
blakebyrnes authored Sep 26, 2024
2 parents 589741a + b4a9ad5 commit f0395c6
Show file tree
Hide file tree
Showing 101 changed files with 1,475 additions and 1,181 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
env:
GITHUB_URL: https://github.com/${{ github.repository }}
ADD_CHROME_VERSION: 120
ADD_CHROME_VERSION: 127
IMAGE: ulixee/ulixee-cloud

steps:
Expand Down
4 changes: 2 additions & 2 deletions client/main/lib/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ICrawlerOutputSchema from '@ulixee/datastore/interfaces/ICrawlerOutputSch
import IPaymentService from '@ulixee/datastore/interfaces/IPaymentService';
import IQueryOptions from '@ulixee/datastore/interfaces/IQueryOptions';
import DatastoreApiClient from '@ulixee/datastore/lib/DatastoreApiClient';
import { DataDomainStore } from '@argonprotocol/localchain';
import { DomainStore } from '@argonprotocol/localchain';
import { isIP } from 'node:net';
import { IInputFilter, IOutputSchema } from '../interfaces/IInputOutput';
import ILocationStringOrObject from '../interfaces/ILocationStringOrObject';
Expand Down Expand Up @@ -61,7 +61,7 @@ export default class ClientForRemote {
if (isDnsOrDomain && typeof uriOrObject === 'string') {
try {
// see if this is a domain
DataDomainStore.parse(this.host);
DomainStore.parse(this.host);

if (!config?.argonMainchainUrl)
throw new Error('No mainchain url provided to lookup this datastore host');
Expand Down
2 changes: 1 addition & 1 deletion client/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"dependencies": {
"@ulixee/commons": "2.0.0-alpha.29",
"@ulixee/datastore": "2.0.0-alpha.29",
"@argonprotocol/localchain": "0.0.4",
"@argonprotocol/localchain": "0.0.8",
"@ulixee/net": "2.0.0-alpha.29",
"@ulixee/schema": "2.0.0-alpha.29"
},
Expand Down
2 changes: 1 addition & 1 deletion cloud/main/.env.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ ULX_DATASTORE_STATS_HOST=# Stats Tracker Service Host
ULX_DATASTORE_REGISTRY_HOST=# Datastore Registry Service Host
ULX_STORAGE_ENGINE_HOST=# Storage Engine Service Host
ULX_REPLAY_REGISTRY_HOST=# Replays Storage/Retrieval Host
ULX_ESCROW_SPEND_TRACKING_HOST=# Escrow Spend tracking Host
ULX_MICROPAYMENT_CHANNEL_SPEND_TRACKING_HOST=# Micropayment Channel Spend tracking Host
ULX_PAYMENT_SERVICE_HOST=# Payment Services Host
ULX_DATASTORE_LOOKUP_SERVICE_HOST=

Expand Down
124 changes: 113 additions & 11 deletions cloud/main/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { applyEnvironmentVariables, parseEnvBool } from '@ulixee/commons/lib/envUtils';
import { filterUndefined } from '@ulixee/commons/lib/objectUtils';
import { parseIdentities } from '@ulixee/datastore-core/env';
import { parseAddress, parseIdentities } from '@ulixee/datastore-core/env';
import type ILocalchainConfig from '@ulixee/datastore/interfaces/ILocalchainConfig';
import { Command } from 'commander';
import * as Path from 'path';
import CloudNodeEnv from '../env';
Expand Down Expand Up @@ -88,15 +89,16 @@ export default function cliCommands(): Command {
)
.argParser(parseInt)
.default(10),
).addOption(
program
.createOption(
'--max-concurrent-heroes-per-browser <count>',
'Max number of concurrent Heroes to run per Chrome instance.',
)
.argParser(parseInt)
.default(10),
)
)
.addOption(
program
.createOption(
'--max-concurrent-heroes-per-browser <count>',
'Max number of concurrent Heroes to run per Chrome instance.',
)
.argParser(parseInt)
.default(10),
)
.addOption(
program
.createOption(
Expand Down Expand Up @@ -139,8 +141,77 @@ export default function cliCommands(): Command {
'--datastore-wait-for-completion',
'Wait for all in-process Datastores to complete before shutting down the Cloud node.',
)
.argParser(parseEnvBool)
.default(false),
)
.addOption(
program
.createOption(
'--argon-payment-address <address>',
'The SS58 formatted address to send payments to.',
)
.argParser(x => parseAddress(x, 'Argon Payments Address'))
.env('ARGON_PAYMENT_ADDRESS'),
)
.addOption(
program
.createOption(
'--argon-notary-id <id>',
'The preferred Argon notary to notarize payments with.',
)
.argParser(parseInt)
.env('ARGON_NOTARY_ID'),
)
.addOption(
program
.createOption(
'--argon-localchain-path <path>',
'Activate payment receipt with the given Argon Localchain.',
)
.env('ARGON_LOCALCHAIN_PATH'),
)
.addOption(
program
.createOption(
'--argon-mainchain-url <url>',
'Connect to the given Argon Mainchain rpc url.',
)
.env('ARGON_MAINCHAIN_URL'),
)
.addOption(
program
.createOption(
'--argon-localchain-password <password>',
'Localchain password provided inline (unsafe).',
)
.env('ARGON_LOCALCHAIN_PASSWORD'),
)
.addOption(
program
.createOption(
'--argon-localchain-password-interactive',
'Localchain password prompted on command line.',
)
.argParser(parseEnvBool)
.env('ARGON_LOCALCHAIN_PASSWORD_INTERACTIVE_CLI'),
)
.addOption(
program
.createOption(
'--argon-localchain-password-file <path>',
'Localchain password from a file path.',
)
.env('ARGON_LOCALCHAIN_PASSWORD_FILE'),
)
.addOption(
program
.createOption(
'--argon-block-rewards-address <address>',
'Activate block rewards capture with the given Argon Localchain address.',
)
.argParser(x => parseAddress(x, 'Block Rewards Address'))
.env('ARGON_BLOCK_REWARDS_ADDRESS'),
)
.allowUnknownOption(true)
.action(async opts => {
console.log('Starting Ulixee Cloud with configuration:', opts);
Expand All @@ -152,13 +223,36 @@ export default function cliCommands(): Command {
hostedServicesPort,
hostedServicesHostname,
env,
argonPaymentAddress,
argonNotaryId,
argonLocalchainPath,
argonMainchainUrl,
argonLocalchainPassword,
argonLocalchainPasswordInteractive,
argonLocalchainPasswordFile,
argonBlockRewardsAddress,
} = opts;
if (env) {
applyEnvironmentVariables(Path.resolve(env), process.env);
}
if (disableChromeAlive) CloudNodeEnv.disableChromeAlive = disableChromeAlive;

const { unblockedPlugins, heroDataDir, maxConcurrentHeroes, maxConcurrentHeroesPerBrowser } = opts;
const { unblockedPlugins, heroDataDir, maxConcurrentHeroes, maxConcurrentHeroesPerBrowser } =
opts;
let localchainConfig: ILocalchainConfig;
if (argonLocalchainPath) {
localchainConfig = {
localchainPath: argonLocalchainPath,
mainchainUrl: argonMainchainUrl,
notaryId: argonNotaryId,
keystorePassword: {
password: argonLocalchainPassword ? Buffer.from(argonLocalchainPassword) : undefined,
interactiveCli: argonLocalchainPasswordInteractive,
passwordFile: argonLocalchainPasswordFile,
},
blockRewardsAddress: argonBlockRewardsAddress,
};
}

const cloudNode = new CloudNode(
filterUndefined({
Expand Down Expand Up @@ -186,6 +280,14 @@ export default function cliCommands(): Command {
maxRuntimeMs: opts.maxDatastoreRuntimeMs,
waitForDatastoreCompletionOnShutdown: opts.datastoreWaitForCompletion,
adminIdentities: parseIdentities(opts.adminIdentities, 'Admin Identities'),
paymentInfo:
argonPaymentAddress && argonNotaryId
? {
address: argonPaymentAddress,
notaryId: argonNotaryId,
}
: undefined,
localchainConfig,
}),
}),
);
Expand Down
8 changes: 4 additions & 4 deletions datastore/broker-admin/src/pages/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
<div class="mb-2"><strong>Granted Balance:</strong> {{ overview.grantedBalance }}</div>
<div class="mb-2"><strong>Organizations:</strong> {{ overview.organizations }}</div>
<div class="mb-2"><strong>Users:</strong> {{ overview.users }}</div>
<div class="mb-2"><strong>Escrows:</strong> {{ overview.escrows }}</div>
<div class="mb-2"><strong>Open Escrows:</strong> {{ overview.openEscrows }}</div>
<div class="mb-2"><strong>ChannelHolds:</strong> {{ overview.channelHolds }}</div>
<div class="mb-2"><strong>Open ChannelHolds:</strong> {{ overview.openChannelHolds }}</div>
<div class="mb-2">
<strong>Balance Pending Escrow Settlement:</strong>
{{ overview.balancePendingEscrowSettlement }}
<strong>Balance Pending ChannelHold Settlement:</strong>
{{ overview.balancePendingChannelHoldSettlement }}
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions datastore/broker-admin/src/pages/Organization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Index.vue
<div class="mr-10 my-5 overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 p-10 bg-white">

<div class="mb-2">
<strong>Balance (milligons):</strong> {{ organization.balance }} available, {{ organization.balanceInEscrows }} in escrow
<strong>Balance (milligons):</strong> {{ organization.balance }} available, {{ organization.balanceInChannelHolds }} in channelHold
</div>
<div class="mb-2">
<strong>Grant Additional Funds (milligons):</strong>
Expand Down Expand Up @@ -124,7 +124,7 @@ export default Vue.defineComponent({
id: route.params.id as string,
name: '',
balance: 0n,
balanceInEscrows: 0n,
balanceInChannelHolds: 0n,
}),
list: Vue.ref<IDatabrokerAdminApiTypes['Organization.users']['result']>([]),
};
Expand Down
6 changes: 3 additions & 3 deletions datastore/broker-admin/src/pages/Organizations.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Index.vue
Balance
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-white">
Balance in Escrow
Balance in ChannelHold
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-white">
&nbsp;
Expand All @@ -85,7 +85,7 @@ Index.vue
{{ toArgons(org.balance) }}
</td>
<td class="px-3 py-4 text-sm text-gray-500">
{{ toArgons(org.balanceInEscrows) }}
{{ toArgons(org.balanceInChannelHolds) }}
</td>
<td class="px-3 py-4 text-sm text-gray-500">
<router-link
Expand Down Expand Up @@ -140,7 +140,7 @@ export default Vue.defineComponent({
name: this.name,
balance,
});
this.list.unshift({ id, name: this.name, balance, balanceInEscrows: 0n });
this.list.unshift({ id, name: this.name, balance, balanceInChannelHolds: 0n });
this.name = '';
this.balance = 0;
} catch (error: any) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import { Database as SqliteDatabase, Statement } from 'better-sqlite3';

export default class EscrowsTable {
private readonly insertQuery: Statement<IEscrowRecord>;
export default class ChannelHoldsTable {
private readonly insertQuery: Statement<IChannelHoldRecord>;
private readonly updateSettlementQuery: Statement<{
escrowId: string;
channelHoldId: string;
settledMilligons: bigint;
date: number;
}>;

constructor(private db: SqliteDatabase) {
db.exec(`CREATE TABLE IF NOT EXISTS Escrows (
escrowId TEXT NOT NULL PRIMARY KEY,
db.exec(`CREATE TABLE IF NOT EXISTS ChannelHolds (
channelHoldId TEXT NOT NULL PRIMARY KEY,
organizationId INTEGER NOT NULL,
createdByIdentity TEXT NOT NULL,
dataDomain TEXT,
domain TEXT,
heldMilligons INTEGER NOT NULL,
settledMilligons INTEGER,
settlementDate DATETIME,
created DATETIME NOT NULL,
FOREIGN KEY (organizationId) REFERENCES Organizations(id)
);`);
db.exec(`CREATE INDEX IF NOT EXISTS Escrows_organizationId ON Escrows (organizationId);`);
db.exec(`CREATE INDEX IF NOT EXISTS ChannelHolds_organizationId ON ChannelHolds (organizationId);`);

this.insertQuery = db.prepare(
`INSERT INTO Escrows (escrowId, organizationId, createdByIdentity, dataDomain, heldMilligons, created)
VALUES (:escrowId, :organizationId, :createdByIdentity, :dataDomain, :heldMilligons, :created)`,
`INSERT INTO ChannelHolds (channelHoldId, organizationId, createdByIdentity, domain, heldMilligons, created)
VALUES (:channelHoldId, :organizationId, :createdByIdentity, :domain, :heldMilligons, :created)`,
);
this.updateSettlementQuery = db.prepare(
`UPDATE Escrows SET settledMilligons = :settledMilligons, settlementDate = :date WHERE escrowId = :escrowId
`UPDATE ChannelHolds SET settledMilligons = :settledMilligons, settlementDate = :date WHERE channelHoldId = :channelHoldId
RETURNING organizationId, heldMilligons`,
);
}

public create(escrow: IEscrowRecord): void {
escrow.created = Date.now();
this.insertQuery.run(escrow);
public create(channelHold: IChannelHoldRecord): void {
channelHold.created = Date.now();
this.insertQuery.run(channelHold);
}

public updateSettlementReturningChange(
escrowId: string,
channelHoldId: string,
settledMilligons: bigint,
settlementDate: number,
): [organizationId: string, holdAmount: bigint, change: bigint] {
const { organizationId, heldMilligons } = this.updateSettlementQuery.get({
escrowId,
channelHoldId,
settledMilligons,
date: settlementDate,
}) as { organizationId: string; heldMilligons: bigint };
Expand All @@ -55,7 +55,7 @@ export default class EscrowsTable {
public count(): number {
return (
(this.db
.prepare(`SELECT COUNT(*) FROM Escrows`)
.prepare(`SELECT COUNT(*) FROM ChannelHolds`)
.safeIntegers(false)
.pluck()
.get() as number) ?? 0
Expand All @@ -65,7 +65,7 @@ export default class EscrowsTable {
public countOpen(): number {
return (
(this.db
.prepare(`SELECT COUNT(*) FROM Escrows WHERE settlementDate IS NULL`)
.prepare(`SELECT COUNT(*) FROM ChannelHolds WHERE settlementDate IS NULL`)
.safeIntegers(false)
.pluck()
.get() as number) ?? 0
Expand All @@ -75,18 +75,18 @@ export default class EscrowsTable {
public pendingBalance(): bigint {
return (
(this.db
.prepare(`SELECT SUM(heldMilligons) FROM Escrows WHERE settlementDate IS NULL`)
.prepare(`SELECT SUM(heldMilligons) FROM ChannelHolds WHERE settlementDate IS NULL`)
.pluck()
.get() as bigint) ?? 0n
);
}
}

export interface IEscrowRecord {
escrowId: string;
export interface IChannelHoldRecord {
channelHoldId: string;
organizationId: string;
createdByIdentity: string;
dataDomain?: string;
domain?: string;
heldMilligons: bigint;
settledMilligons?: bigint;
settlementDate?: number;
Expand Down
Loading

0 comments on commit f0395c6

Please sign in to comment.