From 4b6cc777cd4732eb96ee184d5adfd056b90018c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Szynwelski?= Date: Fri, 17 Nov 2023 13:20:59 +0100 Subject: [PATCH] Transaction confirmation in Warp gateway --- .../decentralized-sequencer/interactions.ts | 123 +++++++++++------- .../decentralized-sequencer/send-data-item.ts | 38 +++--- src/__tests__/unit/evaluation-options.test.ts | 4 +- src/contract/Contract.ts | 2 - src/contract/HandlerBasedContract.ts | 16 +-- src/contract/Signature.ts | 1 - .../sequencer/CentralizedSequencerClient.ts | 15 ++- .../sequencer/DecentralizedSequencerClient.ts | 86 ++++++------ src/contract/sequencer/SequencerClient.ts | 21 ++- src/core/modules/StateEvaluator.ts | 10 +- 10 files changed, 175 insertions(+), 141 deletions(-) diff --git a/src/__tests__/integration/decentralized-sequencer/interactions.ts b/src/__tests__/integration/decentralized-sequencer/interactions.ts index 655eedc4..72bca4cc 100644 --- a/src/__tests__/integration/decentralized-sequencer/interactions.ts +++ b/src/__tests__/integration/decentralized-sequencer/interactions.ts @@ -3,7 +3,7 @@ import Arweave from 'arweave'; import { JWKInterface } from 'arweave/node/lib/wallet'; import fs from 'fs'; import path from 'path'; -import { createServer, Server } from 'http'; +import { createServer, request, Server } from 'http'; import { DeployPlugin, ArweaveSigner } from 'warp-contracts-plugin-deploy'; import { Contract, WriteInteractionResponse } from '../../../contract/Contract'; import { Warp } from '../../../core/Warp'; @@ -11,6 +11,7 @@ import { WarpFactory, defaultCacheOptions, defaultWarpGwOptions } from '../../.. import { SourceType } from '../../../core/modules/impl/WarpGatewayInteractionsLoader'; import { AddressInfo } from 'net'; import { WARP_TAGS } from '../../../core/KnownTags'; +import { LoggerFactory } from '../../../logging/LoggerFactory'; interface ExampleContractState { counter: number; @@ -18,6 +19,7 @@ interface ExampleContractState { // FIXME: change to the address of the sequencer on dev const DECENTRALIZED_SEQUENCER_URL = 'http://sequencer-0.warp.cc:1317'; +const GW_URL = 'http://34.141.17.15:5666/'; describe('Testing sending of interactions to a decentralized sequencer', () => { let contractSrc: string; @@ -26,28 +28,21 @@ describe('Testing sending of interactions to a decentralized sequencer', () => { let arlocal: ArLocal; let warp: Warp; let contract: Contract; - let sequencerServer: Server; - let centralizedSeqeuencerUrl: string; + let mockGwServer: Server; + let mockGwUrl: string; let centralizedSequencerType: boolean; - - beforeAll(async () => { - const port = 1813; - arlocal = new ArLocal(port, false); - await arlocal.start(); - - const arweave = Arweave.init({ - host: 'localhost', - port: port, - protocol: 'http' - }); - - // a mock server simulating a centralized sequencer - centralizedSequencerType = false; - sequencerServer = createServer((req, res) => { + let confirmAnyTx: boolean; + + /** + * For testing purposes, operations returning the sequencer's address and registering/confirming interactions are mocked. + * Other requests are forwarded to the real Gateway. + */ + const mockGw = async () => { + mockGwServer = createServer((req, res) => { if (req.url === '/gateway/sequencer/address') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ - url: centralizedSequencerType ? centralizedSeqeuencerUrl : DECENTRALIZED_SEQUENCER_URL, + url: centralizedSequencerType ? mockGwUrl : DECENTRALIZED_SEQUENCER_URL, type: centralizedSequencerType ? 'centralized' : 'decentralized' })); return; @@ -56,16 +51,57 @@ describe('Testing sending of interactions to a decentralized sequencer', () => { res.writeHead(301, { Location: DECENTRALIZED_SEQUENCER_URL }); res.end(); return; + } else if (req.url?.startsWith('/gateway/interactions/')) { + res.writeHead(confirmAnyTx ? 200 : 204); + res.end(); + return; } - throw new Error("Unexpected sequencer path: " + req.url); - }) + + var options = { + hostname: new URL(GW_URL).hostname, + port: new URL(GW_URL).port, + path: req.url, + method: req.method, + headers: req.headers + }; + + var proxy = request(options, (gwRes) => { + if (gwRes.statusCode) { + res.writeHead(gwRes.statusCode, gwRes.headers) + gwRes.pipe(res, { + end: true + }); + } + }); + + req.pipe(proxy, { + end: true + }); + }); await new Promise(resolve => { - sequencerServer.listen(() => { - const address = sequencerServer.address() as AddressInfo - centralizedSeqeuencerUrl = `http://localhost:${address.port}` + mockGwServer.listen(() => { + const address = mockGwServer.address() as AddressInfo + mockGwUrl = `http://localhost:${address.port}` resolve() }) - }) + }); + } + + beforeAll(async () => { + LoggerFactory.INST.logLevel('debug'); + const port = 1813; + arlocal = new ArLocal(port, false); + await arlocal.start(); + + const arweave = Arweave.init({ + host: 'localhost', + port: port, + protocol: 'http' + }); + + centralizedSequencerType = false; + confirmAnyTx = false; + await mockGw(); const cacheOptions = { ...defaultCacheOptions, @@ -77,6 +113,7 @@ describe('Testing sending of interactions to a decentralized sequencer', () => { .custom(arweave, cacheOptions, 'custom') .useWarpGateway(gatewayOptions, cacheOptions) .build() + .useGwUrl(mockGwUrl) .use(new DeployPlugin()); ({ jwk: wallet } = await warp.generateWallet()); @@ -91,7 +128,7 @@ describe('Testing sending of interactions to a decentralized sequencer', () => { }); contract = warp.contract(contractTxId).setEvaluationOptions({ - sequencerUrl: centralizedSeqeuencerUrl + sequencerUrl: mockGwUrl }); contract.connect(wallet); @@ -100,7 +137,7 @@ describe('Testing sending of interactions to a decentralized sequencer', () => { afterAll(async () => { await arlocal.stop(); await new Promise(resolve => { - sequencerServer.close(resolve) + mockGwServer.close(resolve) }) }); @@ -110,41 +147,37 @@ describe('Testing sending of interactions to a decentralized sequencer', () => { if (tag.name === WARP_TAGS.SEQUENCER_NONCE) { return Number(tag.value) } - } + } } return -1 } - it('should add new interactions waiting for confirmation from the sequencer', async () => { - contract.setEvaluationOptions({ waitForConfirmation: true }) + it('should follow the redirection returned by the centralized sequencer.', async () => { + confirmAnyTx = true; + centralizedSequencerType = true; + contract.setEvaluationOptions({ + waitForConfirmation: true + }); await contract.writeInteraction({ function: 'add' }); const result = await contract.writeInteraction({ function: 'add' }); expect(getNonceFromResult(result)).toEqual(1) - expect(result?.bundlrResponse).toBeUndefined(); - expect(result?.sequencerTxHash).toBeDefined(); }); - it('should add new interactions without waiting for confirmation from the sequencer', async () => { - contract.setEvaluationOptions({ waitForConfirmation: false }) + it('should add new interactions waiting for confirmation from the gateway', async () => { + contract.setEvaluationOptions({ waitForConfirmation: true }) + setTimeout(() => confirmAnyTx = true, 2000); await contract.writeInteraction({ function: 'add' }); const result = await contract.writeInteraction({ function: 'add' }); expect(getNonceFromResult(result)).toEqual(3) - expect(result?.bundlrResponse).toBeUndefined(); - expect(result?.sequencerTxHash).toBeUndefined(); }); - it('should follow the redirection returned by the centralized sequencer.', async () => { - centralizedSequencerType = true; - contract.setEvaluationOptions({ - sequencerUrl: centralizedSeqeuencerUrl, - waitForConfirmation: true - }); + it('should add new interactions without waiting for confirmation from the gateway', async () => { + contract.setEvaluationOptions({ waitForConfirmation: false }) + await contract.writeInteraction({ function: 'add' }); const result = await contract.writeInteraction({ function: 'add' }); - expect(getNonceFromResult(result)).toEqual(4) - expect(result?.bundlrResponse).toBeUndefined(); - expect(result?.sequencerTxHash).toBeDefined(); + expect(getNonceFromResult(result)).toEqual(5) }); }); diff --git a/src/__tests__/integration/decentralized-sequencer/send-data-item.ts b/src/__tests__/integration/decentralized-sequencer/send-data-item.ts index 2b237b39..810ee60a 100644 --- a/src/__tests__/integration/decentralized-sequencer/send-data-item.ts +++ b/src/__tests__/integration/decentralized-sequencer/send-data-item.ts @@ -7,17 +7,18 @@ import { Tag } from '../../../utils/types/arweave-types'; import { WarpFactory } from '../../../core/WarpFactory'; import { WarpFetchWrapper } from '../../../core/WarpFetchWrapper'; import { Signature } from '../../../contract/Signature'; +import { SequencerClient } from '../../../contract/sequencer/SequencerClient'; // FIXME: change to the address of the sequencer on dev const SEQUENCER_URL = 'http://sequencer-0.warp.cc:1317'; +const GW_URL = 'http://34.141.17.15:5666/'; describe('Testing a decentralized sequencer client', () => { - let client: DecentralizedSequencerClient; - beforeAll(async () => { + const createClient = (): SequencerClient => { const warpFetchWrapper = new WarpFetchWrapper(WarpFactory.forLocal()) - client = new DecentralizedSequencerClient(SEQUENCER_URL, warpFetchWrapper); - }); + return new DecentralizedSequencerClient(SEQUENCER_URL, GW_URL, warpFetchWrapper); + } const createSignature = async (): Promise => { const wallet = await Arweave.crypto.generateJWK(); @@ -42,7 +43,8 @@ describe('Testing a decentralized sequencer client', () => { } it('should return consecutive nonces for a given signature', async () => { - const signature = await createSignature() + const client = createClient(); + const signature = await createSignature(); let nonce = await client.getNonce(signature); expect(nonce).toEqual(0); @@ -51,7 +53,8 @@ describe('Testing a decentralized sequencer client', () => { }); it('should reject a data item with an invalid nonce', async () => { - const signature = await createSignature() + const client = createClient(); + const signature = await createSignature(); const dataItem = await createDataItem(signature, 13); expect(client.sendDataItem(dataItem, false)) @@ -60,7 +63,8 @@ describe('Testing a decentralized sequencer client', () => { }); it('should reject a data item without nonce', async () => { - const signature = await createSignature() + const client = createClient(); + const signature = await createSignature(); const dataItem = await createDataItem(signature, 0, false); expect(client.sendDataItem(dataItem, true)) @@ -69,7 +73,8 @@ describe('Testing a decentralized sequencer client', () => { }); it('should reject a data item without contract', async () => { - const signature = await createSignature() + const client = createClient(); + const signature = await createSignature(); const dataItem = await createDataItem(signature, 0, true, false); expect(client.sendDataItem(dataItem, true)) @@ -78,7 +83,8 @@ describe('Testing a decentralized sequencer client', () => { }); it('should reject an unsigned data item', async () => { - const signature = await createSignature() + const client = createClient(); + const signature = await createSignature(); const dataItem = await createDataItem(signature, 0, true, true, false); expect(client.sendDataItem(dataItem, true)) @@ -86,25 +92,13 @@ describe('Testing a decentralized sequencer client', () => { .toThrowError('data item verification error'); }); - it('should return a confirmed result', async () => { - const signature = await createSignature(); - const nonce = await client.getNonce(signature); - const dataItem = await createDataItem(signature, nonce); - const result = await client.sendDataItem(dataItem, true); - - expect(result.sequencerMoved).toEqual(false); - expect(result.bundlrResponse).toBeUndefined(); - expect(result.sequencerTxHash).toBeDefined(); - }); - it('should return an unconfirmed result', async () => { + const client = createClient(); const signature = await createSignature(); const nonce = await client.getNonce(signature); const dataItem = await createDataItem(signature, nonce); const result = await client.sendDataItem(dataItem, false); expect(result.sequencerMoved).toEqual(false); - expect(result.bundlrResponse).toBeUndefined(); - expect(result.sequencerTxHash).toBeUndefined(); }); }); diff --git a/src/__tests__/unit/evaluation-options.test.ts b/src/__tests__/unit/evaluation-options.test.ts index f610be7d..71ad380d 100644 --- a/src/__tests__/unit/evaluation-options.test.ts +++ b/src/__tests__/unit/evaluation-options.test.ts @@ -21,7 +21,7 @@ describe('Evaluation options evaluator', () => { mineArLocalBlocks: true, remoteStateSyncEnabled: false, remoteStateSyncSource: 'https://dre-1.warp.cc/contract', - sequencerUrl: 'https://d1o5nlqr4okus2.cloudfront.net/', + sequencerUrl: undefined, sourceType: SourceType.BOTH, stackTrace: { saveState: false @@ -30,7 +30,7 @@ describe('Evaluation options evaluator', () => { unsafeClient: 'throw', updateCacheForEachInteraction: false, useKVStorage: false, - waitForConfirmation: false, + waitForConfirmation: undefined, useConstructor: false, walletBalanceUrl: 'http://nyc-1.dev.arweave.net:1984/', whitelistSources: [] diff --git a/src/contract/Contract.ts b/src/contract/Contract.ts index e0d10282..2f97a8b9 100644 --- a/src/contract/Contract.ts +++ b/src/contract/Contract.ts @@ -20,10 +20,8 @@ export interface BundlrResponse { } export interface WriteInteractionResponse { - bundlrResponse?: BundlrResponse; originalTxId: string; interactionTx: Transaction | DataItem; - sequencerTxHash?: string; } export interface DREContractStatusResponse { diff --git a/src/contract/HandlerBasedContract.ts b/src/contract/HandlerBasedContract.ts index 01a2f3a7..3883647a 100644 --- a/src/contract/HandlerBasedContract.ts +++ b/src/contract/HandlerBasedContract.ts @@ -341,14 +341,13 @@ export class HandlerBasedContract implements Contract { ); const sequencerClient = await this.getSequencerClient(); + const confirmInteraction = this._evaluationOptions.waitForConfirmation; const sendResponse = await sequencerClient.sendDataItem( interactionDataItem, - this._evaluationOptions.waitForConfirmation + confirmInteraction === undefined || confirmInteraction === true // by default, we wait for confirmation ); if (sendResponse.sequencerMoved) { - this.logger.info( - `The sequencer at the given address (${this._evaluationOptions.sequencerUrl}) is redirecting to a new sequencer` - ); + this.logger.info(`The sequencer is redirecting to a new sequencer`); if (sequencerRedirected) { throw new Error('Too many sequencer redirects'); } @@ -357,16 +356,14 @@ export class HandlerBasedContract implements Contract { } return { - bundlrResponse: sendResponse.bundlrResponse, originalTxId: await interactionDataItem.id, - interactionTx: interactionDataItem, - sequencerTxHash: sendResponse.sequencerTxHash + interactionTx: interactionDataItem }; } private async getSequencerClient(): Promise { if (!this._sequencerClient) { - this._sequencerClient = await createSequencerClient(this._evaluationOptions.sequencerUrl, this._warpFetchWrapper); + this._sequencerClient = await createSequencerClient(this.warp.gwUrl(), this._warpFetchWrapper); } return this._sequencerClient; } @@ -495,6 +492,9 @@ export class HandlerBasedContract implements Contract { connect(signature: ArWallet | CustomSignature | Signer): Contract { this._signature = new Signature(this.warp, signature); + if (this._sequencerClient) { + this._sequencerClient.clearNonce(); + } return this; } diff --git a/src/contract/Signature.ts b/src/contract/Signature.ts index e51eaec8..81297fb7 100644 --- a/src/contract/Signature.ts +++ b/src/contract/Signature.ts @@ -29,7 +29,6 @@ export class Signature { private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner'; private readonly wallet; private cachedAddress?: string; - sequencerNonce: number; constructor(warp: Warp, walletOrSignature: SignatureProvider) { this.warp = warp; diff --git a/src/contract/sequencer/CentralizedSequencerClient.ts b/src/contract/sequencer/CentralizedSequencerClient.ts index a61c6f98..6ea27f5b 100644 --- a/src/contract/sequencer/CentralizedSequencerClient.ts +++ b/src/contract/sequencer/CentralizedSequencerClient.ts @@ -1,19 +1,22 @@ -import { BundlrResponse } from 'contract/Contract'; import { WarpFetchWrapper } from 'core/WarpFetchWrapper'; import { NetworkCommunicationError } from '../../utils/utils'; import { DataItem } from 'warp-arbundles'; import { SendDataItemResponse, SequencerClient } from './SequencerClient'; +import { LoggerFactory } from '../../logging/LoggerFactory'; /** * Client for a centralized sequencer. */ export class CentralizedSequencerClient implements SequencerClient { + private readonly logger = LoggerFactory.INST.create('CentralizedSequencerClient'); + private registerUrl: string; private warpFetchWrapper: WarpFetchWrapper; constructor(sequencerUrl: string, warpFetchWrapper: WarpFetchWrapper) { this.registerUrl = `${sequencerUrl}/gateway/v2/sequencer/register`; this.warpFetchWrapper = warpFetchWrapper; + this.logger.info('The interactions will be sent to the centralized sequencer at the address', sequencerUrl); } /** @@ -24,6 +27,13 @@ export class CentralizedSequencerClient implements SequencerClient { return Promise.resolve(undefined); } + /** + * The sequencer does not have a nonce mechanism. + */ + clearNonce(): void { + // do nothing + } + /** * It sends an interaction to the sequencer and checks if the response has a status of 301 (Moved Permanently). */ @@ -39,16 +49,13 @@ export class CentralizedSequencerClient implements SequencerClient { }); if (response.ok) { - const bundlrResponse = (await response.json()) as BundlrResponse; return { - bundlrResponse, sequencerMoved: false }; } if (response.status == 301) { return { - bundlrResponse: undefined, sequencerMoved: true }; } diff --git a/src/contract/sequencer/DecentralizedSequencerClient.ts b/src/contract/sequencer/DecentralizedSequencerClient.ts index 51ebb36f..74b0209f 100644 --- a/src/contract/sequencer/DecentralizedSequencerClient.ts +++ b/src/contract/sequencer/DecentralizedSequencerClient.ts @@ -5,17 +5,13 @@ import { LoggerFactory } from '../../logging/LoggerFactory'; import { WarpFetchWrapper } from '../../core/WarpFetchWrapper'; import { SendDataItemResponse, SequencerClient } from './SequencerClient'; import { Signature } from 'contract/Signature'; +import { Benchmark } from '../../logging/Benchmark'; type NonceResponse = { address: string; nonce: number; }; -type CheckTxResponse = { - confirmed: boolean; - txHash?: string; -}; - /** * Client for a decentralized sequencer. */ @@ -26,12 +22,15 @@ export class DecentralizedSequencerClient implements SequencerClient { private sendDataItemUrl: string; private getTxUrl: string; private warpFetchWrapper: WarpFetchWrapper; + private nonce: number | undefined; - constructor(sequencerUrl: string, warpFetchWrapper: WarpFetchWrapper) { + constructor(sequencerUrl: string, gatewayUrl: string, warpFetchWrapper: WarpFetchWrapper) { this.nonceUrl = `${sequencerUrl}/api/v1/nonce`; this.sendDataItemUrl = `${sequencerUrl}/api/v1/dataitem`; - this.getTxUrl = `${sequencerUrl}/api/v1/tx-data-item-id`; + this.getTxUrl = `${gatewayUrl}/gateway/interactions/`; this.warpFetchWrapper = warpFetchWrapper; + this.nonce = undefined; + this.logger.info('The interactions will be sent to the decentralized sequencer at the address', sequencerUrl); } /** @@ -42,13 +41,12 @@ export class DecentralizedSequencerClient implements SequencerClient { * @returns nonce */ async getNonce(signature: Signature): Promise { - const nonce = signature.sequencerNonce; - if (nonce !== undefined) { - signature.sequencerNonce = nonce + 1; - return nonce; + if (this.nonce === undefined) { + this.nonce = await this.fetchNonce(signature); + } else { + this.nonce = this.nonce + 1; } - - return this.fetchNonce(signature); + return this.nonce; } /** @@ -75,10 +73,17 @@ export class DecentralizedSequencerClient implements SequencerClient { const nonceResponse = await getJsonResponse(response); this.logger.info('Nonce for owner', { owner, nonceResponse }); - signature.sequencerNonce = nonceResponse.nonce + 1; + this.nonce = nonceResponse.nonce + 1; return nonceResponse.nonce; } + /** + * Clears the stored nonce value. The next call to {@link getNonce} will request a new value from the sequencer. + */ + clearNonce(): void { + this.nonce = undefined; + } + /** * Broadcasts a data item to the sequencer network and optionally monitoring its inclusion in the blockchain. * If the broadcasting is rejected by the node (e.g., during the CheckTx method), an error is thrown. @@ -90,13 +95,19 @@ export class DecentralizedSequencerClient implements SequencerClient { * @returns hash of the sequencer transaction if wait for confirmation is selected */ async sendDataItem(dataItem: DataItem, waitForConfirmation: boolean): Promise { - const response = await this.sendDataItemWithRetry(dataItem); + await this.sendDataItemWithRetry(dataItem); if (waitForConfirmation) { - response.sequencerTxHash = await this.confirmTx(await dataItem.id); + const dataItemId = await dataItem.id; + this.logger.info('Waiting for confirmation of', dataItemId); + const benchmark = Benchmark.measure(); + await this.confirmTx(dataItemId); + this.logger.info('Transaction confirmed after', benchmark.elapsed()); } - return response; + return { + sequencerMoved: false + }; } /** @@ -106,16 +117,15 @@ export class DecentralizedSequencerClient implements SequencerClient { * @param dataItem data item to be sent * @param numberOfTries the number of retries */ - private async sendDataItemWithRetry(dataItem: DataItem, numberOfTries = 20): Promise { + private async sendDataItemWithRetry(dataItem: DataItem, numberOfTries = 20): Promise { if (numberOfTries <= 0) { throw new Error( `Failed to send the interaction (id = ${await dataItem.id}) to the sequencer despite multiple retries` ); } - if (await this.tryToSendDataItem(dataItem)) { - return { sequencerMoved: false }; - } else { + const dataItemSent = await this.tryToSendDataItem(dataItem); + if (!dataItemSent) { await sleep(1000); return this.sendDataItemWithRetry(dataItem, numberOfTries - 1); } @@ -166,38 +176,30 @@ export class DecentralizedSequencerClient implements SequencerClient { * @param dataItem data item to be sent * @param numberOfTries the number of retries */ - private async confirmTx(dataItemId: string, numberOfTries = 20): Promise { + private async confirmTx(dataItemId: string, numberOfTries = 20): Promise { if (numberOfTries <= 0) { - throw new Error(`Failed to confirm of the interaction with id = ${dataItemId} in the sequencer network`); + throw new Error(`Failed to confirm of the interaction with id = ${dataItemId}`); } - await sleep(1000); - - const result = await this.checkTx(dataItemId); - if (!result.confirmed) { + await sleep(500); + const confirmed = await this.checkTx(dataItemId); + if (!confirmed) { return this.confirmTx(dataItemId, numberOfTries - 1); } - return result.txHash; } - private async checkTx(dataItemId: string): Promise { - const response = await this.warpFetchWrapper.fetch(this.getTxUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ data_item_id: dataItemId }) - }); + private async checkTx(dataItemId: string): Promise { + const response = await this.warpFetchWrapper.fetch(this.getTxUrl + dataItemId); - if (response.ok) { - const result = await response.json(); - this.logger.info(`The transaction with hash ${result.tx_hash} confirmed.`); - return { confirmed: true, txHash: result.tx_hash }; + if (response.status == 200) { + const result = await response.text(); + this.logger.info(`The interaction confirmed: ${result}!`); + return true; } - if (response.status == 404) { + if (response.status == 204) { this.logger.debug(`The transaction with data item id (${dataItemId}) not confirmed yet.`); - return { confirmed: false }; + return false; } const text = await response.text(); diff --git a/src/contract/sequencer/SequencerClient.ts b/src/contract/sequencer/SequencerClient.ts index fe5be510..da9a45ba 100644 --- a/src/contract/sequencer/SequencerClient.ts +++ b/src/contract/sequencer/SequencerClient.ts @@ -1,4 +1,3 @@ -import { BundlrResponse } from 'contract/Contract'; import { Signature } from 'contract/Signature'; import { getJsonResponse, stripTrailingSlash } from '../../utils/utils'; import { DataItem } from 'warp-arbundles'; @@ -14,14 +13,6 @@ export type SendDataItemResponse = { * Whether the sequencer returned a "Moved Permanently" status with the address of the new sequencer */ sequencerMoved: boolean; - /** - * The response from the bundler if the sequencer sends an interaction there - */ - bundlrResponse?: BundlrResponse; - /** - * The transaction hash in the decentralized sequencer blockchain containing the data item - */ - sequencerTxHash?: string; }; /** @@ -34,6 +25,11 @@ export interface SequencerClient { */ getNonce(signature: Signature): Promise; + /** + * Clears the stored nonce value. + */ + clearNonce(): void; + /** * It sends an interaction in the form of a data item to the sequencer. * Potentially waits for confirmation that the interaction has been included in the sequencer chain. @@ -62,14 +58,15 @@ type SequencerAddress = { * It queries an endpoint with an address and sequencer type, and returns a client for that sequencer. * * @param sequencerUrl URL address with an endpoint that returns the sequencer's address + * @param gatewayUrl Warp gateway URL * @param warpFetchWrapper wrapper for fetch operation * @returns client for the sequencer */ export const createSequencerClient = async ( - sequencerUrl: string, + gatewayUrl: string, warpFetchWrapper: WarpFetchWrapper ): Promise => { - const response = warpFetchWrapper.fetch(`${stripTrailingSlash(sequencerUrl)}/gateway/sequencer/address`); + const response = warpFetchWrapper.fetch(`${stripTrailingSlash(gatewayUrl)}/gateway/sequencer/address`); const address = await getJsonResponse(response); if (address.type == 'centralized') { @@ -77,7 +74,7 @@ export const createSequencerClient = async ( } if (address.type == 'decentralized') { - return new DecentralizedSequencerClient(address.url, warpFetchWrapper); + return new DecentralizedSequencerClient(address.url, gatewayUrl, warpFetchWrapper); } throw new Error('Unknown sequencer type: ' + address.type); diff --git a/src/core/modules/StateEvaluator.ts b/src/core/modules/StateEvaluator.ts index b4781058..2b2933ff 100644 --- a/src/core/modules/StateEvaluator.ts +++ b/src/core/modules/StateEvaluator.ts @@ -111,7 +111,8 @@ export class DefaultEvaluationOptions implements EvaluationOptions { // (e.g. when using unsafe client and Arweave does not respond properly for a while) ignoreExceptions = true; - waitForConfirmation = false; + // by default, the option is not set, which will result in confirming L2 interactions but not L1 + waitForConfirmation = undefined; updateCacheForEachInteraction = false; @@ -125,7 +126,7 @@ export class DefaultEvaluationOptions implements EvaluationOptions { saveState: false }; - sequencerUrl = `https://d1o5nlqr4okus2.cloudfront.net/`; + sequencerUrl = undefined; gasLimit = Number.MAX_SAFE_INTEGER; @@ -161,7 +162,7 @@ export interface EvaluationOptions { // Allows waiting for confirmation of the interaction. // In the case of the 'disableBundling' option, the confirmation comes from the Arweave network, - // otherwise from the decentralized Warp Sequencer. + // otherwise from the Warp Gateway. waitForConfirmation: boolean; // whether the state cache should be updated after evaluating each interaction transaction. @@ -197,6 +198,9 @@ export interface EvaluationOptions { saveState: boolean; }; + // Sequencer URL. + // During the transitional stage between the centralized and decentralized sequencers, + // this value will not be used, and the SDK will query the Warp Gateway for the address. sequencerUrl: string; gasLimit: number;