Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[@dhealthdapps/backend] refactor(common): remove transaction's serialized headers and reconstruct when decode #100

Open
wants to merge 1 commit into
base: son/techn-369-fix-todo-tasks-common-scope
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions runtime/backend/src/common/models/TransactionSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
*/
// external dependencies
import {
Convert,
NetworkType,
Transaction as SdkTransaction,
TransactionMapping,
TransactionType,
} from "@dhealth/sdk";
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Model } from "mongoose";
Expand Down Expand Up @@ -324,8 +327,37 @@ export class Transaction extends Transferable<TransactionDTO> {
* @returns {SdkTransaction} The `SdkTransaction` object with payload set.
*/
public toSDK(): SdkTransaction {
//XXX this assumes "encodedBody" contains the *full* payload.
return TransactionMapping.createFromPayload(this.encodedBody, false); // false for `isEmbedded`
const sizeBytes = Convert.numberToUint8Array(
(this.encodedBody.length + 256) / 2,
4,
);
const verifiableEntityHeader_Reserved4Bytes = Convert.numberToUint8Array(
0,
4,
);
const signatureBytes = Convert.hexToUint8(this.signature);
const signerPublicKeyBytes = Convert.hexToUint8(this.signerPublicKey);
const entityBody_Reserved4Bytes = Convert.numberToUint8Array(0, 4);
const versionBytes = Convert.numberToUint8Array(0, 1);
const networkBytes = Convert.numberToUint8Array(NetworkType.MAIN_NET, 1);
const typeBytes = Convert.numberToUint8Array(TransactionType.TRANSFER, 2);
const feeBytes = Convert.numberToUint8Array(0, 8);
const deadlineBytes = Convert.numberToUint8Array(0, 8);
const headersUint8Array = [
...sizeBytes,
...verifiableEntityHeader_Reserved4Bytes,
...signatureBytes,
...signerPublicKeyBytes,
...entityBody_Reserved4Bytes,
...versionBytes,
...networkBytes,
...typeBytes,
...feeBytes,
...deadlineBytes,
];
const fullEncodedBody =
Convert.uint8ToHex(headersUint8Array) + this.encodedBody;
return TransactionMapping.createFromPayload(fullEncodedBody, false); // false for `isEmbedded`
}

/**
Expand Down
30 changes: 20 additions & 10 deletions runtime/backend/src/discovery/models/TransactionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,28 @@
import { TransactionType } from "@dhealth/sdk";

/**
* @class TransactionDTO
* @description
* @class TransactionTypes
* @description A helper class that handle and process {@link TransactionType}.
*
* @todo Add relevant helper function documentation
* @todo Use the `@dhealth/contracts` abstraction and include in TransactionDTO if necessary.
* @since v0.2.0
*/
export const getTransactionType = (type: TransactionType): string => {
switch (type) {
case TransactionType.TRANSFER:
return "transfer";
default:
return "custom";
export class TransactionTypes {
/**
* A helper method that returns a string representation
* of a {@link TransactionType}.
*
* @access public
* @static
* @param {TransactionType} type The transaction type to get a string representation of.
* @returns {string}
*/
public static getTransactionType(type: TransactionType): string {
switch (type) {
case TransactionType.TRANSFER:
return "transfer";
default:
return "custom";
}
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ConfigService } from "@nestjs/config";
import { InjectModel } from "@nestjs/mongoose";
import { Cron } from "@nestjs/schedule";
import {
Address,
AggregateTransactionInfo,
Order,
Page,
Expand All @@ -33,7 +32,7 @@ import { NetworkService } from "../../../common/services/NetworkService";
import { DiscoveryCommand, DiscoveryCommandOptions } from "../DiscoveryCommand";
import { AssetsService } from "../../../discovery/services/AssetsService";
import { TransactionsService } from "../../../discovery/services/TransactionsService";
import { getTransactionType } from "../../../discovery/models/TransactionTypes";
import { TransactionTypes } from "../../../discovery/models/TransactionTypes";
import {
Transaction,
TransactionDocument,
Expand Down Expand Up @@ -716,7 +715,7 @@ export class DiscoverTransactions extends DiscoveryCommand {
* @returns {string}
*/
protected extractTransactionType(transaction: SdkTransaction): string {
return getTransactionType(transaction.type);
return TransactionTypes.getTransactionType(transaction.type);
}

/**
Expand Down Expand Up @@ -745,19 +744,16 @@ export class DiscoverTransactions extends DiscoveryCommand {
// serializes transaction to get hexadecimal payload
const payload: string = transaction.serialize();

// @todo Move to the following implementation as this will
// @todo permit to save more than 100bytes of space for each
// @todo contract operation.
// @todo Note that this implementation would require also
// @todo a *transaction parser* that re-build transactions
// This will permit to save 128 bytes of space for each
// contract operation.
// Note that this implementation requires also
// a *transaction parser* that re-build transactions
// following transaction header applies here:
// size | r1 | sig | pub | r2 | ver | net | type
// 4b | 4b | 64b | 32b | 4b | 1b | 1b | 2b
// --> we are dropping 112 bytes as the header
// size | r1 | sig | pub | r2 | ver | net | type | fee | dl
// 4b | 4b | 64b | 32b | 4b | 1b | 1b | 2b | 8b | 8b
// --> we are dropping 128 bytes (256 hex characters) as the header
// and we return only the remaining body after
//return payload.substring(112);

return payload;
return payload.substring(256);
}

/**
Expand Down
13 changes: 11 additions & 2 deletions runtime/backend/tests/unit/common/models/TransactionSchema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
// external dependencies
import { TransactionDTO } from "@/discovery/models/TransactionDTO";
import { TransactionMapping } from "@dhealth/sdk";
import { Convert, TransactionMapping, Transaction as SdkTransaction } from "@dhealth/sdk";

// internal dependencies
import { Transaction, TransactionDocument } from "../../../../src/common/models/TransactionSchema";
Expand Down Expand Up @@ -51,13 +51,22 @@ describe("common/TransactionSchema", () => {
// prepare
let createFromPayloadCall = jest
.spyOn(TransactionMapping, "createFromPayload")
.mockReturnValue({} as any);
.mockReturnValue({} as SdkTransaction);
const transaction = new Transaction();
(transaction as any).encodedBody = "ABC";
const convertNumberToUint8ArrayCall = jest
.spyOn(Convert, "numberToUint8Array")
.mockReturnValue(new Uint8Array());
const convertHexToUint8Call = jest
.spyOn(Convert, "hexToUint8")
.mockReturnValue(new Uint8Array());

// act
const result = transaction.toSDK();

// assert
expect(convertNumberToUint8ArrayCall).toHaveBeenCalledTimes(8);
expect(convertHexToUint8Call).toHaveBeenCalledTimes(2);
expect(createFromPayloadCall).toHaveBeenCalledTimes(1);
expect(result).toStrictEqual({});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
import { TransactionType } from "@dhealth/sdk";

// internal dependencies
import { getTransactionType } from "../../../../src/discovery/models/TransactionTypes";
import { TransactionTypes } from "../../../../src/discovery/models/TransactionTypes";

describe("discovery/TransactionType", () => {
describe("discovery/TransactionTypes", () => {
describe("getTransactionType()", () => {
it("should return transaction type: transfer", () => {
// act
const result = getTransactionType(TransactionType.TRANSFER);
const result = TransactionTypes.getTransactionType(TransactionType.TRANSFER);

// assert
expect(result).toBe("transfer");
});

it("should return transaction type: custom", () => {
// act
const result = getTransactionType(TransactionType.RESERVED);
const result = TransactionTypes.getTransactionType(TransactionType.RESERVED);

// assert
expect(result).toBe("custom");
Expand Down