Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
♻️ Add generic asset type support
Browse files Browse the repository at this point in the history
Define BaseTransactionInput
  • Loading branch information
ManuGowda committed May 28, 2020
1 parent 31f6daf commit 2c0e0cf
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*
* Removal or modification of this copyright notice is prohibited.
*/
import { codec } from '@liskhq/lisk-codec';
import { codec, GenericObject } from '@liskhq/lisk-codec';
import { BaseTransaction } from '@liskhq/lisk-transactions';
import { hash } from '@liskhq/lisk-cryptography';

Expand Down Expand Up @@ -40,8 +40,8 @@ export class TransactionInterfaceAdapter {
}

// First encode message asset and then encode base message
public encode(message: BaseTransaction): Buffer {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
public encode(message: BaseTransaction<{}>): Buffer {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const TransactionClass = this._transactionClassMap.get(message.type);

if (!TransactionClass) {
Expand All @@ -50,25 +50,27 @@ export class TransactionInterfaceAdapter {

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
const binaryAsset = codec.encode(TransactionClass.ASSET_SCHEMA, message.asset as any);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const binaryAssetWithMessage = { ...message, asset: binaryAsset };

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const binaryMessage = codec.encode(BaseTransaction.BASE_SCHEMA, binaryAssetWithMessage as any);
const binaryMessage = codec.encode(BaseTransaction.BASE_SCHEMA, binaryAssetWithMessage as unknown as GenericObject);

return binaryMessage;
}

// First decode base message and then decode asset
public decode(binaryMessage: Buffer): BaseTransaction {
const baseMessage = codec.decode<BaseTransaction>(BaseTransaction.BASE_SCHEMA, binaryMessage);
public decode(binaryMessage: Buffer): BaseTransaction<{}> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const baseMessage = codec.decode<BaseTransaction<{}>>(BaseTransaction.BASE_SCHEMA, binaryMessage);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const TransactionClass = this._transactionClassMap.get(baseMessage.type);

if (!TransactionClass) {
throw new Error('Transaction type not found.');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const assetMessage = codec.decode<BaseTransaction>(TransactionClass.ASSET_SCHEMA, baseMessage.asset as Buffer);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
const assetMessage = codec.decode<BaseTransaction<{}>>(TransactionClass.ASSET_SCHEMA, baseMessage.asset as Buffer);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const message = { ...baseMessage, asset: assetMessage };

const id = hash(binaryMessage)
Expand Down
5 changes: 3 additions & 2 deletions elements/lisk-transactions/src/10_delegate_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { BaseTransaction, StateStore } from './base_transaction';
import { CHAIN_STATE_DELEGATE_USERNAMES, DELEGATE_NAME_FEE } from './constants';
import { TransactionError } from './errors';
import { BaseTransactionInput } from './types';

interface RegisteredDelegate {
readonly username: string;
Expand Down Expand Up @@ -43,13 +44,13 @@ export const delegateRegistrationAssetSchema = {
},
};

export class DelegateTransaction extends BaseTransaction {
export class DelegateTransaction extends BaseTransaction<DelegateAsset> {
public static TYPE = 10;
public static NAME_FEE = BigInt(DELEGATE_NAME_FEE);
public static ASSET_SCHEMA = delegateRegistrationAssetSchema;
public readonly asset: DelegateAsset;

public constructor(transaction: DelegateTransaction) {
public constructor(transaction: BaseTransactionInput<DelegateAsset>) {
super(transaction);

this.asset = transaction.asset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
validateKeysSignatures,
validateSignature,
} from './utils';
import { BaseTransactionInput } from './types';

export const multisigRegAssetSchema = {
$id: 'lisk/multisignature-registration-transaction',
Expand Down Expand Up @@ -80,13 +81,13 @@ export interface MultiSignatureAsset {
readonly numberOfSignatures: number;
}

export class MultisignatureTransaction extends BaseTransaction {
export class MultisignatureTransaction extends BaseTransaction<MultiSignatureAsset> {
public static TYPE = 12;
public static ASSET_SCHEMA = multisigRegAssetSchema;
public readonly asset: MultiSignatureAsset;
private readonly MAX_KEYS_COUNT = 64;

public constructor(transaction: MultisignatureTransaction) {
public constructor(transaction: BaseTransactionInput<MultiSignatureAsset>) {
super(transaction);

this.asset = transaction.asset;
Expand Down
5 changes: 3 additions & 2 deletions elements/lisk-transactions/src/13_vote_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BaseTransaction, StateStore } from './base_transaction';
import { MAX_INT64 } from './constants';
import { TransactionError } from './errors';
import { sortUnlocking } from './utils';
import { BaseTransactionInput } from './types';

export interface Vote {
readonly delegateAddress: Buffer;
Expand Down Expand Up @@ -64,12 +65,12 @@ const TEN_UNIT = BigInt(10) * BigInt(10) ** BigInt(8);
const MAX_VOTE = 10;
const MAX_UNLOCKING = 20;

export class VoteTransaction extends BaseTransaction {
export class VoteTransaction extends BaseTransaction<VoteAsset> {
public static TYPE = 13;
public static ASSET_SCHEMA = voteAssetSchema;
public readonly asset: VoteAsset;

public constructor(transaction: VoteTransaction) {
public constructor(transaction: BaseTransactionInput<VoteAsset>) {
super(transaction);

this.asset = transaction.asset;
Expand Down
6 changes: 3 additions & 3 deletions elements/lisk-transactions/src/14_unlock_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import { BaseTransaction, StateStore } from './base_transaction';
import { TransactionError } from './errors';
import { Account } from './types';
import { Account, BaseTransactionInput } from './types';
import { getPunishmentPeriod, sortUnlocking } from './utils';

export interface Unlock {
Expand Down Expand Up @@ -79,12 +79,12 @@ const getWaitingPeriod = (
return waitTime - (currentHeight - unlockObject.unvoteHeight);
};

export class UnlockTransaction extends BaseTransaction {
export class UnlockTransaction extends BaseTransaction<UnlockAsset> {
public static TYPE = 14;
public static ASSET_SCHEMA = unlockAssetSchema;
public readonly asset: UnlockAsset;

public constructor(transaction: UnlockTransaction) {
public constructor(transaction: BaseTransactionInput<UnlockAsset>) {
super(transaction);

this.asset = transaction.asset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
getPunishmentPeriod,
validateSignature,
} from './utils';
import { BlockHeader } from './types';
import { BlockHeader, BaseTransactionInput } from './types';

const proofOfMisbehaviorAssetSchema = {
$id: 'lisk/proof-of-misbehavior-transaction',
Expand Down Expand Up @@ -104,18 +104,18 @@ const blockHeaderSchema = {
},
};

export interface ProofOfMisbehaviorAsset {
export interface PoMAsset {
readonly header1: BlockHeader;
readonly header2: BlockHeader;
reward: bigint;
}

export class ProofOfMisbehaviorTransaction extends BaseTransaction {
export class ProofOfMisbehaviorTransaction extends BaseTransaction<PoMAsset> {
public static TYPE = 15;
public static ASSET_SCHEMA = proofOfMisbehaviorAssetSchema;
public readonly asset: ProofOfMisbehaviorAsset;
public readonly asset: PoMAsset;

public constructor(transaction: ProofOfMisbehaviorTransaction) {
public constructor(transaction: BaseTransactionInput<PoMAsset>) {
super(transaction);

this.asset = transaction.asset;
Expand Down
5 changes: 3 additions & 2 deletions elements/lisk-transactions/src/8_transfer_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BaseTransaction, StateStore } from './base_transaction';
import { MAX_TRANSACTION_AMOUNT } from './constants';
import { TransactionError } from './errors';
import { verifyMinRemainingBalance } from './utils';
import { BaseTransactionInput } from './types';

export interface TransferAsset {
readonly amount: bigint;
Expand Down Expand Up @@ -48,12 +49,12 @@ export const transferAssetSchema = {
},
};

export class TransferTransaction extends BaseTransaction {
export class TransferTransaction extends BaseTransaction<TransferAsset> {
public static TYPE = 8;
public static ASSET_SCHEMA = transferAssetSchema;
public readonly asset: TransferAsset;

public constructor(transaction: TransferTransaction) {
public constructor(transaction: BaseTransactionInput<TransferAsset>) {
super(transaction);

this.asset = transaction.asset;
Expand Down
30 changes: 15 additions & 15 deletions elements/lisk-transactions/src/base_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { MAX_TRANSACTION_AMOUNT, MIN_FEE_PER_BYTE } from './constants';
import { convertToTransactionError, TransactionError } from './errors';
import { createResponse, TransactionResponse } from './response';
import { baseTransactionSchema } from './schema';
import { Account, BlockHeader } from './types';
import { Account, BlockHeader, BaseTransactionInput } from './types';
import {
buildPublicKeyPassphraseDict,
isMultisignatureAccount,
Expand Down Expand Up @@ -63,7 +63,7 @@ export interface StateStore {
export const ENTITY_ACCOUNT = 'account';
export const ENTITY_TRANSACTION = 'transaction';

export abstract class BaseTransaction {
export abstract class BaseTransaction<T> {
public static TYPE: number;
// Minimum remaining balance requirement for any account to perform a transaction
public static MIN_REMAINING_BALANCE = BigInt('5000000'); // 0.05 LSK
Expand All @@ -74,7 +74,7 @@ export abstract class BaseTransaction {

public readonly id: Buffer;
public readonly type: number;
public asset: object;
public asset: T;
public nonce: bigint;
public fee: bigint;
public senderPublicKey: Buffer;
Expand All @@ -86,7 +86,7 @@ export abstract class BaseTransaction {
private readonly _senderPublicKey: string;
private readonly _senderId: Buffer;

public constructor(transaction: BaseTransaction) {
public constructor(transaction: BaseTransactionInput<T>) {
this.id = transaction.id;
this.type = transaction.type;
this.asset = transaction.asset;
Expand All @@ -106,7 +106,7 @@ export abstract class BaseTransaction {
this._minFee =
(this.constructor as typeof BaseTransaction).NAME_FEE +
BigInt((this.constructor as typeof BaseTransaction).MIN_FEE_PER_BYTE) *
BigInt(this.getBytes().length);
BigInt(this.getBytes().length);
}

return this._minFee;
Expand Down Expand Up @@ -247,14 +247,14 @@ export abstract class BaseTransaction {
updatedBalance <= BigInt(MAX_TRANSACTION_AMOUNT)
? []
: [
new TransactionError(
'Invalid balance amount',
this.id,
'.balance',
sender.balance.toString(),
updatedBalance.toString(),
),
];
new TransactionError(
'Invalid balance amount',
this.id,
'.balance',
sender.balance.toString(),
updatedBalance.toString(),
),
];

// Decrement account nonce
sender.nonce -= BigInt(1);
Expand Down Expand Up @@ -384,7 +384,7 @@ export abstract class BaseTransaction {
protected getAssetBytes(): Buffer {
const assetSchema = (this.constructor as typeof BaseTransaction)
.ASSET_SCHEMA;
return codec.encode(assetSchema as Schema, this.asset as GenericObject);
return codec.encode(assetSchema as Schema, this.asset as unknown as GenericObject);
}

private _validateSchema(): ReadonlyArray<TransactionError> {
Expand All @@ -396,7 +396,7 @@ export abstract class BaseTransaction {

const assetSchemaErrors = validator.validate(
BaseTransaction.ASSET_SCHEMA,
this.asset,
this.asset as unknown as GenericObject,
);
const assetErrors = convertToTransactionError(
this.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import * as cryptography from '@liskhq/lisk-cryptography';

import { DelegateTransaction } from './10_delegate_transaction';
import { MultisignatureTransaction } from './12_multisignature_transaction';
import { MultisignatureTransaction, MultiSignatureAsset } from './12_multisignature_transaction';
import { VoteTransaction } from './13_vote_transaction';
import { UnlockTransaction } from './14_unlock_transaction';
import { TransferTransaction } from './8_transfer_transaction';
Expand All @@ -33,7 +33,7 @@ const transactionMap: { readonly [key: number]: any } = {
};

const sanitizeSignaturesArray = (
tx: BaseTransaction,
tx: BaseTransaction<MultiSignatureAsset>,
keys: MultisigKeys,
): void => {
let numberOfSignatures = keys.mandatoryKeys.length + keys.optionalKeys.length;
Expand All @@ -60,7 +60,7 @@ export const signMultiSignatureTransaction = (options: {
readonly passphrase: string;
readonly networkIdentifier: string;
readonly keys: MultisigKeys;
}): BaseTransaction => {
}): BaseTransaction<MultiSignatureAsset> => {
const { transaction, passphrase, networkIdentifier } = options;
if (transaction.type === undefined || transaction.type === null) {
throw new Error('Transaction type is required.');
Expand All @@ -81,7 +81,7 @@ export const signMultiSignatureTransaction = (options: {
const tx = new TransactionClass({
...transaction,
networkIdentifier,
}) as BaseTransaction;
}) as BaseTransaction<MultiSignatureAsset>;

const { publicKey } = cryptography.getPrivateAndPublicKeyFromPassphrase(
passphrase,
Expand Down
10 changes: 10 additions & 0 deletions elements/lisk-transactions/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ export interface TransactionJSON {
readonly fee: string;
}

export interface BaseTransactionInput<T> {
readonly id: Buffer;
readonly type: number;
readonly senderPublicKey: Buffer;
readonly nonce: bigint;
readonly fee: bigint;
readonly asset: T;
readonly signatures: Array<Readonly<Buffer>>;
}

export interface IsValidResponse {
readonly valid: boolean;
readonly errors?: ReadonlyArray<TransactionError>;
Expand Down

0 comments on commit 2c0e0cf

Please sign in to comment.