@bsv/sdk 1.7.7 → 1.8.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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/kvstore/GlobalKVStore.js +420 -0
- package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -0
- package/dist/cjs/src/kvstore/LocalKVStore.js +6 -6
- package/dist/cjs/src/kvstore/LocalKVStore.js.map +1 -1
- package/dist/cjs/src/kvstore/kvStoreInterpreter.js +74 -0
- package/dist/cjs/src/kvstore/kvStoreInterpreter.js.map +1 -0
- package/dist/cjs/src/kvstore/types.js +11 -0
- package/dist/cjs/src/kvstore/types.js.map +1 -0
- package/dist/cjs/src/overlay-tools/Historian.js +153 -0
- package/dist/cjs/src/overlay-tools/Historian.js.map +1 -0
- package/dist/cjs/src/script/templates/PushDrop.js +2 -2
- package/dist/cjs/src/script/templates/PushDrop.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +4 -4
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/fee-models/LivePolicy.js +90 -0
- package/dist/cjs/src/transaction/fee-models/LivePolicy.js.map +1 -0
- package/dist/cjs/src/transaction/fee-models/index.js +3 -1
- package/dist/cjs/src/transaction/fee-models/index.js.map +1 -1
- package/dist/cjs/src/wallet/WalletClient.js +43 -52
- package/dist/cjs/src/wallet/WalletClient.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +19 -0
- package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js +18 -1
- package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/kvstore/GlobalKVStore.js +416 -0
- package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -0
- package/dist/esm/src/kvstore/LocalKVStore.js +6 -6
- package/dist/esm/src/kvstore/LocalKVStore.js.map +1 -1
- package/dist/esm/src/kvstore/kvStoreInterpreter.js +47 -0
- package/dist/esm/src/kvstore/kvStoreInterpreter.js.map +1 -0
- package/dist/esm/src/kvstore/types.js +8 -0
- package/dist/esm/src/kvstore/types.js.map +1 -0
- package/dist/esm/src/overlay-tools/Historian.js +155 -0
- package/dist/esm/src/overlay-tools/Historian.js.map +1 -0
- package/dist/esm/src/script/templates/PushDrop.js +2 -2
- package/dist/esm/src/script/templates/PushDrop.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +4 -4
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/fee-models/LivePolicy.js +85 -0
- package/dist/esm/src/transaction/fee-models/LivePolicy.js.map +1 -0
- package/dist/esm/src/transaction/fee-models/index.js +1 -0
- package/dist/esm/src/transaction/fee-models/index.js.map +1 -1
- package/dist/esm/src/wallet/WalletClient.js +43 -52
- package/dist/esm/src/wallet/WalletClient.js.map +1 -1
- package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +19 -0
- package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
- package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js +18 -1
- package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/kvstore/GlobalKVStore.d.ts +129 -0
- package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -0
- package/dist/types/src/kvstore/kvStoreInterpreter.d.ts +22 -0
- package/dist/types/src/kvstore/kvStoreInterpreter.d.ts.map +1 -0
- package/dist/types/src/kvstore/types.d.ts +106 -0
- package/dist/types/src/kvstore/types.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/Historian.d.ts +92 -0
- package/dist/types/src/overlay-tools/Historian.d.ts.map +1 -0
- package/dist/types/src/script/templates/PushDrop.d.ts +6 -5
- package/dist/types/src/script/templates/PushDrop.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +2 -2
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/fee-models/LivePolicy.d.ts +41 -0
- package/dist/types/src/transaction/fee-models/LivePolicy.d.ts.map +1 -0
- package/dist/types/src/transaction/fee-models/index.d.ts +1 -0
- package/dist/types/src/transaction/fee-models/index.d.ts.map +1 -1
- package/dist/types/src/wallet/WalletClient.d.ts +1 -1
- package/dist/types/src/wallet/WalletClient.d.ts.map +1 -1
- package/dist/types/src/wallet/substrates/WalletWireProcessor.d.ts.map +1 -1
- package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/docs/reference/script.md +7 -19
- package/docs/reference/transaction.md +75 -6
- package/docs/reference/wallet.md +1 -1
- package/package.json +1 -1
- package/src/kvstore/GlobalKVStore.ts +478 -0
- package/src/kvstore/LocalKVStore.ts +7 -7
- package/src/kvstore/__tests/GlobalKVStore.test.ts +965 -0
- package/src/kvstore/__tests/LocalKVStore.test.ts +72 -0
- package/src/kvstore/kvStoreInterpreter.ts +49 -0
- package/src/kvstore/types.ts +114 -0
- package/src/overlay-tools/Historian.ts +195 -0
- package/src/overlay-tools/__tests/Historian.test.ts +690 -0
- package/src/script/templates/PushDrop.ts +6 -5
- package/src/transaction/Transaction.ts +4 -4
- package/src/transaction/fee-models/LivePolicy.ts +97 -0
- package/src/transaction/fee-models/__tests/LivePolicy.test.ts +148 -0
- package/src/transaction/fee-models/index.ts +1 -0
- package/src/wallet/WalletClient.ts +50 -51
- package/src/wallet/substrates/WalletWireProcessor.ts +21 -0
- package/src/wallet/substrates/WalletWireTransceiver.ts +22 -10
package/docs/reference/script.md
CHANGED
|
@@ -218,21 +218,15 @@ export default class PushDrop implements ScriptTemplate {
|
|
|
218
218
|
fields: number[][];
|
|
219
219
|
}
|
|
220
220
|
constructor(wallet: WalletInterface, originator?: string)
|
|
221
|
-
async lock(fields: number[][], protocolID:
|
|
222
|
-
|
|
223
|
-
string
|
|
224
|
-
], keyID: string, counterparty: string, forSelf = false, includeSignature = true, lockPosition: "before" | "after" = "before"): Promise<LockingScript>
|
|
225
|
-
unlock(protocolID: [
|
|
226
|
-
SecurityLevel,
|
|
227
|
-
string
|
|
228
|
-
], keyID: string, counterparty: string, signOutputs: "all" | "none" | "single" = "all", anyoneCanPay = false, sourceSatoshis?: number, lockingScript?: LockingScript): {
|
|
221
|
+
async lock(fields: number[][], protocolID: WalletProtocol, keyID: string, counterparty: string, forSelf = false, includeSignature = true, lockPosition: "before" | "after" = "before"): Promise<LockingScript>
|
|
222
|
+
unlock(protocolID: WalletProtocol, keyID: string, counterparty: string, signOutputs: "all" | "none" | "single" = "all", anyoneCanPay = false, sourceSatoshis?: number, lockingScript?: LockingScript): {
|
|
229
223
|
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>;
|
|
230
224
|
estimateLength: () => Promise<73>;
|
|
231
225
|
}
|
|
232
226
|
}
|
|
233
227
|
```
|
|
234
228
|
|
|
235
|
-
See also: [LockingScript](./script.md#class-lockingscript), [PublicKey](./primitives.md#class-publickey), [ScriptTemplate](./script.md#interface-scripttemplate), [
|
|
229
|
+
See also: [LockingScript](./script.md#class-lockingscript), [PublicKey](./primitives.md#class-publickey), [ScriptTemplate](./script.md#interface-scripttemplate), [Transaction](./transaction.md#class-transaction), [UnlockingScript](./script.md#class-unlockingscript), [WalletInterface](./wallet.md#interface-walletinterface), [WalletProtocol](./wallet.md#type-walletprotocol), [sign](./compat.md#variable-sign)
|
|
236
230
|
|
|
237
231
|
#### Constructor
|
|
238
232
|
|
|
@@ -277,12 +271,9 @@ Argument Details
|
|
|
277
271
|
Creates a PushDrop locking script with arbitrary data fields and a public key lock.
|
|
278
272
|
|
|
279
273
|
```ts
|
|
280
|
-
async lock(fields: number[][], protocolID:
|
|
281
|
-
SecurityLevel,
|
|
282
|
-
string
|
|
283
|
-
], keyID: string, counterparty: string, forSelf = false, includeSignature = true, lockPosition: "before" | "after" = "before"): Promise<LockingScript>
|
|
274
|
+
async lock(fields: number[][], protocolID: WalletProtocol, keyID: string, counterparty: string, forSelf = false, includeSignature = true, lockPosition: "before" | "after" = "before"): Promise<LockingScript>
|
|
284
275
|
```
|
|
285
|
-
See also: [LockingScript](./script.md#class-lockingscript), [
|
|
276
|
+
See also: [LockingScript](./script.md#class-lockingscript), [WalletProtocol](./wallet.md#type-walletprotocol)
|
|
286
277
|
|
|
287
278
|
Returns
|
|
288
279
|
|
|
@@ -308,15 +299,12 @@ Argument Details
|
|
|
308
299
|
Creates an unlocking script for spending a PushDrop token output.
|
|
309
300
|
|
|
310
301
|
```ts
|
|
311
|
-
unlock(protocolID:
|
|
312
|
-
SecurityLevel,
|
|
313
|
-
string
|
|
314
|
-
], keyID: string, counterparty: string, signOutputs: "all" | "none" | "single" = "all", anyoneCanPay = false, sourceSatoshis?: number, lockingScript?: LockingScript): {
|
|
302
|
+
unlock(protocolID: WalletProtocol, keyID: string, counterparty: string, signOutputs: "all" | "none" | "single" = "all", anyoneCanPay = false, sourceSatoshis?: number, lockingScript?: LockingScript): {
|
|
315
303
|
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>;
|
|
316
304
|
estimateLength: () => Promise<73>;
|
|
317
305
|
}
|
|
318
306
|
```
|
|
319
|
-
See also: [LockingScript](./script.md#class-lockingscript), [
|
|
307
|
+
See also: [LockingScript](./script.md#class-lockingscript), [Transaction](./transaction.md#class-transaction), [UnlockingScript](./script.md#class-unlockingscript), [WalletProtocol](./wallet.md#type-walletprotocol), [sign](./compat.md#variable-sign)
|
|
320
308
|
|
|
321
309
|
Returns
|
|
322
310
|
|
|
@@ -456,6 +456,7 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
|
|
|
456
456
|
| [BeefParty](#class-beefparty) |
|
|
457
457
|
| [BeefTx](#class-beeftx) |
|
|
458
458
|
| [FetchHttpClient](#class-fetchhttpclient) |
|
|
459
|
+
| [LivePolicy](#class-livepolicy) |
|
|
459
460
|
| [MerklePath](#class-merklepath) |
|
|
460
461
|
| [NodejsHttpClient](#class-nodejshttpclient) |
|
|
461
462
|
| [SatoshisPerKilobyte](#class-satoshisperkilobyte) |
|
|
@@ -1198,6 +1199,74 @@ See also: [Fetch](./transaction.md#type-fetch), [HttpClient](./transaction.md#in
|
|
|
1198
1199
|
|
|
1199
1200
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables)
|
|
1200
1201
|
|
|
1202
|
+
---
|
|
1203
|
+
### Class: LivePolicy
|
|
1204
|
+
|
|
1205
|
+
Represents a live fee policy that fetches current rates from ARC GorillaPool.
|
|
1206
|
+
Extends SatoshisPerKilobyte to reuse transaction size calculation logic.
|
|
1207
|
+
|
|
1208
|
+
```ts
|
|
1209
|
+
export default class LivePolicy extends SatoshisPerKilobyte {
|
|
1210
|
+
constructor(cacheValidityMs: number = 5 * 60 * 1000)
|
|
1211
|
+
static getInstance(cacheValidityMs: number = 5 * 60 * 1000): LivePolicy
|
|
1212
|
+
async computeFee(tx: Transaction): Promise<number>
|
|
1213
|
+
}
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
See also: [SatoshisPerKilobyte](./transaction.md#class-satoshisperkilobyte), [Transaction](./transaction.md#class-transaction)
|
|
1217
|
+
|
|
1218
|
+
#### Constructor
|
|
1219
|
+
|
|
1220
|
+
Constructs an instance of the live policy fee model.
|
|
1221
|
+
|
|
1222
|
+
```ts
|
|
1223
|
+
constructor(cacheValidityMs: number = 5 * 60 * 1000)
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
Argument Details
|
|
1227
|
+
|
|
1228
|
+
+ **cacheValidityMs**
|
|
1229
|
+
+ How long to cache the fee rate in milliseconds (default: 5 minutes)
|
|
1230
|
+
|
|
1231
|
+
#### Method computeFee
|
|
1232
|
+
|
|
1233
|
+
Computes the fee for a given transaction using the current live rate.
|
|
1234
|
+
Overrides the parent method to use dynamic rate fetching.
|
|
1235
|
+
|
|
1236
|
+
```ts
|
|
1237
|
+
async computeFee(tx: Transaction): Promise<number>
|
|
1238
|
+
```
|
|
1239
|
+
See also: [Transaction](./transaction.md#class-transaction)
|
|
1240
|
+
|
|
1241
|
+
Returns
|
|
1242
|
+
|
|
1243
|
+
The fee in satoshis for the transaction.
|
|
1244
|
+
|
|
1245
|
+
Argument Details
|
|
1246
|
+
|
|
1247
|
+
+ **tx**
|
|
1248
|
+
+ The transaction for which a fee is to be computed.
|
|
1249
|
+
|
|
1250
|
+
#### Method getInstance
|
|
1251
|
+
|
|
1252
|
+
Gets the singleton instance of LivePolicy to ensure cache sharing across the application.
|
|
1253
|
+
|
|
1254
|
+
```ts
|
|
1255
|
+
static getInstance(cacheValidityMs: number = 5 * 60 * 1000): LivePolicy
|
|
1256
|
+
```
|
|
1257
|
+
See also: [LivePolicy](./transaction.md#class-livepolicy)
|
|
1258
|
+
|
|
1259
|
+
Returns
|
|
1260
|
+
|
|
1261
|
+
The singleton LivePolicy instance
|
|
1262
|
+
|
|
1263
|
+
Argument Details
|
|
1264
|
+
|
|
1265
|
+
+ **cacheValidityMs**
|
|
1266
|
+
+ How long to cache the fee rate in milliseconds (default: 5 minutes)
|
|
1267
|
+
|
|
1268
|
+
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables)
|
|
1269
|
+
|
|
1201
1270
|
---
|
|
1202
1271
|
### Class: MerklePath
|
|
1203
1272
|
|
|
@@ -1520,7 +1589,7 @@ export default class Transaction {
|
|
|
1520
1589
|
addOutput(output: TransactionOutput): void
|
|
1521
1590
|
addP2PKHOutput(address: number[] | string, satoshis?: number): void
|
|
1522
1591
|
updateMetadata(metadata: Record<string, any>): void
|
|
1523
|
-
async fee(modelOrFee: FeeModel | number =
|
|
1592
|
+
async fee(modelOrFee: FeeModel | number = LivePolicy.getInstance(), changeDistribution: "equal" | "random" = "equal"): Promise<void>
|
|
1524
1593
|
getFee(): number
|
|
1525
1594
|
async sign(): Promise<void>
|
|
1526
1595
|
async broadcast(broadcaster: Broadcaster = defaultBroadcaster()): Promise<BroadcastResponse | BroadcastFailure>
|
|
@@ -1540,7 +1609,7 @@ export default class Transaction {
|
|
|
1540
1609
|
}
|
|
1541
1610
|
```
|
|
1542
1611
|
|
|
1543
|
-
See also: [BroadcastFailure](./transaction.md#interface-broadcastfailure), [BroadcastResponse](./transaction.md#interface-broadcastresponse), [Broadcaster](./transaction.md#interface-broadcaster), [ChainTracker](./transaction.md#interface-chaintracker), [FeeModel](./transaction.md#interface-feemodel), [
|
|
1612
|
+
See also: [BroadcastFailure](./transaction.md#interface-broadcastfailure), [BroadcastResponse](./transaction.md#interface-broadcastresponse), [Broadcaster](./transaction.md#interface-broadcaster), [ChainTracker](./transaction.md#interface-chaintracker), [FeeModel](./transaction.md#interface-feemodel), [LivePolicy](./transaction.md#class-livepolicy), [MerklePath](./transaction.md#class-merklepath), [Reader](./primitives.md#class-reader), [TransactionInput](./transaction.md#interface-transactioninput), [TransactionOutput](./transaction.md#interface-transactionoutput), [defaultBroadcaster](./transaction.md#function-defaultbroadcaster), [defaultChainTracker](./transaction.md#function-defaultchaintracker), [sign](./compat.md#variable-sign), [toHex](./primitives.md#variable-tohex), [verify](./compat.md#variable-verify)
|
|
1544
1613
|
|
|
1545
1614
|
#### Method addInput
|
|
1546
1615
|
|
|
@@ -1610,13 +1679,13 @@ Argument Details
|
|
|
1610
1679
|
#### Method fee
|
|
1611
1680
|
|
|
1612
1681
|
Computes fees prior to signing.
|
|
1613
|
-
If no fee model is provided, uses a
|
|
1682
|
+
If no fee model is provided, uses a LivePolicy fee model that fetches current rates from ARC.
|
|
1614
1683
|
If fee is a number, the transaction uses that value as fee.
|
|
1615
1684
|
|
|
1616
1685
|
```ts
|
|
1617
|
-
async fee(modelOrFee: FeeModel | number =
|
|
1686
|
+
async fee(modelOrFee: FeeModel | number = LivePolicy.getInstance(), changeDistribution: "equal" | "random" = "equal"): Promise<void>
|
|
1618
1687
|
```
|
|
1619
|
-
See also: [FeeModel](./transaction.md#interface-feemodel), [
|
|
1688
|
+
See also: [FeeModel](./transaction.md#interface-feemodel), [LivePolicy](./transaction.md#class-livepolicy)
|
|
1620
1689
|
|
|
1621
1690
|
Argument Details
|
|
1622
1691
|
|
|
@@ -2037,7 +2106,7 @@ Argument Details
|
|
|
2037
2106
|
Example
|
|
2038
2107
|
|
|
2039
2108
|
```ts
|
|
2040
|
-
tx.verify(new WhatsOnChain(),
|
|
2109
|
+
tx.verify(new WhatsOnChain(), LivePolicy.getInstance())
|
|
2041
2110
|
```
|
|
2042
2111
|
|
|
2043
2112
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables)
|
package/docs/reference/wallet.md
CHANGED
|
@@ -2629,7 +2629,7 @@ The SDK is how applications communicate with wallets over a communications subst
|
|
|
2629
2629
|
export default class WalletClient implements WalletInterface {
|
|
2630
2630
|
public substrate: "auto" | WalletInterface;
|
|
2631
2631
|
originator?: OriginatorDomainNameStringUnder250Bytes;
|
|
2632
|
-
constructor(substrate: "auto" | "Cicada" | "XDM" | "window.CWI" | "json-api" | "react-native" | WalletInterface = "auto", originator?: OriginatorDomainNameStringUnder250Bytes)
|
|
2632
|
+
constructor(substrate: "auto" | "Cicada" | "XDM" | "window.CWI" | "json-api" | "react-native" | "secure-json-api" | WalletInterface = "auto", originator?: OriginatorDomainNameStringUnder250Bytes)
|
|
2633
2633
|
async connectToSubstrate(): Promise<void>
|
|
2634
2634
|
async createAction(args: CreateActionArgs): Promise<CreateActionResult>
|
|
2635
2635
|
async signAction(args: SignActionArgs): Promise<SignActionResult>
|
package/package.json
CHANGED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import Transaction from '../transaction/Transaction.js'
|
|
2
|
+
import * as Utils from '../primitives/utils.js'
|
|
3
|
+
import { TopicBroadcaster, LookupResolver } from '../overlay-tools/index.js'
|
|
4
|
+
import { BroadcastResponse, BroadcastFailure } from '../transaction/Broadcaster.js'
|
|
5
|
+
import { WalletInterface, WalletProtocol, CreateActionInput, OutpointString, PubKeyHex, CreateActionOutput, HexString } from '../wallet/Wallet.interfaces.js'
|
|
6
|
+
import { PushDrop } from '../script/index.js'
|
|
7
|
+
import WalletClient from '../wallet/WalletClient.js'
|
|
8
|
+
import { Beef } from '../transaction/Beef.js'
|
|
9
|
+
import { Historian } from '../overlay-tools/Historian.js'
|
|
10
|
+
import { KVContext, kvStoreInterpreter } from './kvStoreInterpreter.js'
|
|
11
|
+
import { ProtoWallet } from '../wallet/ProtoWallet.js'
|
|
12
|
+
import { kvProtocol, KVStoreConfig, KVStoreQuery, KVStoreEntry, KVStoreGetOptions, KVStoreSetOptions, KVStoreRemoveOptions } from './types.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Default configuration values for GlobalKVStore operations.
|
|
16
|
+
* Provides sensible defaults for overlay connection and protocol settings.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_CONFIG: KVStoreConfig = {
|
|
19
|
+
protocolID: [1, 'kvstore'],
|
|
20
|
+
serviceName: 'ls_kvstore',
|
|
21
|
+
tokenAmount: 1,
|
|
22
|
+
topics: ['tm_kvstore'],
|
|
23
|
+
networkPreset: 'mainnet',
|
|
24
|
+
acceptDelayedBroadcast: false,
|
|
25
|
+
tokenSetDescription: '', // Will be set dynamically
|
|
26
|
+
tokenUpdateDescription: '', // Will be set dynamically
|
|
27
|
+
tokenRemovalDescription: '' // Will be set dynamically
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Implements a global key-value storage system which uses an overlay service to track key-value pairs.
|
|
32
|
+
* Each key-value pair is represented by a PushDrop token output.
|
|
33
|
+
* Allows getting, setting, and removing key-value pairs with optional fetching by protocolID and history tracking.
|
|
34
|
+
*/
|
|
35
|
+
export class GlobalKVStore {
|
|
36
|
+
/**
|
|
37
|
+
* The wallet interface used to create transactions and perform cryptographic operations.
|
|
38
|
+
* @readonly
|
|
39
|
+
*/
|
|
40
|
+
private readonly wallet: WalletInterface
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for the KVStore instance containing all runtime options.
|
|
44
|
+
* @private
|
|
45
|
+
* @readonly
|
|
46
|
+
*/
|
|
47
|
+
private readonly config: KVStoreConfig
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Historian instance used to extract history from transaction outputs.
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
private readonly historian: Historian<string, KVContext>
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Lookup resolver used to query the overlay for transaction outputs.
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
private readonly lookupResolver: LookupResolver
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Topic broadcaster used to broadcast transactions to the overlay.
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
private readonly topicBroadcaster: TopicBroadcaster
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A map to store locks for each key to ensure atomic updates.
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
private readonly keyLocks: Map<string, Array<(value: void | PromiseLike<void>) => void>> = new Map()
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Cached user identity key
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
private cachedIdentityKey: PubKeyHex | null = null
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Creates an instance of the GlobalKVStore.
|
|
81
|
+
*
|
|
82
|
+
* @param {KVStoreConfig} [config={}] - Configuration options for the KVStore. Defaults to empty object.
|
|
83
|
+
* @param {WalletInterface} [config.wallet] - Wallet to use for operations. Defaults to WalletClient.
|
|
84
|
+
* @throws {Error} If the configuration contains invalid parameters.
|
|
85
|
+
*/
|
|
86
|
+
constructor (config: KVStoreConfig = {}) {
|
|
87
|
+
// Merge with defaults to create a fully resolved config
|
|
88
|
+
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
89
|
+
this.wallet = config.wallet ?? new WalletClient()
|
|
90
|
+
this.historian = new Historian<string, KVContext>(kvStoreInterpreter)
|
|
91
|
+
this.lookupResolver = new LookupResolver({
|
|
92
|
+
networkPreset: this.config.networkPreset
|
|
93
|
+
})
|
|
94
|
+
this.topicBroadcaster = new TopicBroadcaster(this.config.topics as string[], {
|
|
95
|
+
networkPreset: this.config.networkPreset
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retrieves data from the KVStore.
|
|
101
|
+
* Can query by key+controller (single result), protocolID, controller, or key (multiple results).
|
|
102
|
+
*
|
|
103
|
+
* @param {KVStoreQuery} query - Query parameters sent to overlay
|
|
104
|
+
* @param {KVStoreGetOptions} [options={}] - Configuration options for the get operation
|
|
105
|
+
* @returns {Promise<KVStoreEntry | KVStoreEntry[] | undefined>} Single entry for key+controller queries, array for all other queries
|
|
106
|
+
*/
|
|
107
|
+
async get (query: KVStoreQuery, options: KVStoreGetOptions = {}): Promise<KVStoreEntry | KVStoreEntry[] | undefined> {
|
|
108
|
+
if (Object.keys(query).length === 0) {
|
|
109
|
+
throw new Error('Must specify either key, controller, or protocolID')
|
|
110
|
+
}
|
|
111
|
+
if (query.key != null && query.controller != null) {
|
|
112
|
+
// Specific key+controller query - return single entry
|
|
113
|
+
const entries = await this.queryOverlay(query, options)
|
|
114
|
+
return entries.length > 0 ? entries[0] : undefined
|
|
115
|
+
}
|
|
116
|
+
return await this.queryOverlay(query, options)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Sets a key-value pair. The current user (wallet identity) becomes the controller.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} key - The key to set (user computes this however they want)
|
|
123
|
+
* @param {string} value - The value to store
|
|
124
|
+
* @param {KVStoreSetOptions} [options={}] - Configuration options for the set operation
|
|
125
|
+
* @returns {Promise<OutpointString>} The outpoint of the created token
|
|
126
|
+
*/
|
|
127
|
+
async set (key: string, value: string, options: KVStoreSetOptions = {}): Promise<OutpointString> {
|
|
128
|
+
if (typeof key !== 'string' || key.length === 0) {
|
|
129
|
+
throw new Error('Key must be a non-empty string.')
|
|
130
|
+
}
|
|
131
|
+
if (typeof value !== 'string') {
|
|
132
|
+
throw new Error('Value must be a string.')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const controller = await this.getIdentityKey()
|
|
136
|
+
const lockQueue = await this.queueOperationOnKey(key)
|
|
137
|
+
const protocolID = options.protocolID ?? this.config.protocolID
|
|
138
|
+
const tokenSetDescription = (options.tokenSetDescription != null && options.tokenSetDescription !== '') ? options.tokenSetDescription : `Create KVStore value for ${key}`
|
|
139
|
+
const tokenUpdateDescription = (options.tokenUpdateDescription != null && options.tokenUpdateDescription !== '') ? options.tokenUpdateDescription : `Update KVStore value for ${key}`
|
|
140
|
+
const tokenAmount = options.tokenAmount ?? this.config.tokenAmount
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// Check for existing token to spend
|
|
144
|
+
const existingEntries = await this.queryOverlay({ key, controller }, { includeToken: true })
|
|
145
|
+
const existingToken = existingEntries.length > 0 ? existingEntries[0].token : undefined
|
|
146
|
+
|
|
147
|
+
// Create PushDrop locking script
|
|
148
|
+
const pushdrop = new PushDrop(this.wallet, this.config.originator)
|
|
149
|
+
const lockingScript = await pushdrop.lock(
|
|
150
|
+
[
|
|
151
|
+
Utils.toArray(JSON.stringify(protocolID), 'utf8'),
|
|
152
|
+
Utils.toArray(key, 'utf8'),
|
|
153
|
+
Utils.toArray(value, 'utf8'),
|
|
154
|
+
Utils.toArray(controller, 'hex')
|
|
155
|
+
],
|
|
156
|
+
protocolID ?? this.config.protocolID as WalletProtocol,
|
|
157
|
+
Utils.toUTF8(Utils.toArray(key, 'utf8')),
|
|
158
|
+
'anyone',
|
|
159
|
+
true
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
let inputs: CreateActionInput[] = []
|
|
163
|
+
let inputBEEF: Beef | undefined
|
|
164
|
+
|
|
165
|
+
if (existingToken != null) {
|
|
166
|
+
inputs = [{
|
|
167
|
+
outpoint: `${existingToken.txid}.${existingToken.outputIndex}`,
|
|
168
|
+
unlockingScriptLength: 74,
|
|
169
|
+
inputDescription: 'Previous KVStore token'
|
|
170
|
+
}]
|
|
171
|
+
inputBEEF = existingToken.beef
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (inputs.length > 0) {
|
|
175
|
+
// Update existing token
|
|
176
|
+
const { signableTransaction } = await this.wallet.createAction({
|
|
177
|
+
description: tokenUpdateDescription,
|
|
178
|
+
inputBEEF: inputBEEF?.toBinary(),
|
|
179
|
+
inputs,
|
|
180
|
+
outputs: [{
|
|
181
|
+
satoshis: tokenAmount ?? this.config.tokenAmount as number,
|
|
182
|
+
lockingScript: lockingScript.toHex(),
|
|
183
|
+
outputDescription: 'KVStore token'
|
|
184
|
+
}],
|
|
185
|
+
options: {
|
|
186
|
+
acceptDelayedBroadcast: this.config.acceptDelayedBroadcast,
|
|
187
|
+
randomizeOutputs: false
|
|
188
|
+
}
|
|
189
|
+
}, this.config.originator)
|
|
190
|
+
|
|
191
|
+
if (signableTransaction == null) {
|
|
192
|
+
throw new Error('Unable to create update transaction')
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const tx = Transaction.fromAtomicBEEF(signableTransaction.tx)
|
|
196
|
+
const unlocker = pushdrop.unlock(
|
|
197
|
+
this.config.protocolID as WalletProtocol,
|
|
198
|
+
key,
|
|
199
|
+
'anyone'
|
|
200
|
+
)
|
|
201
|
+
const unlockingScript = await unlocker.sign(tx, 0)
|
|
202
|
+
|
|
203
|
+
const { tx: finalTx } = await this.wallet.signAction({
|
|
204
|
+
reference: signableTransaction.reference,
|
|
205
|
+
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
206
|
+
}, this.config.originator)
|
|
207
|
+
|
|
208
|
+
if (finalTx == null) {
|
|
209
|
+
throw new Error('Unable to finalize update transaction')
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const transaction = Transaction.fromAtomicBEEF(finalTx)
|
|
213
|
+
await this.submitToOverlay(transaction)
|
|
214
|
+
return `${transaction.id('hex')}.0`
|
|
215
|
+
} else {
|
|
216
|
+
// Create new token
|
|
217
|
+
const { tx } = await this.wallet.createAction({
|
|
218
|
+
description: tokenSetDescription,
|
|
219
|
+
outputs: [{
|
|
220
|
+
satoshis: tokenAmount ?? this.config.tokenAmount as number,
|
|
221
|
+
lockingScript: lockingScript.toHex(),
|
|
222
|
+
outputDescription: 'KVStore token'
|
|
223
|
+
}],
|
|
224
|
+
options: {
|
|
225
|
+
acceptDelayedBroadcast: this.config.acceptDelayedBroadcast,
|
|
226
|
+
randomizeOutputs: false
|
|
227
|
+
}
|
|
228
|
+
}, this.config.originator)
|
|
229
|
+
|
|
230
|
+
if (tx == null) {
|
|
231
|
+
throw new Error('Failed to create transaction')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const transaction = Transaction.fromAtomicBEEF(tx)
|
|
235
|
+
await this.submitToOverlay(transaction)
|
|
236
|
+
return `${transaction.id('hex')}.0`
|
|
237
|
+
}
|
|
238
|
+
} finally {
|
|
239
|
+
if (lockQueue.length > 0) {
|
|
240
|
+
this.finishOperationOnKey(key, lockQueue)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Removes the key-value pair associated with the given key from the overlay service.
|
|
247
|
+
*
|
|
248
|
+
* @param {string} key - The key to remove.
|
|
249
|
+
* @param {CreateActionOutput[] | undefined} [outputs=undefined] - Additional outputs to include in the removal transaction.
|
|
250
|
+
* @param {KVStoreRemoveOptions} [options=undefined] - Optional parameters for the removal operation.
|
|
251
|
+
* @returns {Promise<HexString>} A promise that resolves to the txid of the removal transaction if successful.
|
|
252
|
+
* @throws {Error} If the key is invalid.
|
|
253
|
+
* @throws {Error} If the key does not exist in the store.
|
|
254
|
+
* @throws {Error} If the overlay service is unreachable or the transaction fails.
|
|
255
|
+
* @throws {Error} If there are existing tokens that cannot be unlocked.
|
|
256
|
+
*/
|
|
257
|
+
async remove (key: string, outputs?: CreateActionOutput[], options: KVStoreRemoveOptions = {}): Promise<HexString> {
|
|
258
|
+
if (typeof key !== 'string' || key.length === 0) {
|
|
259
|
+
throw new Error('Key must be a non-empty string.')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const controller = await this.getIdentityKey()
|
|
263
|
+
const lockQueue = await this.queueOperationOnKey(key)
|
|
264
|
+
|
|
265
|
+
const protocolID = options.protocolID ?? this.config.protocolID
|
|
266
|
+
const tokenRemovalDescription = (options.tokenRemovalDescription != null && options.tokenRemovalDescription !== '') ? options.tokenRemovalDescription : `Remove KVStore value for ${key}`
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const existingEntries = await this.queryOverlay({ key, controller }, { includeToken: true })
|
|
270
|
+
|
|
271
|
+
if (existingEntries.length === 0 || existingEntries[0].token == null) {
|
|
272
|
+
throw new Error('The item did not exist, no item was deleted.')
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const existingToken = existingEntries[0].token
|
|
276
|
+
const inputs: CreateActionInput[] = [{
|
|
277
|
+
outpoint: `${existingToken.txid}.${existingToken.outputIndex}`,
|
|
278
|
+
unlockingScriptLength: 74,
|
|
279
|
+
inputDescription: 'KVStore token to remove'
|
|
280
|
+
}]
|
|
281
|
+
|
|
282
|
+
const pushdrop = new PushDrop(this.wallet, this.config.originator)
|
|
283
|
+
const { signableTransaction } = await this.wallet.createAction({
|
|
284
|
+
description: tokenRemovalDescription,
|
|
285
|
+
inputBEEF: existingToken.beef.toBinary(),
|
|
286
|
+
inputs,
|
|
287
|
+
outputs,
|
|
288
|
+
options: {
|
|
289
|
+
acceptDelayedBroadcast: this.config.acceptDelayedBroadcast
|
|
290
|
+
}
|
|
291
|
+
}, this.config.originator)
|
|
292
|
+
|
|
293
|
+
if (signableTransaction == null) {
|
|
294
|
+
throw new Error('Unable to create removal transaction')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const tx = Transaction.fromAtomicBEEF(signableTransaction.tx)
|
|
298
|
+
const unlocker = pushdrop.unlock(
|
|
299
|
+
protocolID ?? this.config.protocolID as WalletProtocol,
|
|
300
|
+
key,
|
|
301
|
+
'anyone'
|
|
302
|
+
)
|
|
303
|
+
const unlockingScript = await unlocker.sign(tx, 0)
|
|
304
|
+
|
|
305
|
+
const { tx: finalTx } = await this.wallet.signAction({
|
|
306
|
+
reference: signableTransaction.reference,
|
|
307
|
+
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
308
|
+
}, this.config.originator)
|
|
309
|
+
|
|
310
|
+
if (finalTx == null) {
|
|
311
|
+
throw new Error('Unable to finalize removal transaction')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const transaction = Transaction.fromAtomicBEEF(finalTx)
|
|
315
|
+
await this.submitToOverlay(transaction)
|
|
316
|
+
return transaction.id('hex')
|
|
317
|
+
} finally {
|
|
318
|
+
if (lockQueue.length > 0) {
|
|
319
|
+
this.finishOperationOnKey(key, lockQueue)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Queues an operation on a specific key to ensure atomic updates.
|
|
326
|
+
* Prevents concurrent operations on the same key from interfering with each other.
|
|
327
|
+
*
|
|
328
|
+
* @param {string} key - The key to queue an operation for.
|
|
329
|
+
* @returns {Promise<Array<(value: void | PromiseLike<void>) => void>>} The lock queue for cleanup.
|
|
330
|
+
* @private
|
|
331
|
+
*/
|
|
332
|
+
private async queueOperationOnKey (key: string): Promise<Array<(value: void | PromiseLike<void>) => void>> {
|
|
333
|
+
// Check if a lock exists for this key and wait for it to resolve
|
|
334
|
+
let lockQueue = this.keyLocks.get(key)
|
|
335
|
+
if (lockQueue == null) {
|
|
336
|
+
lockQueue = []
|
|
337
|
+
this.keyLocks.set(key, lockQueue)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
let resolveNewLock: () => void = () => { }
|
|
341
|
+
const newLock = new Promise<void>((resolve) => {
|
|
342
|
+
resolveNewLock = resolve
|
|
343
|
+
if (lockQueue != null) { lockQueue.push(resolve) }
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// If we are the only request, resolve the lock immediately, queue remains at 1 item until request ends.
|
|
347
|
+
if (lockQueue.length === 1) {
|
|
348
|
+
resolveNewLock()
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
await newLock
|
|
352
|
+
return lockQueue
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Finishes an operation on a key and resolves the next waiting operation.
|
|
357
|
+
*
|
|
358
|
+
* @param {string} key - The key to finish the operation for.
|
|
359
|
+
* @param {Array<(value: void | PromiseLike<void>) => void>} lockQueue - The lock queue from queueOperationOnKey.
|
|
360
|
+
* @private
|
|
361
|
+
*/
|
|
362
|
+
private finishOperationOnKey (key: string, lockQueue: Array<(value: void | PromiseLike<void>) => void>): void {
|
|
363
|
+
lockQueue.shift() // Remove the current lock from the queue
|
|
364
|
+
if (lockQueue.length > 0) {
|
|
365
|
+
// If there are more locks waiting, resolve the next one
|
|
366
|
+
lockQueue[0]()
|
|
367
|
+
} else {
|
|
368
|
+
// Clean up empty queue to prevent memory leak
|
|
369
|
+
this.keyLocks.delete(key)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Helper function to fetch and cache user identity key
|
|
375
|
+
*
|
|
376
|
+
* @returns {Promise<PubKeyHex>} The identity key of the current user
|
|
377
|
+
* @private
|
|
378
|
+
*/
|
|
379
|
+
private async getIdentityKey (): Promise<PubKeyHex> {
|
|
380
|
+
if (this.cachedIdentityKey == null) {
|
|
381
|
+
this.cachedIdentityKey = (await this.wallet.getPublicKey({ identityKey: true }, this.config.originator)).publicKey
|
|
382
|
+
}
|
|
383
|
+
return this.cachedIdentityKey
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Queries the overlay service for KV entries.
|
|
388
|
+
*
|
|
389
|
+
* @param {KVStoreQuery} query - Query parameters sent to overlay
|
|
390
|
+
* @param {KVStoreGetOptions} options - Configuration options for the query
|
|
391
|
+
* @returns {Promise<KVStoreEntry[]>} Array of matching KV entries
|
|
392
|
+
* @private
|
|
393
|
+
*/
|
|
394
|
+
private async queryOverlay (query: KVStoreQuery, options: KVStoreGetOptions = {}): Promise<KVStoreEntry[]> {
|
|
395
|
+
const answer = await this.lookupResolver.query({
|
|
396
|
+
service: options.serviceName ?? this.config.serviceName as string,
|
|
397
|
+
query
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
if (answer.type !== 'output-list' || answer.outputs.length === 0) {
|
|
401
|
+
return []
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const entries: KVStoreEntry[] = []
|
|
405
|
+
|
|
406
|
+
for (const result of answer.outputs) {
|
|
407
|
+
try {
|
|
408
|
+
const tx = Transaction.fromBEEF(result.beef)
|
|
409
|
+
const output = tx.outputs[result.outputIndex]
|
|
410
|
+
const decoded = PushDrop.decode(output.lockingScript)
|
|
411
|
+
|
|
412
|
+
if (decoded.fields.length !== 5) {
|
|
413
|
+
continue
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Verify signature
|
|
417
|
+
const anyoneWallet = new ProtoWallet('anyone')
|
|
418
|
+
const signature = decoded.fields.pop() as number[]
|
|
419
|
+
try {
|
|
420
|
+
await anyoneWallet.verifySignature({
|
|
421
|
+
data: decoded.fields.reduce((a, e) => [...a, ...e], []),
|
|
422
|
+
signature,
|
|
423
|
+
counterparty: Utils.toHex(decoded.fields[kvProtocol.controller]),
|
|
424
|
+
protocolID: JSON.parse(Utils.toUTF8(decoded.fields[kvProtocol.protocolID])),
|
|
425
|
+
keyID: Utils.toUTF8(decoded.fields[kvProtocol.key])
|
|
426
|
+
})
|
|
427
|
+
} catch (error) {
|
|
428
|
+
// Skip all outputs that fail signature verification
|
|
429
|
+
continue
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const entry: KVStoreEntry = {
|
|
433
|
+
key: Utils.toUTF8(decoded.fields[kvProtocol.key]),
|
|
434
|
+
value: Utils.toUTF8(decoded.fields[kvProtocol.value]),
|
|
435
|
+
controller: Utils.toHex(decoded.fields[kvProtocol.controller]),
|
|
436
|
+
protocolID: JSON.parse(Utils.toUTF8(decoded.fields[kvProtocol.protocolID]))
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (options.includeToken === true) {
|
|
440
|
+
entry.token = {
|
|
441
|
+
txid: tx.id('hex'),
|
|
442
|
+
outputIndex: result.outputIndex,
|
|
443
|
+
beef: Beef.fromBinary(result.beef),
|
|
444
|
+
satoshis: output.satoshis ?? 1
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (options.history === true) {
|
|
449
|
+
entry.history = await this.historian.buildHistory(tx, {
|
|
450
|
+
key: entry.key,
|
|
451
|
+
protocolID: entry.protocolID
|
|
452
|
+
})
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
entries.push(entry)
|
|
456
|
+
} catch (error) {
|
|
457
|
+
continue
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return entries
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Submits a transaction to an overlay service using TopicBroadcaster.
|
|
466
|
+
* Broadcasts the transaction to the configured topics for network propagation.
|
|
467
|
+
*
|
|
468
|
+
* @param {Transaction} transaction - The transaction to broadcast.
|
|
469
|
+
* @returns {Promise<BroadcastResponse | BroadcastFailure>} The broadcast result.
|
|
470
|
+
* @throws {Error} If the broadcast fails or the network is unreachable.
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
private async submitToOverlay (transaction: Transaction): Promise<BroadcastResponse | BroadcastFailure> {
|
|
474
|
+
return await this.topicBroadcaster.broadcast(transaction)
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export default GlobalKVStore
|