Skip to content

Commit

Permalink
PersistentDelegationRequestTransaction message bug fix (#709)
Browse files Browse the repository at this point in the history
* Fixed message parsing from dto and buffer
  • Loading branch information
fboucquez committed Nov 9, 2020
1 parent 13fa9f7 commit e87b852
Show file tree
Hide file tree
Showing 14 changed files with 259 additions and 184 deletions.
106 changes: 38 additions & 68 deletions src/infrastructure/transaction/CreateTransactionFromDTO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,43 @@
* limitations under the License.
*/
import { Convert as convert } from '../../core/format';
import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping';
import { Address } from '../../model/account/Address';
import { PublicAccount } from '../../model/account/PublicAccount';
import { EncryptedMessage } from '../../model/message/EncryptedMessage';
import { Message } from '../../model/message/Message';
import { MessageType } from '../../model/message/MessageType';
import { PersistentHarvestingDelegationMessage } from '../../model/message/PersistentHarvestingDelegationMessage';
import { EmptyMessage, PlainMessage } from '../../model/message/PlainMessage';
import { Mosaic } from '../../model/mosaic/Mosaic';
import { MosaicFlags } from '../../model/mosaic/MosaicFlags';
import { MosaicId } from '../../model/mosaic/MosaicId';
import { MosaicNonce } from '../../model/mosaic/MosaicNonce';
import { NamespaceId } from '../../model/namespace/NamespaceId';
import { AccountAddressRestrictionTransaction } from '../../model/transaction/AccountAddressRestrictionTransaction';
import { AccountKeyLinkTransaction } from '../../model/transaction/AccountKeyLinkTransaction';
import { AccountMetadataTransaction } from '../../model/transaction/AccountMetadataTransaction';
import { AccountMosaicRestrictionTransaction } from '../../model/transaction/AccountMosaicRestrictionTransaction';
import { AccountOperationRestrictionTransaction } from '../../model/transaction/AccountOperationRestrictionTransaction';
import { AddressAliasTransaction } from '../../model/transaction/AddressAliasTransaction';
import { AggregateTransaction } from '../../model/transaction/AggregateTransaction';
import { AggregateTransactionCosignature } from '../../model/transaction/AggregateTransactionCosignature';
import { AggregateTransactionInfo } from '../../model/transaction/AggregateTransactionInfo';
import { Deadline } from '../../model/transaction/Deadline';
import { LockFundsTransaction } from '../../model/transaction/LockFundsTransaction';
import { MosaicAddressRestrictionTransaction } from '../../model/transaction/MosaicAddressRestrictionTransaction';
import { MosaicAliasTransaction } from '../../model/transaction/MosaicAliasTransaction';
import { MosaicDefinitionTransaction } from '../../model/transaction/MosaicDefinitionTransaction';
import { MosaicGlobalRestrictionTransaction } from '../../model/transaction/MosaicGlobalRestrictionTransaction';
import { MosaicMetadataTransaction } from '../../model/transaction/MosaicMetadataTransaction';
import { MosaicSupplyChangeTransaction } from '../../model/transaction/MosaicSupplyChangeTransaction';
import { MultisigAccountModificationTransaction } from '../../model/transaction/MultisigAccountModificationTransaction';
import { NamespaceMetadataTransaction } from '../../model/transaction/NamespaceMetadataTransaction';
import { NamespaceRegistrationTransaction } from '../../model/transaction/NamespaceRegistrationTransaction';
import { NodeKeyLinkTransaction } from '../../model/transaction/NodeKeyLinkTransaction';
import { SecretLockTransaction } from '../../model/transaction/SecretLockTransaction';
import { SecretProofTransaction } from '../../model/transaction/SecretProofTransaction';
import { SignedTransaction } from '../../model/transaction/SignedTransaction';
import { Transaction } from '../../model/transaction/Transaction';
import { TransactionInfo } from '../../model/transaction/TransactionInfo';
import { TransactionType } from '../../model/transaction/TransactionType';
import { TransferTransaction } from '../../model/transaction/TransferTransaction';
import { VotingKeyLinkTransaction } from '../../model/transaction/VotingKeyLinkTransaction';
import { VrfKeyLinkTransaction } from '../../model/transaction/VrfKeyLinkTransaction';
import { UInt64 } from '../../model/UInt64';
import { UnresolvedMapping } from '../../core/utils';
import { MessageFactory, UInt64 } from '../../model';
import { Address, PublicAccount } from '../../model/account';
import { Mosaic, MosaicFlags, MosaicId, MosaicNonce } from '../../model/mosaic';
import { NamespaceId } from '../../model/namespace';
import {
AccountAddressRestrictionTransaction,
AccountKeyLinkTransaction,
AccountMetadataTransaction,
AccountMosaicRestrictionTransaction,
AccountOperationRestrictionTransaction,
AddressAliasTransaction,
AggregateTransaction,
AggregateTransactionCosignature,
AggregateTransactionInfo,
Deadline,
LockFundsTransaction,
MosaicAddressRestrictionTransaction,
MosaicAliasTransaction,
MosaicDefinitionTransaction,
MosaicGlobalRestrictionTransaction,
MosaicMetadataTransaction,
MosaicSupplyChangeTransaction,
MultisigAccountModificationTransaction,
NamespaceMetadataTransaction,
NamespaceRegistrationTransaction,
NodeKeyLinkTransaction,
SecretLockTransaction,
SecretProofTransaction,
SignedTransaction,
Transaction,
TransactionInfo,
TransactionType,
TransferTransaction,
VotingKeyLinkTransaction,
VrfKeyLinkTransaction,
} from '../../model/transaction';

/**
* Extract recipientAddress value from encoded hexadecimal notation.
Expand Down Expand Up @@ -101,29 +94,6 @@ export const extractMosaics = (mosaics: any): Mosaic[] => {
});
};

/**
* Extract message from either JSON payload (unencoded) or DTO (encoded)
*
* @param message - message payload
* @return {Message}
*/
const extractMessage = (message: any): Message => {
let msgObj = EmptyMessage;
if (message) {
const messagePayload = message.payload ? message.payload : message.substring(2);
const messageType = message.type !== undefined ? message.type : convert.hexToUint8(message.substring(0, 2))[0];

if (messageType === MessageType.PlainMessage) {
msgObj = PlainMessage.createFromPayload(messagePayload);
} else if (messageType === MessageType.EncryptedMessage) {
msgObj = EncryptedMessage.createFromPayload(messagePayload);
} else if (messageType === MessageType.PersistentHarvestingDelegationMessage) {
msgObj = PersistentHarvestingDelegationMessage.createFromPayload(messagePayload);
}
}
return msgObj;
};

/**
* Extract deadline from json payload.
* @param deadline - deadline dto
Expand Down Expand Up @@ -168,7 +138,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr
UInt64.fromNumericString(transactionDTO.maxFee || '0'),
extractRecipient(transactionDTO.recipientAddress),
extractMosaics(transactionDTO.mosaics),
extractMessage(transactionDTO.message !== undefined ? transactionDTO.message : undefined),
MessageFactory.createMessageFromHex(transactionDTO.message),
transactionDTO.signature,
transactionDTO.signerPublicKey
? PublicAccount.createFromPublicKey(transactionDTO.signerPublicKey, transactionDTO.network)
Expand Down
16 changes: 10 additions & 6 deletions src/model/message/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Convert } from '../../core/format/Convert';
* limitations under the License.
*/

import { Convert } from '../../core/format/Convert';
import { Convert } from '../../core/format';
import { MessageType } from './MessageType';

/**
Expand All @@ -40,9 +40,9 @@ export abstract class Message {
/**
* Message type
*/
public readonly type: number,
public readonly type: MessageType,
/**
* Message payload
* Message payload, it could be the message hex, encryped text or plain text depending on the message type.
*/
public readonly payload: string,
) {}
Expand All @@ -54,8 +54,12 @@ export abstract class Message {
if (!this.payload) {
return '';
}
return this.type === MessageType.PersistentHarvestingDelegationMessage
? this.payload
: this.type.toString(16).padStart(2, '0').toUpperCase() + Convert.utf8ToHex(this.payload);
if (this.type === MessageType.PersistentHarvestingDelegationMessage) {
return this.payload;
}
if (this.type === MessageType.RawMessage) {
return this.payload;
}
return this.type.toString(16).padStart(2, '0').toUpperCase() + Convert.utf8ToHex(this.payload);
}
}
65 changes: 65 additions & 0 deletions src/model/message/MessageFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2020 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Convert } from '../../core/format';
import { EncryptedMessage } from './EncryptedMessage';
import { Message } from './Message';
import { MessageMarker } from './MessageMarker';
import { MessageType } from './MessageType';
import { PersistentHarvestingDelegationMessage } from './PersistentHarvestingDelegationMessage';
import { PlainMessage } from './PlainMessage';
import { RawMessage } from './RawMessage';

/**
* Objects that knows how to create messages from serialized payloads.
*
* Note: this could be in the Message class but the circular dependency breaks Typescript.
*/
export class MessageFactory {
/**
* It creates a message from the byte array payload
* @param payload the payload as byte array
*/
public static createMessageFromBuffer(payload?: Uint8Array): Message {
return this.createMessageFromHex(payload ? Convert.uint8ToHex(payload) : undefined);
}
/**
* It creates a message from the hex payload
* @param payload the payload as byte array
*/
public static createMessageFromHex(payload?: string): Message {
if (!payload || !payload.length) {
return new RawMessage('');
}
if (payload.length == 264 && payload.startsWith(MessageMarker.PersistentDelegationUnlock)) {
return new PersistentHarvestingDelegationMessage(payload);
}
const messageType = Convert.hexToUint8(payload)[0];
switch (messageType) {
case MessageType.PlainMessage:
return new PlainMessage(Message.decodeHex(payload.substring(2)));
case MessageType.EncryptedMessage:
return new EncryptedMessage(Message.decodeHex(payload.substring(2)));
}
return new RawMessage(payload);
}
}

/**
* Raw message containing an empty string without any type or prefix.
* @type {PlainMessage}
*/
export const EmptyMessage: Message = MessageFactory.createMessageFromBuffer();
2 changes: 2 additions & 0 deletions src/model/message/MessageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

/**
* The Message type. Supported supply types are:
* -1: RawMessage (no type appended)
* 0: PlainMessage
* 1: EncryptedMessage.
* 254: Persistent harvesting delegation.
*/
export enum MessageType {
RawMessage = -1,
PlainMessage = 0x00,
EncryptedMessage = 0x01,
PersistentHarvestingDelegationMessage = 0xfe,
Expand Down
6 changes: 3 additions & 3 deletions src/model/message/PersistentHarvestingDelegationMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/

import { Crypto } from '../../core/crypto';
import { Convert } from '../../core/format/Convert';
import { Account } from '../account/Account';
import { NetworkType } from '../network/NetworkType';
import { Convert } from '../../core/format';
import { Account } from '../account';
import { NetworkType } from '../network';
import { Message } from './Message';
import { MessageMarker } from './MessageMarker';
import { MessageType } from './MessageType';
Expand Down
6 changes: 0 additions & 6 deletions src/model/message/PlainMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,3 @@ export class PlainMessage extends Message {
super(MessageType.PlainMessage, payload);
}
}

/**
* Plain message containing an empty string
* @type {PlainMessage}
*/
export const EmptyMessage: PlainMessage = PlainMessage.create('');
39 changes: 39 additions & 0 deletions src/model/message/RawMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2018 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Convert } from '../../core/format';
import { Message } from './Message';
import { MessageType } from './MessageType';

/**
* The a raw message that doesn't assume any prefix.
*/
export class RawMessage extends Message {
/**
* Create plain message object.
* @returns PlainMessage
*/
public static create(payload: Uint8Array): RawMessage {
return new RawMessage(Convert.uint8ToHex(payload));
}
/**
* @internal
* @param payload
*/
constructor(payload: string) {
super(MessageType.RawMessage, payload);
}
}
2 changes: 2 additions & 0 deletions src/model/message/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

export * from './EncryptedMessage';
export * from './Message';
export * from './MessageFactory';
export * from './MessageMarker';
export * from './MessageType';
export * from './PersistentHarvestingDelegationMessage';
export * from './PlainMessage';
export * from './RawMessage';
44 changes: 8 additions & 36 deletions src/model/transaction/TransferTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,13 @@ import {
} from 'catbuffer-typescript';
import * as Long from 'long';
import { Convert } from '../../core/format';
import { DtoMapping } from '../../core/utils/DtoMapping';
import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping';
import { Address } from '../account/Address';
import { PublicAccount } from '../account/PublicAccount';
import { UnresolvedAddress } from '../account/UnresolvedAddress';
import { EncryptedMessage } from '../message/EncryptedMessage';
import { Message } from '../message/Message';
import { MessageType } from '../message/MessageType';
import { PersistentHarvestingDelegationMessage } from '../message/PersistentHarvestingDelegationMessage';
import { EmptyMessage, PlainMessage } from '../message/PlainMessage';
import { Mosaic } from '../mosaic/Mosaic';
import { NamespaceId } from '../namespace/NamespaceId';
import { NetworkType } from '../network/NetworkType';
import { Statement } from '../receipt/Statement';
import { DtoMapping, UnresolvedMapping } from '../../core/utils';
import { Address, PublicAccount, UnresolvedAddress } from '../account';
import { EmptyMessage, Message, MessageFactory, MessageType } from '../message';
import { Mosaic } from '../mosaic';
import { NamespaceId } from '../namespace';
import { NetworkType } from '../network';
import { Statement } from '../receipt';
import { UInt64 } from '../UInt64';
import { Deadline } from './Deadline';
import { InnerTransaction } from './InnerTransaction';
Expand Down Expand Up @@ -151,7 +144,7 @@ export class TransferTransaction extends Transaction {
const id = new UInt64(mosaic.mosaicId.unresolvedMosaicId).toHex();
return new Mosaic(UnresolvedMapping.toUnresolvedMosaic(id), new UInt64(mosaic.amount.amount));
}),
TransferTransaction.createMessageFromBuffer(builder.getMessage()),
MessageFactory.createMessageFromBuffer(builder.getMessage()),
networkType,
isEmbedded ? new UInt64([0, 0]) : new UInt64((builder as TransferTransactionBuilder).fee.amount),
isEmbedded || signature.match(`^[0]+$`) ? undefined : signature,
Expand Down Expand Up @@ -298,25 +291,4 @@ export class TransferTransaction extends Transaction {
alias.find((name) => this.recipientAddress.equals(name)) !== undefined
);
}

/**
* @internal
*/
private static createMessageFromBuffer(messageBuffer: Uint8Array): Message {
if (!messageBuffer.length) {
return EmptyMessage;
}
const messageType = messageBuffer[0];
const messageHex = Convert.uint8ToHex(messageBuffer).substring(2);
switch (messageType) {
case MessageType.PlainMessage:
return PlainMessage.createFromPayload(messageHex);
case MessageType.EncryptedMessage:
return EncryptedMessage.createFromPayload(messageHex);
case MessageType.PersistentHarvestingDelegationMessage:
return PersistentHarvestingDelegationMessage.createFromPayload(messageHex);
default:
throw new Error('Message Type is not valid');
}
}
}

0 comments on commit e87b852

Please sign in to comment.