@1sat/wallet-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +50 -0
  2. package/dist/accounts/index.d.ts +8 -0
  3. package/dist/accounts/index.d.ts.map +1 -0
  4. package/dist/accounts/index.js +7 -0
  5. package/dist/accounts/index.js.map +1 -0
  6. package/dist/accounts/metering.d.ts +15 -0
  7. package/dist/accounts/metering.d.ts.map +1 -0
  8. package/dist/accounts/metering.js +32 -0
  9. package/dist/accounts/metering.js.map +1 -0
  10. package/dist/accounts/middleware.d.ts +41 -0
  11. package/dist/accounts/middleware.d.ts.map +1 -0
  12. package/dist/accounts/middleware.js +137 -0
  13. package/dist/accounts/middleware.js.map +1 -0
  14. package/dist/accounts/migrations.d.ts +18 -0
  15. package/dist/accounts/migrations.d.ts.map +1 -0
  16. package/dist/accounts/migrations.js +46 -0
  17. package/dist/accounts/migrations.js.map +1 -0
  18. package/dist/accounts/paymentValidation.d.ts +59 -0
  19. package/dist/accounts/paymentValidation.d.ts.map +1 -0
  20. package/dist/accounts/paymentValidation.js +112 -0
  21. package/dist/accounts/paymentValidation.js.map +1 -0
  22. package/dist/accounts/pricing.d.ts +30 -0
  23. package/dist/accounts/pricing.d.ts.map +1 -0
  24. package/dist/accounts/pricing.js +32 -0
  25. package/dist/accounts/pricing.js.map +1 -0
  26. package/dist/accounts/repo.d.ts +17 -0
  27. package/dist/accounts/repo.d.ts.map +1 -0
  28. package/dist/accounts/repo.js +94 -0
  29. package/dist/accounts/repo.js.map +1 -0
  30. package/dist/accounts/types.d.ts +45 -0
  31. package/dist/accounts/types.d.ts.map +1 -0
  32. package/dist/accounts/types.js +2 -0
  33. package/dist/accounts/types.js.map +1 -0
  34. package/dist/createBearerServer.d.ts +21 -0
  35. package/dist/createBearerServer.d.ts.map +1 -0
  36. package/dist/createBearerServer.js +30 -0
  37. package/dist/createBearerServer.js.map +1 -0
  38. package/dist/createWalletMonitor.d.ts +29 -0
  39. package/dist/createWalletMonitor.d.ts.map +1 -0
  40. package/dist/createWalletMonitor.js +90 -0
  41. package/dist/createWalletMonitor.js.map +1 -0
  42. package/dist/createWalletRpcHandler.d.ts +4 -0
  43. package/dist/createWalletRpcHandler.d.ts.map +1 -0
  44. package/dist/createWalletRpcHandler.js +92 -0
  45. package/dist/createWalletRpcHandler.js.map +1 -0
  46. package/dist/createWalletServer.d.ts +32 -0
  47. package/dist/createWalletServer.d.ts.map +1 -0
  48. package/dist/createWalletServer.js +183 -0
  49. package/dist/createWalletServer.js.map +1 -0
  50. package/dist/dispatch.d.ts +21 -0
  51. package/dist/dispatch.d.ts.map +1 -0
  52. package/dist/dispatch.js +183 -0
  53. package/dist/dispatch.js.map +1 -0
  54. package/dist/index.d.ts +15 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +8 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/resolvers/bearer.d.ts +12 -0
  59. package/dist/resolvers/bearer.d.ts.map +1 -0
  60. package/dist/resolvers/bearer.js +43 -0
  61. package/dist/resolvers/bearer.js.map +1 -0
  62. package/dist/resolvers/index.d.ts +3 -0
  63. package/dist/resolvers/index.d.ts.map +1 -0
  64. package/dist/resolvers/index.js +2 -0
  65. package/dist/resolvers/index.js.map +1 -0
  66. package/dist/types.d.ts +52 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +2 -0
  69. package/dist/types.js.map +1 -0
  70. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @1sat/wallet-server
2
+
3
+ BRC-100 wallet storage RPC server. Symmetric counterpart to `@1sat/wallet-remote`.
4
+
5
+ Exposes a `WalletStorageProvider` (typically `StorageKnex` backed by postgres) over JSON-RPC. Supports two identity resolution modes:
6
+
7
+ - **BRC-100 mutual auth** — for public or direct client access
8
+ - **Bearer token + trusted identity header** — for proxy deployments where an upstream (e.g. 1sat-stack) has already authenticated the caller
9
+
10
+ Both can run on the same process under different routes.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ bun add @1sat/wallet-server @bsv/sdk @bsv/wallet-toolbox knex pg
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { createWalletRpcHandler, bearerResolver } from '@1sat/wallet-server'
22
+ import { StorageKnex } from '@bsv/wallet-toolbox'
23
+ import knex from 'knex'
24
+
25
+ const storage = new StorageKnex({
26
+ chain: 'main',
27
+ knex: knex({ client: 'pg', connection: process.env.DATABASE_URL }),
28
+ commissionSatoshis: 0,
29
+ commissionPubKeyHex: undefined,
30
+ feeModel: { model: 'sat/kb', value: 1 },
31
+ })
32
+
33
+ const handler = createWalletRpcHandler({
34
+ storage,
35
+ resolveIdentity: bearerResolver({ token: process.env.INTERNAL_API_KEY! }),
36
+ })
37
+
38
+ Bun.serve({ port: 8100, fetch: handler })
39
+ ```
40
+
41
+ ## Resolvers
42
+
43
+ - `bearerResolver({ token, header? })` — requires `Authorization: Bearer <token>` plus `X-Identity-Key: <pubkey>`. Use for firewalled internal routes where an upstream has already authenticated the caller.
44
+ - `brc100Resolver({ wallet })` — stock BRC-100 mutual authentication via `@bsv/auth-express-middleware`. Use for public routes.
45
+
46
+ ## Related packages
47
+
48
+ - `@1sat/wallet-remote` — client that talks to this server
49
+ - `@1sat/wallet-node` / `@1sat/wallet-browser` — local storage wallet factories
50
+ - `@1sat/cli` — ships the `1sat serve` command built on this package
@@ -0,0 +1,8 @@
1
+ export type { Account, AccountsConfig, IdentityKey, NewPayment, Payment, PaymentQuote, } from './types';
2
+ export { AccountsRepo } from './repo';
3
+ export { ACCOUNTS_TABLE, PAYMENTS_TABLE, runMigrations, up, down, } from './migrations';
4
+ export { measureUsedBytes } from './metering';
5
+ export { BYTES_PER_GB, computeCapacity, priceForDeficit, quoteForAccount, type CapacityInput, type CapacityResult, } from './pricing';
6
+ export { BRC0121_HEADERS, Brc0121PaymentError, internalizePayment, parseBrc0121Payment, readBrc0121Headers, validateBrc0121Payment, type Brc0121PaymentHeaders, type ParsedPayment, type ValidatePaymentInput, type ValidatePaymentResult, } from './paymentValidation';
7
+ export { AccountsGate, type AccountsMiddlewareDeps, type BillabilityCheckInput, type BillabilityDecision, } from './middleware';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/accounts/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,OAAO,EACP,cAAc,EACd,WAAW,EACX,UAAU,EACV,OAAO,EACP,YAAY,GACZ,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EACN,cAAc,EACd,cAAc,EACd,aAAa,EACb,EAAE,EACF,IAAI,GACJ,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EACN,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,cAAc,GACnB,MAAM,WAAW,CAAA;AAClB,OAAO,EACN,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACN,YAAY,EACZ,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,GACxB,MAAM,cAAc,CAAA"}
@@ -0,0 +1,7 @@
1
+ export { AccountsRepo } from './repo';
2
+ export { ACCOUNTS_TABLE, PAYMENTS_TABLE, runMigrations, up, down, } from './migrations';
3
+ export { measureUsedBytes } from './metering';
4
+ export { BYTES_PER_GB, computeCapacity, priceForDeficit, quoteForAccount, } from './pricing';
5
+ export { BRC0121_HEADERS, Brc0121PaymentError, internalizePayment, parseBrc0121Payment, readBrc0121Headers, validateBrc0121Payment, } from './paymentValidation';
6
+ export { AccountsGate, } from './middleware';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accounts/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EACN,cAAc,EACd,cAAc,EACd,aAAa,EACb,EAAE,EACF,IAAI,GACJ,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,EACN,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,GAGf,MAAM,WAAW,CAAA;AAClB,OAAO,EACN,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,GAKtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACN,YAAY,GAIZ,MAAM,cAAc,CAAA"}
@@ -0,0 +1,15 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Measures the stored-byte footprint of a single wallet-toolbox user.
4
+ *
5
+ * Sums the sizes of data that represent persisted wallet state:
6
+ * - `transactions.rawTx`
7
+ * - `transactions.inputBEEF`
8
+ * - `outputs.lockingScript` (or `outputs.scriptLength` when the blob was pushed to rawTx)
9
+ *
10
+ * These are the fields that actually consume storage per user and that the
11
+ * dispatcher's billable methods grow (`createAction`, `processAction`,
12
+ * `internalizeAction`, `processSyncChunk`, `insertCertificateAuth`).
13
+ */
14
+ export declare function measureUsedBytes(knex: Knex, userId: number): Promise<number>;
15
+ //# sourceMappingURL=metering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metering.d.ts","sourceRoot":"","sources":["../../src/accounts/metering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Measures the stored-byte footprint of a single wallet-toolbox user.
3
+ *
4
+ * Sums the sizes of data that represent persisted wallet state:
5
+ * - `transactions.rawTx`
6
+ * - `transactions.inputBEEF`
7
+ * - `outputs.lockingScript` (or `outputs.scriptLength` when the blob was pushed to rawTx)
8
+ *
9
+ * These are the fields that actually consume storage per user and that the
10
+ * dispatcher's billable methods grow (`createAction`, `processAction`,
11
+ * `internalizeAction`, `processSyncChunk`, `insertCertificateAuth`).
12
+ */
13
+ export async function measureUsedBytes(knex, userId) {
14
+ const [txRow] = await knex('transactions')
15
+ .where({ userId })
16
+ .select(knex.raw('COALESCE(SUM(COALESCE(LENGTH(rawTx), 0) + COALESCE(LENGTH(inputBEEF), 0)), 0) as total'));
17
+ const [outRow] = await knex('outputs')
18
+ .where({ userId })
19
+ .select(knex.raw('COALESCE(SUM(COALESCE(scriptLength, LENGTH(lockingScript), 0)), 0) as total'));
20
+ return asNumber(txRow?.total) + asNumber(outRow?.total);
21
+ }
22
+ function asNumber(v) {
23
+ if (v == null)
24
+ return 0;
25
+ if (typeof v === 'number')
26
+ return v;
27
+ if (typeof v === 'bigint')
28
+ return Number(v);
29
+ const n = Number(v);
30
+ return Number.isFinite(n) ? n : 0;
31
+ }
32
+ //# sourceMappingURL=metering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metering.js","sourceRoot":"","sources":["../../src/accounts/metering.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,IAAU,EACV,MAAc;IAEd,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC;SACxC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;SACjB,MAAM,CACN,IAAI,CAAC,GAAG,CACP,wFAAwF,CACxF,CACD,CAAA;IAEF,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC;SACpC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;SACjB,MAAM,CACN,IAAI,CAAC,GAAG,CACP,6EAA6E,CAC7E,CACD,CAAA;IAEF,OAAO,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AACxD,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC3B,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,CAAC,CAAA;IACvB,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAA;IACnC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAA;IAC3C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IACnB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAClC,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { WalletInterface } from '@bsv/sdk';
2
+ import type { Knex } from 'knex';
3
+ import type { ResolvedIdentity, WalletStorageProvider } from '../types';
4
+ import { AccountsRepo } from './repo';
5
+ import type { AccountsConfig, IdentityKey } from './types';
6
+ export interface AccountsMiddlewareDeps {
7
+ config: AccountsConfig;
8
+ walletStorage: WalletStorageProvider;
9
+ knex: Knex;
10
+ wallet: WalletInterface;
11
+ serverIdentityKey: IdentityKey;
12
+ /** Returns the current chain tip block height. */
13
+ currentBlock: () => Promise<number>;
14
+ }
15
+ export interface BillabilityCheckInput {
16
+ method: string;
17
+ identity: ResolvedIdentity;
18
+ request: Request;
19
+ id: string | number | null;
20
+ }
21
+ export type BillabilityDecision = {
22
+ type: 'allow';
23
+ } | {
24
+ type: 'blocked';
25
+ response: Response;
26
+ };
27
+ /**
28
+ * Gate that runs before dispatch for each JSON-RPC call. Decides whether to
29
+ * allow the call, to return a 402 challenge, or to accept a payment and then
30
+ * allow.
31
+ */
32
+ export declare class AccountsGate {
33
+ private readonly deps;
34
+ readonly repo: AccountsRepo;
35
+ private readonly freeKeys;
36
+ constructor(deps: AccountsMiddlewareDeps);
37
+ check(input: BillabilityCheckInput): Promise<BillabilityDecision>;
38
+ private resolveUserId;
39
+ private buildChallenge;
40
+ }
41
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/accounts/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC,OAAO,KAAK,EAEX,gBAAgB,EAChB,qBAAqB,EACrB,MAAM,UAAU,CAAA;AASjB,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1D,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,cAAc,CAAA;IACtB,aAAa,EAAE,qBAAqB,CAAA;IACpC,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,eAAe,CAAA;IACvB,iBAAiB,EAAE,WAAW,CAAA;IAC9B,kDAAkD;IAClD,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAA;AAE1C;;;;GAIG;AACH,qBAAa,YAAY;IAIZ,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;gBAEd,IAAI,EAAE,sBAAsB;IAQnD,KAAK,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,CAAC;YAyEzD,aAAa;IAO3B,OAAO,CAAC,cAAc;CAmCtB"}
@@ -0,0 +1,137 @@
1
+ import { Random, Utils } from '@bsv/sdk';
2
+ import { isBillableMethod } from '../dispatch';
3
+ import { measureUsedBytes } from './metering';
4
+ import { Brc0121PaymentError, internalizePayment, parseBrc0121Payment, readBrc0121Headers, } from './paymentValidation';
5
+ import { BYTES_PER_GB, computeCapacity, priceForDeficit } from './pricing';
6
+ import { AccountsRepo } from './repo';
7
+ /**
8
+ * Gate that runs before dispatch for each JSON-RPC call. Decides whether to
9
+ * allow the call, to return a 402 challenge, or to accept a payment and then
10
+ * allow.
11
+ */
12
+ export class AccountsGate {
13
+ deps;
14
+ repo;
15
+ freeKeys;
16
+ constructor(deps) {
17
+ this.deps = deps;
18
+ this.repo = new AccountsRepo(deps.knex);
19
+ this.freeKeys = new Set([
20
+ deps.serverIdentityKey,
21
+ ...(deps.config.freeIdentityKeys ?? []),
22
+ ]);
23
+ }
24
+ async check(input) {
25
+ if (!this.deps.config.enabled)
26
+ return { type: 'allow' };
27
+ if (!isBillableMethod(input.method))
28
+ return { type: 'allow' };
29
+ if (this.freeKeys.has(input.identity.identityKey))
30
+ return { type: 'allow' };
31
+ const userId = await this.resolveUserId(input.identity.identityKey);
32
+ if (userId == null) {
33
+ // No wallet-toolbox user yet means zero bytes stored. Allow the first
34
+ // billable request so the user row can be created; metering starts
35
+ // from the next request.
36
+ return { type: 'allow' };
37
+ }
38
+ const currentBlock = await this.deps.currentBlock();
39
+ const used = await measureUsedBytes(this.deps.knex, userId);
40
+ const paidBytes = await this.repo.sumUnexpiredBytes(input.identity.identityKey, currentBlock);
41
+ const capacity = computeCapacity({
42
+ baselineBytes: this.deps.config.baselineBytes,
43
+ paidBytes,
44
+ usedBytes: used,
45
+ });
46
+ if (capacity.deficitBytes <= 0)
47
+ return { type: 'allow' };
48
+ const paymentHeaders = readBrc0121Headers(input.request);
49
+ if (!paymentHeaders) {
50
+ return {
51
+ type: 'blocked',
52
+ response: this.buildChallenge(input.id, capacity.deficitBytes),
53
+ };
54
+ }
55
+ // A payment is attached. Validate and credit before allowing the retry.
56
+ const quote = priceForDeficit(capacity.deficitBytes, this.deps.config.satsPerGb);
57
+ try {
58
+ const parsed = parseBrc0121Payment(paymentHeaders, quote.satoshisRequired);
59
+ if (await this.repo.paymentExists(parsed.txid)) {
60
+ return {
61
+ type: 'blocked',
62
+ response: errorResponse(input.id, -32001, 'payment already applied'),
63
+ };
64
+ }
65
+ const validation = await internalizePayment({
66
+ wallet: this.deps.wallet,
67
+ senderIdentityKey: input.identity.identityKey,
68
+ parsed,
69
+ });
70
+ await this.repo.upsertAccount(input.identity.identityKey);
71
+ await this.repo.recordPayment({
72
+ identityKey: input.identity.identityKey,
73
+ txid: validation.txid,
74
+ bytesCovered: quote.bytesCovered,
75
+ satsPaid: validation.satoshisReceived,
76
+ paidThroughBlock: currentBlock + this.deps.config.durationBlocks,
77
+ });
78
+ return { type: 'allow' };
79
+ }
80
+ catch (err) {
81
+ if (err instanceof Brc0121PaymentError) {
82
+ return {
83
+ type: 'blocked',
84
+ response: errorResponse(input.id, -32002, err.message),
85
+ };
86
+ }
87
+ throw err;
88
+ }
89
+ }
90
+ async resolveUserId(identityKey) {
91
+ const result = await this.deps.walletStorage.findOrInsertUser(identityKey);
92
+ return result?.user?.userId;
93
+ }
94
+ buildChallenge(id, deficitBytes) {
95
+ const quote = priceForDeficit(deficitBytes, this.deps.config.satsPerGb);
96
+ const orderID = Utils.toBase64(Random(16));
97
+ const derivationPrefix = Utils.toBase64(Random(16));
98
+ const body = {
99
+ jsonrpc: '2.0',
100
+ error: {
101
+ code: -32000,
102
+ message: 'payment required',
103
+ data: {
104
+ status: 'payment-required',
105
+ satoshisRequired: quote.satoshisRequired,
106
+ bytesRequested: quote.bytesCovered,
107
+ gigabytesCharged: quote.gigabytesCharged,
108
+ bytesPerGb: BYTES_PER_GB,
109
+ derivationPrefix,
110
+ orderID,
111
+ },
112
+ },
113
+ id,
114
+ };
115
+ return new Response(JSON.stringify(body), {
116
+ status: 402,
117
+ headers: {
118
+ 'content-type': 'application/json',
119
+ 'x-bsv-payment-order-id': orderID,
120
+ 'x-bsv-payment-derivation-prefix': derivationPrefix,
121
+ 'x-bsv-payment-satoshis-required': String(quote.satoshisRequired),
122
+ },
123
+ });
124
+ }
125
+ }
126
+ function errorResponse(id, code, message) {
127
+ const body = {
128
+ jsonrpc: '2.0',
129
+ error: { code, message },
130
+ id,
131
+ };
132
+ return new Response(JSON.stringify(body), {
133
+ status: 402,
134
+ headers: { 'content-type': 'application/json' },
135
+ });
136
+ }
137
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/accounts/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Knex migrations for the accounts layer.
4
+ *
5
+ * Tables are prefixed `wsa_` (wallet-server-accounts) to avoid colliding with
6
+ * wallet-toolbox's own tables (`transactions`, `outputs`, etc.) that live in
7
+ * the same database.
8
+ */
9
+ export declare const ACCOUNTS_TABLE = "wsa_accounts";
10
+ export declare const PAYMENTS_TABLE = "wsa_payments";
11
+ export declare function up(knex: Knex): Promise<void>;
12
+ export declare function down(knex: Knex): Promise<void>;
13
+ /**
14
+ * Runs migrations against the given knex instance. Idempotent — safe to call on
15
+ * every server start.
16
+ */
17
+ export declare function runMigrations(knex: Knex): Promise<void>;
18
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/accounts/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEhC;;;;;;GAMG;AAEH,eAAO,MAAM,cAAc,iBAAiB,CAAA;AAC5C,eAAO,MAAM,cAAc,iBAAiB,CAAA;AAE5C,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BlD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpD;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Knex migrations for the accounts layer.
3
+ *
4
+ * Tables are prefixed `wsa_` (wallet-server-accounts) to avoid colliding with
5
+ * wallet-toolbox's own tables (`transactions`, `outputs`, etc.) that live in
6
+ * the same database.
7
+ */
8
+ export const ACCOUNTS_TABLE = 'wsa_accounts';
9
+ export const PAYMENTS_TABLE = 'wsa_payments';
10
+ export async function up(knex) {
11
+ const hasAccounts = await knex.schema.hasTable(ACCOUNTS_TABLE);
12
+ if (!hasAccounts) {
13
+ await knex.schema.createTable(ACCOUNTS_TABLE, (t) => {
14
+ t.string('identity_key', 66).primary();
15
+ t.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
16
+ t.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
17
+ });
18
+ }
19
+ const hasPayments = await knex.schema.hasTable(PAYMENTS_TABLE);
20
+ if (!hasPayments) {
21
+ await knex.schema.createTable(PAYMENTS_TABLE, (t) => {
22
+ t.increments('id').primary();
23
+ t.string('identity_key', 66).notNullable();
24
+ t.string('txid', 64).notNullable().unique();
25
+ t.bigInteger('bytes_covered').notNullable();
26
+ t.bigInteger('sats_paid').notNullable();
27
+ t.integer('paid_through_block').notNullable();
28
+ t.timestamp('applied_at').notNullable().defaultTo(knex.fn.now());
29
+ t.index('identity_key');
30
+ t.index('paid_through_block');
31
+ t.foreign('identity_key').references(`${ACCOUNTS_TABLE}.identity_key`);
32
+ });
33
+ }
34
+ }
35
+ export async function down(knex) {
36
+ await knex.schema.dropTableIfExists(PAYMENTS_TABLE);
37
+ await knex.schema.dropTableIfExists(ACCOUNTS_TABLE);
38
+ }
39
+ /**
40
+ * Runs migrations against the given knex instance. Idempotent — safe to call on
41
+ * every server start.
42
+ */
43
+ export async function runMigrations(knex) {
44
+ await up(knex);
45
+ }
46
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/accounts/migrations.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAA;AAC5C,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAA;AAE5C,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IAClC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;IAC9D,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;YACnD,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAA;YACtC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;YAChE,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;IAC9D,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;YACnD,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAA;YAC5B,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;YAC1C,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAA;YAC3C,CAAC,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,WAAW,EAAE,CAAA;YAC3C,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAA;YACvC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,WAAW,EAAE,CAAA;YAC7C,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;YAEhE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;YACvB,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAA;YAC7B,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,UAAU,CAAC,GAAG,cAAc,eAAe,CAAC,CAAA;QACvE,CAAC,CAAC,CAAA;IACH,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACpC,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAA;IACnD,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAA;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAU;IAC7C,MAAM,EAAE,CAAC,IAAI,CAAC,CAAA;AACf,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { type WalletInterface } from '@bsv/sdk';
2
+ /**
3
+ * BRC-0121 request-header names (lowercase for Headers API compatibility).
4
+ */
5
+ export declare const BRC0121_HEADERS: {
6
+ readonly derivationPrefix: "x-bsv-payment-derivation-prefix";
7
+ readonly derivationSuffix: "x-bsv-payment-derivation-suffix";
8
+ readonly transaction: "x-bsv-payment-transaction";
9
+ readonly orderID: "x-bsv-payment-order-id";
10
+ readonly outputIndex: "x-bsv-payment-output-index";
11
+ };
12
+ export interface Brc0121PaymentHeaders {
13
+ derivationPrefix: string;
14
+ derivationSuffix: string;
15
+ beefBase64: string;
16
+ orderID?: string;
17
+ outputIndex: number;
18
+ }
19
+ export declare function readBrc0121Headers(req: Request): Brc0121PaymentHeaders | null;
20
+ export interface ValidatePaymentInput {
21
+ wallet: WalletInterface;
22
+ senderIdentityKey: string;
23
+ headers: Brc0121PaymentHeaders;
24
+ expectedSatoshis: number;
25
+ }
26
+ export interface ParsedPayment {
27
+ txid: string;
28
+ beef: number[];
29
+ satoshisAtOutput: number;
30
+ outputIndex: number;
31
+ derivationPrefix: string;
32
+ derivationSuffix: string;
33
+ }
34
+ export interface ValidatePaymentResult {
35
+ txid: string;
36
+ satoshisReceived: number;
37
+ }
38
+ /**
39
+ * Parses the BRC-0121 payment headers into a usable shape. Performs the cheap
40
+ * checks (BEEF decode, output exists, satoshis sufficient) but does NOT touch
41
+ * the wallet — so callers can check duplicate-txid before internalizing.
42
+ */
43
+ export declare function parseBrc0121Payment(headers: Brc0121PaymentHeaders, expectedSatoshis: number): ParsedPayment;
44
+ /**
45
+ * Internalizes a previously-parsed BRC-29 payment into the server wallet.
46
+ * The wallet SDK handles BEEF validation, script derivation, and UTXO
47
+ * recording; failures throw `Brc0121PaymentError`.
48
+ */
49
+ export declare function internalizePayment(input: {
50
+ wallet: WalletInterface;
51
+ senderIdentityKey: string;
52
+ parsed: ParsedPayment;
53
+ }): Promise<ValidatePaymentResult>;
54
+ /** @deprecated Use `parseBrc0121Payment` then `internalizePayment`. */
55
+ export declare function validateBrc0121Payment(input: ValidatePaymentInput): Promise<ValidatePaymentResult>;
56
+ export declare class Brc0121PaymentError extends Error {
57
+ constructor(message: string);
58
+ }
59
+ //# sourceMappingURL=paymentValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paymentValidation.d.ts","sourceRoot":"","sources":["../../src/accounts/paymentValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAEnE;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;CAMlB,CAAA;AAEV,MAAM,WAAW,qBAAqB;IACrC,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,qBAAqB,GAAG,IAAI,CAmB7E;AAED,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,eAAe,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,qBAAqB,CAAA;IAC9B,gBAAgB,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,gBAAgB,EAAE,MAAM,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,qBAAqB,EAC9B,gBAAgB,EAAE,MAAM,GACtB,aAAa,CAyBf;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC/C,MAAM,EAAE,eAAe,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,aAAa,CAAA;CACrB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA2BjC;AAED,uEAAuE;AACvE,wBAAsB,sBAAsB,CAC3C,KAAK,EAAE,oBAAoB,GACzB,OAAO,CAAC,qBAAqB,CAAC,CAOhC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI3B"}
@@ -0,0 +1,112 @@
1
+ import { Transaction, Utils } from '@bsv/sdk';
2
+ /**
3
+ * BRC-0121 request-header names (lowercase for Headers API compatibility).
4
+ */
5
+ export const BRC0121_HEADERS = {
6
+ derivationPrefix: 'x-bsv-payment-derivation-prefix',
7
+ derivationSuffix: 'x-bsv-payment-derivation-suffix',
8
+ transaction: 'x-bsv-payment-transaction',
9
+ orderID: 'x-bsv-payment-order-id',
10
+ outputIndex: 'x-bsv-payment-output-index',
11
+ };
12
+ export function readBrc0121Headers(req) {
13
+ const derivationPrefix = req.headers.get(BRC0121_HEADERS.derivationPrefix);
14
+ const derivationSuffix = req.headers.get(BRC0121_HEADERS.derivationSuffix);
15
+ const beefBase64 = req.headers.get(BRC0121_HEADERS.transaction);
16
+ if (!derivationPrefix || !derivationSuffix || !beefBase64)
17
+ return null;
18
+ const outIdxHeader = req.headers.get(BRC0121_HEADERS.outputIndex);
19
+ const outputIndex = outIdxHeader ? Number.parseInt(outIdxHeader, 10) : 0;
20
+ if (!Number.isInteger(outputIndex) || outputIndex < 0) {
21
+ throw new Brc0121PaymentError(`invalid output index: ${outIdxHeader}`);
22
+ }
23
+ return {
24
+ derivationPrefix,
25
+ derivationSuffix,
26
+ beefBase64,
27
+ orderID: req.headers.get(BRC0121_HEADERS.orderID) ?? undefined,
28
+ outputIndex,
29
+ };
30
+ }
31
+ /**
32
+ * Parses the BRC-0121 payment headers into a usable shape. Performs the cheap
33
+ * checks (BEEF decode, output exists, satoshis sufficient) but does NOT touch
34
+ * the wallet — so callers can check duplicate-txid before internalizing.
35
+ */
36
+ export function parseBrc0121Payment(headers, expectedSatoshis) {
37
+ const beef = decodeBeefHeader(headers.beefBase64);
38
+ const tx = Transaction.fromAtomicBEEF(beef);
39
+ const txid = tx.id('hex');
40
+ const targetOutput = tx.outputs[headers.outputIndex];
41
+ if (!targetOutput) {
42
+ throw new Brc0121PaymentError(`BEEF has no output at index ${headers.outputIndex}`);
43
+ }
44
+ const satoshis = targetOutput.satoshis ?? 0;
45
+ if (satoshis < expectedSatoshis) {
46
+ throw new Brc0121PaymentError(`payment output ${satoshis} sats below required ${expectedSatoshis}`);
47
+ }
48
+ return {
49
+ txid,
50
+ beef,
51
+ satoshisAtOutput: satoshis,
52
+ outputIndex: headers.outputIndex,
53
+ derivationPrefix: headers.derivationPrefix,
54
+ derivationSuffix: headers.derivationSuffix,
55
+ };
56
+ }
57
+ /**
58
+ * Internalizes a previously-parsed BRC-29 payment into the server wallet.
59
+ * The wallet SDK handles BEEF validation, script derivation, and UTXO
60
+ * recording; failures throw `Brc0121PaymentError`.
61
+ */
62
+ export async function internalizePayment(input) {
63
+ try {
64
+ await input.wallet.internalizeAction({
65
+ tx: input.parsed.beef,
66
+ outputs: [
67
+ {
68
+ paymentRemittance: {
69
+ derivationPrefix: input.parsed.derivationPrefix,
70
+ derivationSuffix: input.parsed.derivationSuffix,
71
+ senderIdentityKey: input.senderIdentityKey,
72
+ },
73
+ outputIndex: input.parsed.outputIndex,
74
+ protocol: 'wallet payment',
75
+ },
76
+ ],
77
+ labels: ['wallet-server', 'brc-0121'],
78
+ description: 'BRC-0121 storage capacity payment',
79
+ });
80
+ }
81
+ catch (err) {
82
+ throw new Brc0121PaymentError(`wallet.internalizeAction rejected payment: ${err instanceof Error ? err.message : String(err)}`);
83
+ }
84
+ return {
85
+ txid: input.parsed.txid,
86
+ satoshisReceived: input.parsed.satoshisAtOutput,
87
+ };
88
+ }
89
+ /** @deprecated Use `parseBrc0121Payment` then `internalizePayment`. */
90
+ export async function validateBrc0121Payment(input) {
91
+ const parsed = parseBrc0121Payment(input.headers, input.expectedSatoshis);
92
+ return internalizePayment({
93
+ wallet: input.wallet,
94
+ senderIdentityKey: input.senderIdentityKey,
95
+ parsed,
96
+ });
97
+ }
98
+ export class Brc0121PaymentError extends Error {
99
+ constructor(message) {
100
+ super(message);
101
+ this.name = 'Brc0121PaymentError';
102
+ }
103
+ }
104
+ function decodeBeefHeader(value) {
105
+ try {
106
+ return Utils.toArray(value, 'base64');
107
+ }
108
+ catch (err) {
109
+ throw new Brc0121PaymentError(`cannot decode ${BRC0121_HEADERS.transaction} as base64: ${err instanceof Error ? err.message : String(err)}`);
110
+ }
111
+ }
112
+ //# sourceMappingURL=paymentValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paymentValidation.js","sourceRoot":"","sources":["../../src/accounts/paymentValidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { AccountsConfig } from './types';
2
+ declare const BYTES_PER_GB = 1073741824;
3
+ export interface CapacityInput {
4
+ baselineBytes: number;
5
+ paidBytes: number;
6
+ usedBytes: number;
7
+ }
8
+ export interface CapacityResult {
9
+ baselineBytes: number;
10
+ paidBytes: number;
11
+ capacityBytes: number;
12
+ usedBytes: number;
13
+ deficitBytes: number;
14
+ }
15
+ export declare function computeCapacity(input: CapacityInput): CapacityResult;
16
+ /**
17
+ * Given a byte deficit and a `satsPerGb` rate, compute the sats required to
18
+ * cover the deficit (rounded up at GB granularity).
19
+ */
20
+ export declare function priceForDeficit(deficitBytes: number, satsPerGb: number): {
21
+ satoshisRequired: number;
22
+ gigabytesCharged: number;
23
+ bytesCovered: number;
24
+ };
25
+ export declare function quoteForAccount(config: AccountsConfig, capacity: CapacityResult): {
26
+ satoshisRequired: number;
27
+ bytesCovered: number;
28
+ };
29
+ export { BYTES_PER_GB };
30
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/accounts/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,QAAA,MAAM,YAAY,aAAgB,CAAA;AAElC,MAAM,WAAW,aAAa;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAUpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GACf;IACF,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACpB,CAUA;AAED,wBAAgB,eAAe,CAC9B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,cAAc,GACtB;IACF,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;CACpB,CAEA;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}