@mysten/sui
Version:
Sui TypeScript API
1 lines • 24.4 kB
Source Map (JSON)
{"version":3,"file":"parallel.mjs","names":["#signer","#client","#gasMode","#coinBatchSize","#initialCoinBalance","#minimumCoinBalance","#sourceCoins","#defaultGasBudget","#maxPoolSize","#cache","#executeQueue","#epochInfo","#updateCache","#waitForLastDigest","#getUsedObjects","#execute","#objectIdQueues","#buildQueue","#getGasPrice","#pendingTransactions","#getValidDuringExpiration","#getGasCoin","#coinPool","#lastDigest","#cacheLock","#refillCoinPool","#ensureEpochInfo","#epochInfoPromise","#fetchEpochInfo"],"sources":["../../../src/transactions/executor/parallel.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { promiseWithResolvers } from '@mysten/utils';\nimport type { SuiObjectRef } from '../../bcs/types.js';\nimport type { ClientWithCoreApi } from '../../client/core.js';\nimport { coreClientResolveTransactionPlugin } from '../../client/core-resolver.js';\nimport type { SuiClientTypes } from '../../client/types.js';\nimport type { Signer } from '../../cryptography/index.js';\nimport type { ObjectCacheOptions } from '../ObjectCache.js';\nimport { Transaction } from '../Transaction.js';\nimport { TransactionDataBuilder } from '../TransactionData.js';\nimport { CachingTransactionExecutor } from './caching.js';\nimport { ParallelQueue, SerialQueue } from './queue.js';\n\nconst PARALLEL_EXECUTOR_DEFAULTS = {\n\tcoinBatchSize: 20,\n\tinitialCoinBalance: 200_000_000n,\n\tminimumCoinBalance: 50_000_000n,\n\tmaxPoolSize: 50,\n} satisfies Partial<ParallelTransactionExecutorCoinOptions>;\n\nconst EPOCH_BOUNDARY_WINDOW = 60_000;\n\ninterface ParallelTransactionExecutorBaseOptions extends Omit<ObjectCacheOptions, 'address'> {\n\tclient: ClientWithCoreApi;\n\tsigner: Signer;\n\t/** The gasBudget to use if the transaction has not defined it's own gasBudget, defaults to `minimumCoinBalance` */\n\tdefaultGasBudget?: bigint;\n\t/** The maximum number of transactions that can be execute in parallel, this also determines the maximum number of gas coins that will be created */\n\tmaxPoolSize?: number;\n}\n\nexport interface ParallelTransactionExecutorCoinOptions extends ParallelTransactionExecutorBaseOptions {\n\t/** Gas mode - use owned coins for gas payments (default) */\n\tgasMode?: 'coins';\n\t/** The number of coins to create in a batch when refilling the gas pool */\n\tcoinBatchSize?: number;\n\t/** The initial balance of each coin created for the gas pool */\n\tinitialCoinBalance?: bigint;\n\t/** The minimum balance of a coin that can be reused for future transactions. If the gasCoin is below this value, it will be used when refilling the gasPool */\n\tminimumCoinBalance?: bigint;\n\t/** An initial list of coins used to fund the gas pool, uses all owned SUI coins by default */\n\tsourceCoins?: string[];\n}\n\nexport interface ParallelTransactionExecutorAddressBalanceOptions extends ParallelTransactionExecutorBaseOptions {\n\t/** Gas mode - use address balance for gas payments instead of owned coins */\n\tgasMode: 'addressBalance';\n}\n\n/** Options for ParallelTransactionExecutor - discriminated union based on gasMode */\nexport type ParallelTransactionExecutorOptions =\n\t| ParallelTransactionExecutorCoinOptions\n\t| ParallelTransactionExecutorAddressBalanceOptions;\n\ninterface CoinWithBalance {\n\tid: string;\n\tversion: string;\n\tdigest: string;\n\tbalance: bigint;\n}\nexport class ParallelTransactionExecutor {\n\t#signer: Signer;\n\t#client: ClientWithCoreApi;\n\t#gasMode: 'coins' | 'addressBalance';\n\t#coinBatchSize: number;\n\t#initialCoinBalance: bigint;\n\t#minimumCoinBalance: bigint;\n\t#defaultGasBudget: bigint;\n\t#maxPoolSize: number;\n\t#sourceCoins: Map<string, SuiObjectRef | null> | null;\n\t#coinPool: CoinWithBalance[] = [];\n\t#cache: CachingTransactionExecutor;\n\t#objectIdQueues = new Map<string, (() => void)[]>();\n\t#buildQueue = new SerialQueue();\n\t#executeQueue: ParallelQueue;\n\t#lastDigest: string | null = null;\n\t#cacheLock: Promise<void> | null = null;\n\t#pendingTransactions = 0;\n\t#epochInfo: null | {\n\t\tepoch: string;\n\t\tprice: bigint;\n\t\texpiration: number;\n\t\tchainIdentifier: string;\n\t} = null;\n\t#epochInfoPromise: Promise<void> | null = null;\n\n\tconstructor(options: ParallelTransactionExecutorOptions) {\n\t\tthis.#signer = options.signer;\n\t\tthis.#client = options.client;\n\t\tthis.#gasMode = options.gasMode ?? 'coins';\n\n\t\tif (this.#gasMode === 'coins') {\n\t\t\tconst coinOptions = options as ParallelTransactionExecutorCoinOptions;\n\t\t\tthis.#coinBatchSize = coinOptions.coinBatchSize ?? PARALLEL_EXECUTOR_DEFAULTS.coinBatchSize;\n\t\t\tthis.#initialCoinBalance =\n\t\t\t\tcoinOptions.initialCoinBalance ?? PARALLEL_EXECUTOR_DEFAULTS.initialCoinBalance;\n\t\t\tthis.#minimumCoinBalance =\n\t\t\t\tcoinOptions.minimumCoinBalance ?? PARALLEL_EXECUTOR_DEFAULTS.minimumCoinBalance;\n\t\t\tthis.#sourceCoins = coinOptions.sourceCoins\n\t\t\t\t? new Map(coinOptions.sourceCoins.map((id) => [id, null]))\n\t\t\t\t: null;\n\t\t} else {\n\t\t\tthis.#coinBatchSize = 0;\n\t\t\tthis.#initialCoinBalance = 0n;\n\t\t\tthis.#minimumCoinBalance = PARALLEL_EXECUTOR_DEFAULTS.minimumCoinBalance;\n\t\t\tthis.#sourceCoins = null;\n\t\t}\n\n\t\tthis.#defaultGasBudget = options.defaultGasBudget ?? this.#minimumCoinBalance;\n\t\tthis.#maxPoolSize = options.maxPoolSize ?? PARALLEL_EXECUTOR_DEFAULTS.maxPoolSize;\n\t\tthis.#cache = new CachingTransactionExecutor({\n\t\t\tclient: options.client,\n\t\t\tcache: options.cache,\n\t\t});\n\t\tthis.#executeQueue = new ParallelQueue(this.#maxPoolSize);\n\t}\n\n\tresetCache() {\n\t\tthis.#epochInfo = null;\n\t\treturn this.#updateCache(() => this.#cache.reset());\n\t}\n\n\tasync waitForLastTransaction() {\n\t\tawait this.#updateCache(() => this.#waitForLastDigest());\n\t}\n\n\tasync executeTransaction<Include extends SuiClientTypes.TransactionInclude = {}>(\n\t\ttransaction: Transaction,\n\t\tinclude?: Include & SuiClientTypes.TransactionInclude,\n\t\tadditionalSignatures: string[] = [],\n\t): Promise<SuiClientTypes.TransactionResult<Include & { effects: true }>> {\n\t\tconst { promise, resolve, reject } =\n\t\t\tpromiseWithResolvers<SuiClientTypes.TransactionResult<Include & { effects: true }>>();\n\t\tconst usedObjects = await this.#getUsedObjects(transaction);\n\n\t\tconst execute = () => {\n\t\t\tthis.#executeQueue.runTask(() => {\n\t\t\t\tconst promise = this.#execute(transaction, usedObjects, include, additionalSignatures);\n\n\t\t\t\treturn promise.then(resolve, reject);\n\t\t\t});\n\t\t};\n\n\t\tconst conflicts = new Set<string>();\n\n\t\tusedObjects.forEach((objectId) => {\n\t\t\tconst queue = this.#objectIdQueues.get(objectId);\n\t\t\tif (queue) {\n\t\t\t\tconflicts.add(objectId);\n\t\t\t\tthis.#objectIdQueues.get(objectId)!.push(() => {\n\t\t\t\t\tconflicts.delete(objectId);\n\t\t\t\t\tif (conflicts.size === 0) {\n\t\t\t\t\t\texecute();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.#objectIdQueues.set(objectId, []);\n\t\t\t}\n\t\t});\n\n\t\tif (conflicts.size === 0) {\n\t\t\texecute();\n\t\t}\n\n\t\treturn promise;\n\t}\n\n\tasync #getUsedObjects(transaction: Transaction) {\n\t\tconst usedObjects = new Set<string>();\n\t\tlet serialized = false;\n\n\t\ttransaction.addSerializationPlugin(async (blockData, _options, next) => {\n\t\t\tawait next();\n\n\t\t\tif (serialized) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tserialized = true;\n\n\t\t\tblockData.inputs.forEach((input) => {\n\t\t\t\tif (input.Object?.ImmOrOwnedObject?.objectId) {\n\t\t\t\t\tusedObjects.add(input.Object.ImmOrOwnedObject.objectId);\n\t\t\t\t} else if (input.Object?.Receiving?.objectId) {\n\t\t\t\t\tusedObjects.add(input.Object.Receiving.objectId);\n\t\t\t\t} else if (\n\t\t\t\t\tinput.UnresolvedObject?.objectId &&\n\t\t\t\t\t!input.UnresolvedObject.initialSharedVersion\n\t\t\t\t) {\n\t\t\t\t\tusedObjects.add(input.UnresolvedObject.objectId);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tawait transaction.prepareForSerialization({ client: this.#client });\n\n\t\treturn usedObjects;\n\t}\n\n\tasync #execute<Include extends SuiClientTypes.TransactionInclude = {}>(\n\t\ttransaction: Transaction,\n\t\tusedObjects: Set<string>,\n\t\tinclude?: Include,\n\t\tadditionalSignatures: string[] = [],\n\t): Promise<SuiClientTypes.TransactionResult<Include & { effects: true }>> {\n\t\tlet gasCoin: CoinWithBalance | null = null;\n\t\ttry {\n\t\t\ttransaction.setSenderIfNotSet(this.#signer.toSuiAddress());\n\n\t\t\tawait this.#buildQueue.runTask(async () => {\n\t\t\t\tconst data = transaction.getData();\n\n\t\t\t\tif (!data.gasData.price) {\n\t\t\t\t\ttransaction.setGasPrice(await this.#getGasPrice());\n\t\t\t\t}\n\n\t\t\t\ttransaction.setGasBudgetIfNotSet(this.#defaultGasBudget);\n\n\t\t\t\tawait this.#updateCache();\n\t\t\t\tthis.#pendingTransactions++;\n\n\t\t\t\tif (this.#gasMode === 'addressBalance') {\n\t\t\t\t\t// Address balance mode: use empty gas payment with ValidDuring expiration\n\t\t\t\t\ttransaction.setGasPayment([]);\n\t\t\t\t\ttransaction.setExpiration(await this.#getValidDuringExpiration());\n\t\t\t\t} else {\n\t\t\t\t\t// Coin mode: use gas coin from pool\n\t\t\t\t\tgasCoin = await this.#getGasCoin();\n\t\t\t\t\ttransaction.setGasPayment([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tobjectId: gasCoin.id,\n\t\t\t\t\t\t\tversion: gasCoin.version,\n\t\t\t\t\t\t\tdigest: gasCoin.digest,\n\t\t\t\t\t\t},\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\t// Resolve cached references\n\t\t\t\tawait this.#cache.buildTransaction({ transaction, onlyTransactionKind: true });\n\t\t\t});\n\n\t\t\tconst bytes = await transaction.build({ client: this.#client });\n\n\t\t\tconst { signature } = await this.#signer.signTransaction(bytes);\n\n\t\t\tconst results = await this.#cache.executeTransaction({\n\t\t\t\ttransaction: bytes,\n\t\t\t\tsignatures: [signature, ...additionalSignatures],\n\t\t\t\tinclude,\n\t\t\t});\n\n\t\t\tconst tx = results.$kind === 'Transaction' ? results.Transaction : results.FailedTransaction;\n\t\t\tconst effects = tx.effects!;\n\t\t\tconst gasObject = effects.gasObject;\n\t\t\tconst gasUsed = effects.gasUsed;\n\n\t\t\tif (gasCoin && gasUsed && gasObject) {\n\t\t\t\tconst coin = gasCoin as CoinWithBalance;\n\t\t\t\tconst gasOwner = gasObject.outputOwner?.AddressOwner ?? gasObject.outputOwner?.ObjectOwner;\n\n\t\t\t\tif (gasOwner === this.#signer.toSuiAddress()) {\n\t\t\t\t\tconst totalUsed =\n\t\t\t\t\t\tBigInt(gasUsed.computationCost) +\n\t\t\t\t\t\tBigInt(gasUsed.storageCost) -\n\t\t\t\t\t\tBigInt(gasUsed.storageRebate);\n\t\t\t\t\tconst remainingBalance = coin.balance - totalUsed;\n\n\t\t\t\t\tlet usesGasCoin = false;\n\t\t\t\t\tnew TransactionDataBuilder(transaction.getData()).mapArguments((arg) => {\n\t\t\t\t\t\tif (arg.$kind === 'GasCoin') {\n\t\t\t\t\t\t\tusesGasCoin = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn arg;\n\t\t\t\t\t});\n\n\t\t\t\t\tconst gasRef = {\n\t\t\t\t\t\tobjectId: gasObject.objectId,\n\t\t\t\t\t\tversion: gasObject.outputVersion!,\n\t\t\t\t\t\tdigest: gasObject.outputDigest!,\n\t\t\t\t\t};\n\n\t\t\t\t\tif (!usesGasCoin && remainingBalance >= this.#minimumCoinBalance) {\n\t\t\t\t\t\tthis.#coinPool.push({\n\t\t\t\t\t\t\tid: gasRef.objectId,\n\t\t\t\t\t\t\tversion: gasRef.version,\n\t\t\t\t\t\t\tdigest: gasRef.digest,\n\t\t\t\t\t\t\tbalance: remainingBalance,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!this.#sourceCoins) {\n\t\t\t\t\t\t\tthis.#sourceCoins = new Map();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.#sourceCoins.set(gasRef.objectId, gasRef);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.#lastDigest = tx.digest;\n\n\t\t\treturn results as SuiClientTypes.TransactionResult<Include & { effects: true }>;\n\t\t} catch (error) {\n\t\t\tif (gasCoin) {\n\t\t\t\tif (!this.#sourceCoins) {\n\t\t\t\t\tthis.#sourceCoins = new Map();\n\t\t\t\t}\n\n\t\t\t\tthis.#sourceCoins.set((gasCoin as CoinWithBalance).id, null);\n\t\t\t}\n\n\t\t\tawait this.#updateCache(async () => {\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tthis.#cache.cache.deleteObjects([...usedObjects]),\n\t\t\t\t\tthis.#waitForLastDigest(),\n\t\t\t\t]);\n\t\t\t});\n\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tusedObjects.forEach((objectId) => {\n\t\t\t\tconst queue = this.#objectIdQueues.get(objectId);\n\t\t\t\tif (queue && queue.length > 0) {\n\t\t\t\t\tqueue.shift()!();\n\t\t\t\t} else if (queue) {\n\t\t\t\t\tthis.#objectIdQueues.delete(objectId);\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.#pendingTransactions--;\n\t\t}\n\t}\n\n\t/** Helper for synchronizing cache updates, by ensuring only one update happens at a time. This can also be used to wait for any pending cache updates */\n\tasync #updateCache(fn?: () => Promise<void>) {\n\t\tif (this.#cacheLock) {\n\t\t\tawait this.#cacheLock;\n\t\t}\n\n\t\tthis.#cacheLock =\n\t\t\tfn?.().then(\n\t\t\t\t() => {\n\t\t\t\t\tthis.#cacheLock = null;\n\t\t\t\t},\n\t\t\t\t() => {},\n\t\t\t) ?? null;\n\t}\n\n\tasync #waitForLastDigest() {\n\t\tconst digest = this.#lastDigest;\n\t\tif (digest) {\n\t\t\tthis.#lastDigest = null;\n\t\t\tawait this.#client.core.waitForTransaction({ digest });\n\t\t}\n\t}\n\n\tasync #getGasCoin() {\n\t\tif (this.#coinPool.length === 0 && this.#pendingTransactions <= this.#maxPoolSize) {\n\t\t\tawait this.#refillCoinPool();\n\t\t}\n\n\t\tif (this.#coinPool.length === 0) {\n\t\t\tthrow new Error('No coins available');\n\t\t}\n\n\t\tconst coin = this.#coinPool.shift()!;\n\t\treturn coin;\n\t}\n\n\tasync #getGasPrice(): Promise<bigint> {\n\t\tawait this.#ensureEpochInfo();\n\t\treturn this.#epochInfo!.price;\n\t}\n\n\tasync #getValidDuringExpiration() {\n\t\tawait this.#ensureEpochInfo();\n\t\tconst currentEpoch = BigInt(this.#epochInfo!.epoch);\n\t\treturn {\n\t\t\tValidDuring: {\n\t\t\t\tminEpoch: String(currentEpoch),\n\t\t\t\tmaxEpoch: String(currentEpoch + 1n),\n\t\t\t\tminTimestamp: null,\n\t\t\t\tmaxTimestamp: null,\n\t\t\t\tchain: this.#epochInfo!.chainIdentifier,\n\t\t\t\tnonce: (Math.random() * 0x100000000) >>> 0,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync #ensureEpochInfo(): Promise<void> {\n\t\tif (this.#epochInfo && this.#epochInfo.expiration - EPOCH_BOUNDARY_WINDOW - Date.now() > 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#epochInfoPromise) {\n\t\t\tawait this.#epochInfoPromise;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#epochInfoPromise = this.#fetchEpochInfo();\n\t\ttry {\n\t\t\tawait this.#epochInfoPromise;\n\t\t} finally {\n\t\t\tthis.#epochInfoPromise = null;\n\t\t}\n\t}\n\n\tasync #fetchEpochInfo(): Promise<void> {\n\t\tconst [{ systemState }, { chainIdentifier }] = await Promise.all([\n\t\t\tthis.#client.core.getCurrentSystemState(),\n\t\t\tthis.#client.core.getChainIdentifier(),\n\t\t]);\n\n\t\tthis.#epochInfo = {\n\t\t\tepoch: systemState.epoch,\n\t\t\tprice: BigInt(systemState.referenceGasPrice),\n\t\t\texpiration:\n\t\t\t\tNumber(systemState.epochStartTimestampMs) + Number(systemState.parameters.epochDurationMs),\n\t\t\tchainIdentifier,\n\t\t};\n\t}\n\n\tasync #refillCoinPool() {\n\t\tconst batchSize = Math.min(\n\t\t\tthis.#coinBatchSize,\n\t\t\tthis.#maxPoolSize - (this.#coinPool.length + this.#pendingTransactions) + 1,\n\t\t);\n\n\t\tif (batchSize === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst txb = new Transaction();\n\t\tconst address = this.#signer.toSuiAddress();\n\t\ttxb.setSender(address);\n\n\t\tif (this.#sourceCoins) {\n\t\t\tconst refs = [];\n\t\t\tconst ids = [];\n\t\t\tfor (const [id, ref] of this.#sourceCoins) {\n\t\t\t\tif (ref) {\n\t\t\t\t\trefs.push(ref);\n\t\t\t\t} else {\n\t\t\t\t\tids.push(id);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (ids.length > 0) {\n\t\t\t\tconst { objects } = await this.#client.core.getObjects({\n\t\t\t\t\tobjectIds: ids,\n\t\t\t\t});\n\t\t\t\trefs.push(\n\t\t\t\t\t...objects\n\t\t\t\t\t\t.filter((obj): obj is SuiClientTypes.Object => !(obj instanceof Error))\n\t\t\t\t\t\t.map((obj) => ({\n\t\t\t\t\t\t\tobjectId: obj.objectId,\n\t\t\t\t\t\t\tversion: obj.version,\n\t\t\t\t\t\t\tdigest: obj.digest,\n\t\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\ttxb.setGasPayment(refs);\n\t\t\tthis.#sourceCoins = new Map();\n\t\t}\n\n\t\tconst amounts = new Array(batchSize).fill(this.#initialCoinBalance);\n\t\tconst splitResults = txb.splitCoins(txb.gas, amounts);\n\t\tconst coinResults = [];\n\t\tfor (let i = 0; i < amounts.length; i++) {\n\t\t\tcoinResults.push(splitResults[i]);\n\t\t}\n\t\ttxb.transferObjects(coinResults, address);\n\n\t\tawait this.waitForLastTransaction();\n\n\t\ttxb.addBuildPlugin(coreClientResolveTransactionPlugin);\n\t\tconst bytes = await txb.build({ client: this.#client });\n\t\tconst { signature } = await this.#signer.signTransaction(bytes);\n\n\t\tconst result = await this.#client.core.executeTransaction({\n\t\t\ttransaction: bytes,\n\t\t\tsignatures: [signature],\n\t\t\tinclude: { effects: true },\n\t\t});\n\n\t\tconst tx = result.$kind === 'Transaction' ? result.Transaction : result.FailedTransaction;\n\t\tconst effects = tx.effects!;\n\n\t\teffects.changedObjects.forEach((changedObj) => {\n\t\t\tif (\n\t\t\t\tchangedObj.objectId === effects.gasObject?.objectId ||\n\t\t\t\tchangedObj.outputState !== 'ObjectWrite'\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.#coinPool.push({\n\t\t\t\tid: changedObj.objectId,\n\t\t\t\tversion: changedObj.outputVersion!,\n\t\t\t\tdigest: changedObj.outputDigest!,\n\t\t\t\tbalance: BigInt(this.#initialCoinBalance),\n\t\t\t});\n\t\t});\n\n\t\tif (!this.#sourceCoins) {\n\t\t\tthis.#sourceCoins = new Map();\n\t\t}\n\n\t\tconst gasObject = effects.gasObject!;\n\t\tthis.#sourceCoins!.set(gasObject.objectId, {\n\t\t\tobjectId: gasObject.objectId,\n\t\t\tversion: gasObject.outputVersion!,\n\t\t\tdigest: gasObject.outputDigest!,\n\t\t});\n\n\t\tawait this.#client.core.waitForTransaction({ digest: tx.digest });\n\t}\n}\n"],"mappings":"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"}