Skip to content

Commit

Permalink
refactor!: rework TxSubmitProvider to submit transactions as hex stri…
Browse files Browse the repository at this point in the history
…ng instead of Buffer
  • Loading branch information
Juan Cruz authored and rhyslbw committed Sep 5, 2022
1 parent dee30b5 commit 032a1b7
Show file tree
Hide file tree
Showing 16 changed files with 67 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Cardano, ProviderError, ProviderFailure } from '@cardano-sdk/core';
import { axiosError } from '../util';
import { bufferToHexString } from '@cardano-sdk/util';
import { logger } from '@cardano-sdk/util-dev';
import { txSubmitHttpProvider } from '../../src';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';

const config = { baseUrl: 'http://some-hostname:3000/tx-submit', logger };

const emptyUintArrayAsHexString = bufferToHexString(Buffer.from(new Uint8Array()));

describe('txSubmitHttpProvider', () => {
describe('healthCheck', () => {
it('is not ok if cannot connect', async () => {
Expand Down Expand Up @@ -46,7 +49,7 @@ describe('txSubmitHttpProvider', () => {
it('resolves if successful', async () => {
axiosMock.onPost().replyOnce(200, '');
const provider = txSubmitHttpProvider(config);
await expect(provider.submitTx({ signedTransaction: new Uint8Array() })).resolves.not.toThrow();
await expect(provider.submitTx({ signedTransaction: emptyUintArrayAsHexString })).resolves.not.toThrow();
});

describe('errors', () => {
Expand All @@ -56,7 +59,7 @@ describe('txSubmitHttpProvider', () => {
throw axiosError(bodyError);
});
const provider = txSubmitHttpProvider(config);
await provider.submitTx({ signedTransaction: new Uint8Array() });
await provider.submitTx({ signedTransaction: emptyUintArrayAsHexString });
throw new Error('Expected to throw');
} catch (error) {
if (error instanceof ProviderError) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
Cardano,
CardanoNode,
StakePoolProvider,
QueryStakePoolsArgs,
StakePoolProvider,
StakePoolSearchResults,
StakePoolStats
} from '@cardano-sdk/core';
Expand Down Expand Up @@ -68,11 +68,7 @@ export class DbSyncStakePoolProvider extends DbSyncProvider implements StakePool
}
}

private async getPoolsDataOrdered(
poolUpdates: PoolUpdate[],
totalAdaAmount: string,
options?: QueryStakePoolsArgs
) {
private async getPoolsDataOrdered(poolUpdates: PoolUpdate[], totalAdaAmount: string, options?: QueryStakePoolsArgs) {
const hashesIds = poolUpdates.map(({ id }) => id);
const updatesIds = poolUpdates.map(({ updateId }) => updateId);

Expand Down
14 changes: 2 additions & 12 deletions packages/cardano-services/src/TxSubmit/openApi.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,13 @@
},
"components": {
"schemas": {
"TypifiedValue": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"__type": {
"type": "string"
}
}
},
"txsubmit_submit_body": {
"required": ["signedTransaction"],
"type": "object",
"properties": {
"signedTransaction": {
"$ref": "#/components/schemas/TypifiedValue"
"type": "string",
"description": "transaction hash represented as hex string"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { InMemoryCache, UNLIMITED_CACHE_TTL } from '../../../src/InMemoryCache';
import { Ogmios, OgmiosCardanoNode } from '@cardano-sdk/ogmios';
import { Pool } from 'pg';
import { SrvRecord } from 'dns';
import { bufferToHexString } from '@cardano-sdk/util';
import { createHealthyMockOgmiosServer, ogmiosServerReady } from '../../util';
import { createMockOgmiosServer } from '../../../../ogmios/test/mocks/mockOgmiosServer';
import { getPort, getRandomPort } from 'get-port-please';
Expand Down Expand Up @@ -323,9 +324,9 @@ describe('Service dependency abstractions', () => {
serviceDiscoveryTimeout: 1000
});

await expect(provider.submitTx({ signedTransaction: new Uint8Array([]) })).rejects.toBeInstanceOf(
Cardano.TxSubmissionErrors.EraMismatchError
);
await expect(
provider.submitTx({ signedTransaction: bufferToHexString(Buffer.from(new Uint8Array([]))) })
).rejects.toBeInstanceOf(Cardano.TxSubmissionErrors.EraMismatchError);
expect(dnsResolverMock).toBeCalledTimes(2);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Ogmios } from '@cardano-sdk/ogmios';
import { RabbitMQContainer } from '../../../../rabbitmq/test/docker';
import { RunningTxSubmitWorker } from '../../../src/TxWorker/utils';
import { SrvRecord } from 'dns';
import { bufferToHexString } from '@cardano-sdk/util';
import { createMockOgmiosServer } from '../../../../ogmios/test/mocks/mockOgmiosServer';
import { getPort, getRandomPort } from 'get-port-please';
import { listenPromise, serverClosePromise } from '../../../src/util';
Expand Down Expand Up @@ -191,9 +192,9 @@ describe('Service dependency abstractions', () => {
});

const txs = await txsPromise;
await expect(provider.submitTx({ signedTransaction: txs[0].txBodyUint8Array })).rejects.toBeInstanceOf(
Cardano.TxSubmissionErrors.EraMismatchError
);
await expect(
provider.submitTx({ signedTransaction: bufferToHexString(Buffer.from(txs[0].txBodyUint8Array)) })
).rejects.toBeInstanceOf(Cardano.TxSubmissionErrors.EraMismatchError);
expect(dnsResolverMock).toBeCalledTimes(2);
});

Expand Down
34 changes: 22 additions & 12 deletions packages/cardano-services/test/TxSubmit/TxSubmitHttpService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { APPLICATION_JSON, CONTENT_TYPE, HttpServer, HttpServerConfig, TxSubmitH
import { Cardano, ProviderError, ProviderFailure, TxSubmitProvider } from '@cardano-sdk/core';
import { CreateHttpProviderConfig, txSubmitHttpProvider } from '@cardano-sdk/cardano-services-client';
import { FATAL, createLogger } from 'bunyan';
import { fromSerializableObject, toSerializableObject } from '@cardano-sdk/util';
import { bufferToHexString, fromSerializableObject } from '@cardano-sdk/util';
import { getPort } from 'get-port-please';
import { dummyLogger as logger } from 'ts-log';
import axios from 'axios';
import cbor from 'cbor';

const serializeProviderArg = (arg: unknown) => JSON.stringify({ signedTransaction: toSerializableObject(arg) });
const bodyTx = serializeProviderArg(cbor.encode('#####'));
const serializeProviderArg = (arg: unknown) => ({ signedTransaction: arg });
const bodyTx = serializeProviderArg(cbor.encode('#####').toString('hex'));
const UNSUPPORTED_MEDIA_STRING = 'Request failed with status code 415';
const APPLICATION_CBOR = 'application/cbor';
const emptyUintArrayAsHexString = bufferToHexString(Buffer.from(new Uint8Array()));

describe('TxSubmitHttpService', () => {
let txSubmitProvider: TxSubmitProvider;
Expand Down Expand Up @@ -82,9 +83,13 @@ describe('TxSubmitHttpService', () => {
expect(await serverHealth()).toEqual({ ok: false });

try {
await axios.post(`${baseUrl}/submit`, serializeProviderArg(Buffer.from(new Uint8Array())), {
headers: { [CONTENT_TYPE]: APPLICATION_JSON }
});
await axios.post(
`${baseUrl}/submit`,
{ signedTransaction: emptyUintArrayAsHexString },
{
headers: { [CONTENT_TYPE]: APPLICATION_JSON }
}
);
throw new Error('fail');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
Expand Down Expand Up @@ -124,7 +129,7 @@ describe('TxSubmitHttpService', () => {
describe('/submit', () => {
it('calls underlying TxSubmitProvider with a valid argument', async () => {
(txSubmitProvider.submitTx as jest.Mock).mockImplementation(async ({ signedTransaction }) => {
expect(ArrayBuffer.isView(signedTransaction)).toBe(true);
expect(typeof signedTransaction === 'string').toBe(true);
});
expect(
(
Expand Down Expand Up @@ -187,7 +192,7 @@ describe('TxSubmitHttpService', () => {
expect.assertions(2);
const clientProvider = txSubmitHttpProvider(clientConfig);
try {
await clientProvider.submitTx({ signedTransaction: new Uint8Array() });
await clientProvider.submitTx({ signedTransaction: emptyUintArrayAsHexString });
} catch (error: any) {
if (error instanceof ProviderError) {
const innerError = error.innerError as Cardano.TxSubmissionError;
Expand All @@ -198,12 +203,17 @@ describe('TxSubmitHttpService', () => {
});

// eslint-disable-next-line max-len
it('returns a 400 coded response with detail in the body to a transaction containing a domain violation', async () => {
// returns a 400 coded response with detail in the body to a transaction containing a domain violation
it('asd', async () => {
expect.assertions(3);
try {
await axios.post(`${baseUrl}/submit`, serializeProviderArg(Buffer.from(new Uint8Array())), {
headers: { [CONTENT_TYPE]: APPLICATION_JSON }
});
await axios.post(
`${baseUrl}/submit`,
{ signedTransaction: emptyUintArrayAsHexString },
{
headers: { [CONTENT_TYPE]: APPLICATION_JSON }
}
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
expect(error.response.status).toBe(400);
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/Provider/TxSubmitProvider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// @ts-ignore
import Cardano, { Provider } from '../..';

type SerializedTransaction = Cardano.util.HexBlob;

export interface SubmitTxArgs {
signedTransaction: Uint8Array;
signedTransaction: SerializedTransaction;
}

export interface TxSubmitProvider extends Provider {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Buffer } from 'buffer';
import { Cardano, ProviderError, ProviderFailure, TxSubmitProvider } from '@cardano-sdk/core';
import {
ConnectionConfig,
Expand Down Expand Up @@ -57,8 +56,7 @@ export const ogmiosTxSubmitProvider = (connectionConfig: ConnectionConfig, logge
}
);
const txSubmissionClient = await createTxSubmissionClient(interactionContext);
const txHex = Buffer.from(signedTransaction).toString('hex');
await txSubmissionClient.submitTx(txHex);
await txSubmissionClient.submitTx(signedTransaction);
} catch (error) {
throw Cardano.util.asTxSubmissionError(error) || new Cardano.UnknownTxSubmissionError(error);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Cardano, HealthCheckResponse, ProviderError, TxSubmitProvider } from '@cardano-sdk/core';
import { Connection, createConnectionObject } from '@cardano-ogmios/client';
import { bufferToHexString } from '@cardano-sdk/util';
import { createMockOgmiosServer, listenPromise, serverClosePromise } from '../../mocks/mockOgmiosServer';
import { getRandomPort } from 'get-port-please';
import { dummyLogger as logger } from 'ts-log';
import { ogmiosTxSubmitProvider } from '../../../src';
import http from 'http';

const emptyUintArrayAsHexString = bufferToHexString(Buffer.from(new Uint8Array()));

describe('ogmiosTxSubmitProvider', () => {
let mockServer: http.Server;
let connection: Connection;
Expand Down Expand Up @@ -91,7 +94,7 @@ describe('ogmiosTxSubmitProvider', () => {

it('resolves if successful', async () => {
try {
const res = await provider.submitTx({ signedTransaction: new Uint8Array() });
const res = await provider.submitTx({ signedTransaction: emptyUintArrayAsHexString });
expect(res).toBeUndefined();
} catch (error) {
expect(error).toBeUndefined();
Expand All @@ -110,7 +113,7 @@ describe('ogmiosTxSubmitProvider', () => {
});
await listenPromise(mockServer, connection.port);
provider = ogmiosTxSubmitProvider(connection, logger);
await expect(provider.submitTx({ signedTransaction: new Uint8Array() })).rejects.toThrowError(
await expect(provider.submitTx({ signedTransaction: emptyUintArrayAsHexString })).rejects.toThrowError(
Cardano.TxSubmissionErrors.EraMismatchError
);
});
Expand Down
3 changes: 2 additions & 1 deletion packages/rabbitmq/src/TxSubmitWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Cardano, ProviderError, ProviderFailure, TxSubmitProvider } from '@card
import { Channel, Connection, Message, connect } from 'amqplib';
import { EventEmitter } from 'events';
import { Logger } from 'ts-log';
import { bufferToHexString } from '@cardano-sdk/util';

const moduleName = 'TxSubmitWorker';

Expand Down Expand Up @@ -284,7 +285,7 @@ export class TxSubmitWorker extends EventEmitter {

this.#dependencies.logger.info(`${moduleName}: submitting tx #${counter} id: ${txId}`);
this.#dependencies.logger.debug(`${moduleName}: tx #${counter} dump:`, [content.toString('hex')]);
await this.#dependencies.txSubmitProvider.submitTx({ signedTransaction: txBody });
await this.#dependencies.txSubmitProvider.submitTx({ signedTransaction: bufferToHexString(Buffer.from(txBody)) });

this.#dependencies.logger.debug(`${moduleName}: ACKing RabbitMQ message #${counter}`);
this.#channel?.ack(message);
Expand Down
7 changes: 3 additions & 4 deletions packages/rabbitmq/src/rabbitmqTxSubmitProvider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Buffer } from 'buffer';
import {
Cardano,
HealthCheckResponse,
Expand All @@ -10,7 +9,7 @@ import {
import { Channel, Connection, connect } from 'amqplib';
import { Logger } from 'ts-log';
import { TX_SUBMISSION_QUEUE, getErrorPrototype, waitForPending } from './utils';
import { fromSerializableObject } from '@cardano-sdk/util';
import { fromSerializableObject, hexStringToBuffer } from '@cardano-sdk/util';

const moduleName = 'RabbitMqTxSubmitProvider';

Expand Down Expand Up @@ -157,7 +156,7 @@ export class RabbitMqTxSubmitProvider implements TxSubmitProvider {
* Submit a transaction to RabbitMQ
*
* @param args data required to submit tx
* @param args.signedTransaction Uint8Array representation of a signedTransaction
* @param args.signedTransaction hex string representation of a signedTransaction
*/
async submitTx({ signedTransaction }: SubmitTxArgs) {
return new Promise<void>(async (resolve, reject) => {
Expand All @@ -177,7 +176,7 @@ export class RabbitMqTxSubmitProvider implements TxSubmitProvider {

// Actually send the message
await this.#ensureQueue();
this.#channel!.sendToQueue(TX_SUBMISSION_QUEUE, Buffer.from(signedTransaction));
this.#channel!.sendToQueue(TX_SUBMISSION_QUEUE, hexStringToBuffer(signedTransaction));
this.#dependencies.logger.debug(`${moduleName}: queued tx id: ${txId}`);

// Set the queue for response message
Expand Down
2 changes: 1 addition & 1 deletion packages/rabbitmq/test/TxSubmitWorker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('TxSubmitWorker', () => {
let res: unknown = null;

for (let i = 0; i < 7; ++i) {
promises.push(rabbitMqTxSubmitProvider.submitTx({ signedTransaction: txs[i].txBodyUint8Array }));
promises.push(rabbitMqTxSubmitProvider.submitTx({ signedTransaction: txs[i].txBodyHex }));
// Wait 10ms to be sure the transactions are enqueued in the right order
await new Promise((resolve) => setTimeout(resolve, 10));
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rabbitmq/test/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class RabbitMQContainer {
{ rabbitmqUrl },
{ logger: contextLogger(logger, 'enqueueTx') }
);
await rabbitMqTxSubmitProvider.submitTx({ signedTransaction: txs[idx].txBodyUint8Array });
await rabbitMqTxSubmitProvider.submitTx({ signedTransaction: txs[idx].txBodyHex });
} catch (error) {
err = error;
} finally {
Expand Down
8 changes: 6 additions & 2 deletions packages/rabbitmq/test/rabbitmqTxSubmitProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ describe('RabbitMqTxSubmitProvider', () => {
try {
const txs = await txsPromise;
provider = new RabbitMqTxSubmitProvider({ rabbitmqUrl: url }, { logger });
const resA = await provider.submitTx({ signedTransaction: txs[0].txBodyUint8Array });
const resA = await provider.submitTx({
signedTransaction: txs[0].txBodyHex
});
// Called again to cover the idempotent RabbitMqTxSubmitProvider.#ensureQueue() operation
const resB = await provider.submitTx({ signedTransaction: txs[1].txBodyUint8Array });
const resB = await provider.submitTx({
signedTransaction: txs[1].txBodyHex
});
expect(resA).toBeUndefined();
expect(resB).toBeUndefined();
} catch (error) {
Expand Down
6 changes: 4 additions & 2 deletions packages/wallet/src/SingleAddressWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ import { Cip30DataSignature } from '@cardano-sdk/cip30';
import { InputSelector, defaultSelectionConstraints, roundRobinRandomImprove } from '@cardano-sdk/cip2';
import { Logger } from 'ts-log';
import { RetryBackoffConfig } from 'backoff-rxjs';
import { Shutdown, contextLogger } from '@cardano-sdk/util';
import { Shutdown, bufferToHexString, contextLogger } from '@cardano-sdk/util';
import { TrackedUtxoProvider } from './services/ProviderTracker/TrackedUtxoProvider';
import { TxInternals, createTransactionInternals, ensureValidityInterval } from './Transaction';
import { WalletStores, createInMemoryWalletStores } from './persistence';
Expand Down Expand Up @@ -412,7 +412,9 @@ export class SingleAddressWallet implements ObservableWallet {
this.#logger.debug(`Submitting transaction ${tx.id}`);
this.#newTransactions.submitting$.next(tx);
try {
await this.txSubmitProvider.submitTx({ signedTransaction: coreToCsl.tx(tx).to_bytes() });
await this.txSubmitProvider.submitTx({
signedTransaction: bufferToHexString(Buffer.from(coreToCsl.tx(tx).to_bytes()))
});
this.#logger.debug(`Submitted transaction ${tx.id}`);
this.#newTransactions.pending$.next(tx);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BehaviorSubject } from 'rxjs';
import { CLEAN_TX_SUBMIT_STATS, ProviderFnStats, TrackedTxSubmitProvider, TxSubmitProviderStats } from '../../../src';
import { TxSubmitProvider } from '@cardano-sdk/core';
import { bufferToHexString } from '@cardano-sdk/util';
import { mockTxSubmitProvider } from '../../mocks';

describe('TrackedTxSubmitProvider', () => {
Expand Down Expand Up @@ -48,7 +49,7 @@ describe('TrackedTxSubmitProvider', () => {
test(
'submitTx',
testFunctionStats(
(provider) => provider.submitTx({ signedTransaction: new Uint8Array() }),
(provider) => provider.submitTx({ signedTransaction: bufferToHexString(Buffer.from(new Uint8Array())) }),
(stats) => stats.submitTx$
)
);
Expand Down

0 comments on commit 032a1b7

Please sign in to comment.