All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nem.sdk.infrastructure.BinarySerializationImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019 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.
 */

package io.nem.sdk.infrastructure;

import io.nem.catapult.builders.AccountAddressRestrictionTransactionBodyBuilder;
import io.nem.catapult.builders.AccountLinkActionDto;
import io.nem.catapult.builders.AccountLinkTransactionBodyBuilder;
import io.nem.catapult.builders.AccountMetadataTransactionBodyBuilder;
import io.nem.catapult.builders.AccountMosaicRestrictionTransactionBodyBuilder;
import io.nem.catapult.builders.AccountOperationRestrictionTransactionBodyBuilder;
import io.nem.catapult.builders.AccountRestrictionFlagsDto;
import io.nem.catapult.builders.AddressAliasTransactionBodyBuilder;
import io.nem.catapult.builders.AddressDto;
import io.nem.catapult.builders.AggregateTransactionBodyBuilder;
import io.nem.catapult.builders.AliasActionDto;
import io.nem.catapult.builders.AmountDto;
import io.nem.catapult.builders.BlockDurationDto;
import io.nem.catapult.builders.CosignatureBuilder;
import io.nem.catapult.builders.EmbeddedTransactionBuilder;
import io.nem.catapult.builders.EntityTypeDto;
import io.nem.catapult.builders.Hash256Dto;
import io.nem.catapult.builders.HashLockTransactionBodyBuilder;
import io.nem.catapult.builders.KeyDto;
import io.nem.catapult.builders.LockHashAlgorithmDto;
import io.nem.catapult.builders.MosaicAddressRestrictionTransactionBodyBuilder;
import io.nem.catapult.builders.MosaicAliasTransactionBodyBuilder;
import io.nem.catapult.builders.MosaicDefinitionTransactionBodyBuilder;
import io.nem.catapult.builders.MosaicFlagsDto;
import io.nem.catapult.builders.MosaicGlobalRestrictionTransactionBodyBuilder;
import io.nem.catapult.builders.MosaicIdDto;
import io.nem.catapult.builders.MosaicMetadataTransactionBodyBuilder;
import io.nem.catapult.builders.MosaicNonceDto;
import io.nem.catapult.builders.MosaicRestrictionTypeDto;
import io.nem.catapult.builders.MosaicSupplyChangeActionDto;
import io.nem.catapult.builders.MosaicSupplyChangeTransactionBodyBuilder;
import io.nem.catapult.builders.MultisigAccountModificationTransactionBodyBuilder;
import io.nem.catapult.builders.NamespaceIdDto;
import io.nem.catapult.builders.NamespaceMetadataTransactionBodyBuilder;
import io.nem.catapult.builders.NamespaceRegistrationTransactionBodyBuilder;
import io.nem.catapult.builders.NetworkTypeDto;
import io.nem.catapult.builders.SecretLockTransactionBodyBuilder;
import io.nem.catapult.builders.SecretProofTransactionBodyBuilder;
import io.nem.catapult.builders.Serializer;
import io.nem.catapult.builders.SignatureDto;
import io.nem.catapult.builders.TimestampDto;
import io.nem.catapult.builders.TransactionBuilder;
import io.nem.catapult.builders.TransactionBuilderFactory;
import io.nem.catapult.builders.TransferTransactionBodyBuilder;
import io.nem.catapult.builders.UnresolvedAddressDto;
import io.nem.catapult.builders.UnresolvedMosaicBuilder;
import io.nem.catapult.builders.UnresolvedMosaicIdDto;
import io.nem.core.utils.ConvertUtils;
import io.nem.core.utils.ExceptionUtils;
import io.nem.core.utils.StringEncoder;
import io.nem.sdk.api.BinarySerialization;
import io.nem.sdk.model.account.Address;
import io.nem.sdk.model.account.PublicAccount;
import io.nem.sdk.model.account.UnresolvedAddress;
import io.nem.sdk.model.blockchain.BlockDuration;
import io.nem.sdk.model.blockchain.NetworkType;
import io.nem.sdk.model.message.Message;
import io.nem.sdk.model.message.MessageType;
import io.nem.sdk.model.mosaic.Mosaic;
import io.nem.sdk.model.mosaic.MosaicFlags;
import io.nem.sdk.model.mosaic.MosaicId;
import io.nem.sdk.model.mosaic.MosaicNonce;
import io.nem.sdk.model.mosaic.MosaicSupplyChangeActionType;
import io.nem.sdk.model.mosaic.UnresolvedMosaicId;
import io.nem.sdk.model.namespace.AliasAction;
import io.nem.sdk.model.namespace.NamespaceId;
import io.nem.sdk.model.namespace.NamespaceRegistrationType;
import io.nem.sdk.model.transaction.AccountAddressRestrictionTransaction;
import io.nem.sdk.model.transaction.AccountAddressRestrictionTransactionFactory;
import io.nem.sdk.model.transaction.AccountLinkAction;
import io.nem.sdk.model.transaction.AccountLinkTransaction;
import io.nem.sdk.model.transaction.AccountLinkTransactionFactory;
import io.nem.sdk.model.transaction.AccountMetadataTransaction;
import io.nem.sdk.model.transaction.AccountMetadataTransactionFactory;
import io.nem.sdk.model.transaction.AccountMosaicRestrictionTransaction;
import io.nem.sdk.model.transaction.AccountMosaicRestrictionTransactionFactory;
import io.nem.sdk.model.transaction.AccountOperationRestrictionTransaction;
import io.nem.sdk.model.transaction.AccountOperationRestrictionTransactionFactory;
import io.nem.sdk.model.transaction.AccountRestrictionFlags;
import io.nem.sdk.model.transaction.AddressAliasTransaction;
import io.nem.sdk.model.transaction.AddressAliasTransactionFactory;
import io.nem.sdk.model.transaction.AggregateTransaction;
import io.nem.sdk.model.transaction.AggregateTransactionCosignature;
import io.nem.sdk.model.transaction.AggregateTransactionFactory;
import io.nem.sdk.model.transaction.Deadline;
import io.nem.sdk.model.transaction.HashLockTransaction;
import io.nem.sdk.model.transaction.HashLockTransactionFactory;
import io.nem.sdk.model.transaction.LockHashAlgorithmType;
import io.nem.sdk.model.transaction.MetadataTransaction;
import io.nem.sdk.model.transaction.MosaicAddressRestrictionTransaction;
import io.nem.sdk.model.transaction.MosaicAddressRestrictionTransactionFactory;
import io.nem.sdk.model.transaction.MosaicAliasTransaction;
import io.nem.sdk.model.transaction.MosaicAliasTransactionFactory;
import io.nem.sdk.model.transaction.MosaicDefinitionTransaction;
import io.nem.sdk.model.transaction.MosaicDefinitionTransactionFactory;
import io.nem.sdk.model.transaction.MosaicGlobalRestrictionTransaction;
import io.nem.sdk.model.transaction.MosaicGlobalRestrictionTransactionFactory;
import io.nem.sdk.model.transaction.MosaicMetadataTransaction;
import io.nem.sdk.model.transaction.MosaicMetadataTransactionFactory;
import io.nem.sdk.model.transaction.MosaicRestrictionType;
import io.nem.sdk.model.transaction.MosaicSupplyChangeTransaction;
import io.nem.sdk.model.transaction.MosaicSupplyChangeTransactionFactory;
import io.nem.sdk.model.transaction.MultisigAccountModificationTransaction;
import io.nem.sdk.model.transaction.MultisigAccountModificationTransactionFactory;
import io.nem.sdk.model.transaction.NamespaceMetadataTransaction;
import io.nem.sdk.model.transaction.NamespaceMetadataTransactionFactory;
import io.nem.sdk.model.transaction.NamespaceRegistrationTransaction;
import io.nem.sdk.model.transaction.NamespaceRegistrationTransactionFactory;
import io.nem.sdk.model.transaction.SecretLockTransaction;
import io.nem.sdk.model.transaction.SecretLockTransactionFactory;
import io.nem.sdk.model.transaction.SecretProofTransaction;
import io.nem.sdk.model.transaction.SecretProofTransactionFactory;
import io.nem.sdk.model.transaction.Transaction;
import io.nem.sdk.model.transaction.TransactionFactory;
import io.nem.sdk.model.transaction.TransactionType;
import io.nem.sdk.model.transaction.TransferTransaction;
import io.nem.sdk.model.transaction.TransferTransactionFactory;
import java.io.DataInputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.Validate;
import org.bouncycastle.util.encoders.Hex;


/**
 * Implementation of BinarySerialization. It uses the catbuffer generated builders to deserialize an
 * object.
 */
public class BinarySerializationImpl implements BinarySerialization {

    /**
     * Cached instance.
     */
    public static final BinarySerialization INSTANCE = new BinarySerializationImpl();

    /**
     * The serializers, one per {@link TransactionType} must be registered.
     */
    private final Map> serializers = new EnumMap<>(
        TransactionType.class);

    /**
     * Constructor
     */
    public BinarySerializationImpl() {
        register(new TransferTransactionSerializer());
        register(new MosaicSupplyChangeTransactionSerializer());
        register(new MosaicDefinitionTransactionSerializer());
        register(new AccountLinkTransactionSerializer());
        register(new AccountMetadataTransactionSerializer());
        register(new MosaicMetadataTransactionSerializer());
        register(new NamespaceMetadataTransactionSerializer());
        register(new NamespaceRegistrationTransactionSerializer());
        register(new SecretLockTransactionSerializer());
        register(new SecretProofTransactionSerializer());
        register(new AddressAliasTransactionSerializer());
        register(new MosaicAliasTransactionSerializer());
        register(new HashLockTransactionSerializer());
        register(new MultisigAccountModificationTransactionSerializer());
        register(new MosaicAddressRestrictionTransactionSerializer());
        register(new MosaicGlobalRestrictionTransactionSerializer());
        register(new AccountMosaicRestrictionTransactionSerializer());
        register(new AccountOperationRestrictionTransactionSerializer());
        register(new AccountAddressRestrictionTransactionSerializer());
        register(new AggregateTransactionSerializer(TransactionType.AGGREGATE_COMPLETE, this));
        register(new AggregateTransactionSerializer(TransactionType.AGGREGATE_BONDED, this));
    }

    /**
     * @param serializer the serializer to be registered.
     */
    private void register(TransactionSerializer serializer) {
        if (serializers.put(serializer.getTransactionType(), serializer) != null) {
            throw new IllegalArgumentException(
                "TransactionSerializer for type " + serializer.getTransactionType()
                    + " was already registered!");
        }
    }

    /**
     * It returns the registered {@link TransactionSerializer} for the given {@link
     * TransactionType}.
     *
     * @param transactionType the transaction type.
     * @param  the transaction type
     * @return the {@link TransactionSerializer}
     */
     TransactionSerializer resolveSerializer(
        TransactionType transactionType) {
        @SuppressWarnings("unchecked")
        TransactionSerializer mapper = (TransactionSerializer) serializers
            .get(transactionType);
        if (mapper == null) {
            throw new UnsupportedOperationException(
                "Unimplemented Transaction type " + transactionType);
        }
        return mapper;
    }

    /**
     * Serialization basic implementation, it just delegates the work to the transactions.
     *
     * @param transaction the transaction
     * @return the serialized transaction.
     */
    @Override
    public  byte[] serialize(T transaction) {
        Validate.notNull(transaction, "Transaction must not be null");
        TransactionBuilder transactionBuilder = getTransactionBuilder(transaction);
        return serializeTransaction(transactionBuilder.serialize(), transaction);
    }


    /**
     * Serialized the transfer transaction to embedded bytes.
     *
     * @param transaction the transaction
     * @param  the transaction class
     * @return bytes of the transaction.
     */
    public  byte[] serializeEmbedded(T transaction) {
        Validate.notNull(transaction, "Transaction must not be null");
        EmbeddedTransactionBuilder embeddedTransactionBuilder = EmbeddedTransactionBuilder.create(
            new KeyDto(getRequiredSignerBytes(transaction.getSigner())),
            transaction.getVersion().byteValue(),
            NetworkTypeDto.rawValueOf((byte) transaction.getNetworkType().getValue()),
            EntityTypeDto.rawValueOf((short) transaction.getType().getValue()));
        return serializeTransaction(embeddedTransactionBuilder.serialize(), transaction);
    }

    /**
     * Creates the right {@link EmbeddedTransactionBuilder} from the transaction
     *
     * @param transaction the transaction
     * @param  the transaction class
     * @return the {@link EmbeddedTransactionBuilder}
     */
    private  EmbeddedTransactionBuilder toEmbeddedTransactionBuilder(
        T transaction) {
        return TransactionBuilderFactory.createEmbeddedTransactionBuilder(
            SerializationUtils.toDataInput(serializeEmbedded(transaction)));
    }

    /**
     * Gets the top level {@link TransactionBuilder} for the given transaction
     *
     * @param transaction the transaction
     * @return the top level {@link TransactionBuilder}
     */
    private TransactionBuilder getTransactionBuilder(Transaction transaction) {

        final SignatureDto signatureDto = transaction.getSignature()
            .map(SerializationUtils::toSignatureDto)
            .orElseGet(() -> new SignatureDto(ByteBuffer.allocate(64)));

        final ByteBuffer signerBuffer = transaction.getSigner()
            .map(SerializationUtils::toByteBuffer).orElseGet(() -> ByteBuffer.allocate(32));

        return TransactionBuilder.create(
            signatureDto,
            new KeyDto(signerBuffer),
            transaction.getVersion().byteValue(),
            NetworkTypeDto.rawValueOf((byte) transaction.getNetworkType().getValue()),
            EntityTypeDto.rawValueOf((short) transaction.getType().getValue()),
            new AmountDto(transaction.getMaxFee().longValue()),
            new TimestampDto(transaction.getDeadline().getInstant()));
    }

    /**
     * This method concats the common bytes to the serialized specific transaction.
     *
     * @param  the type of the transaction
     * @param commonBytes the common byte array.
     * @param transaction the transaction.
     * @return the serialized transaction.
     */
    private  byte[] serializeTransaction(byte[] commonBytes, T transaction) {
        TransactionSerializer transactionSerializer = resolveSerializer(transaction.getType());
        Validate.isTrue(
            transactionSerializer.getTransactionClass().isAssignableFrom(transaction.getClass()),
            "Invalid TransactionSerializer's transaction class.");
        byte[] transactionBytes = transactionSerializer.toBodyBuilder(transaction).serialize();
        return SerializationUtils.concat(commonBytes, transactionBytes);
    }

    /**
     * It returns the transaction's byte array size useful to calculate its fee.
     *
     * @param  the type of the transaction
     * @param transaction the transaction
     * @return the size of the transaction.
     */
    @Override
    public  int getSize(T transaction) {
        return getTransactionBuilder(transaction).getSize() + resolveSerializer(
            transaction.getType()).toBodyBuilder(transaction)
            .getSize();
    }

    /**
     * Returns the transaction creator public account.
     *
     * @return the signer public account
     */
    private ByteBuffer getRequiredSignerBytes(Optional signer) {
        return signer.map(SerializationUtils::toByteBuffer)
            .orElseThrow(() -> new IllegalStateException("SignerBytes is required"));
    }

    /**
     * Deserialization of transactions. All the code related to the deserialization is handled in
     * the class and its helpers. Transaction Model Objects are not polluted with deserialization
     * functionality.
     *
     * @param payload the byte array payload
     * @return the {@link Transaction}
     */
    @Override
    @SuppressWarnings("squid:S1192")
    public Transaction deserialize(byte[] payload) {
        Validate.notNull(payload, "Payload must not be null");
        DataInputStream stream = SerializationUtils.toDataInput(payload);
        TransactionBuilder builder = TransactionBuilderFactory
            .createTransactionBuilder(stream);

        return toTransaction(builder);
    }

    /**
     * It converts a {@link TransactionBuilder} to a {@link Transaction}
     *
     * @param builder the builder
     * @return the {@link Transaction} model.
     */
    private Transaction toTransaction(TransactionBuilder builder) {
        TransactionType transactionType = TransactionType
            .rawValueOf(SerializationUtils.shortToUnsignedInt(builder.getType().getValue()));
        NetworkType networkType = NetworkType
            .rawValueOf(SerializationUtils.byteToUnsignedInt(builder.getNetwork().getValue()));

        Deadline deadline = new Deadline(
            SerializationUtils.toUnsignedBigInteger(builder.getDeadline().getTimestamp()));

        TransactionFactory factory = resolveSerializer(transactionType)
            .fromBodyBuilder(networkType, builder.getBody());

        factory.version(SerializationUtils.byteToUnsignedInt(builder.getVersion()));
        factory.maxFee(SerializationUtils.toUnsignedBigInteger(builder.getFee()));
        factory.deadline(deadline);
        if (!areAllZeros(builder.getSignature().getSignature().array())) {
            factory
                .signature(SerializationUtils.toHexString(builder.getSignature().getSignature()));
        }
        if (!areAllZeros(builder.getSignerPublicKey().getKey().array())) {
            factory.signer(
                SerializationUtils.toPublicAccount(builder.getSignerPublicKey(), networkType));
        }

        return factory.build();
    }


    /**
     * @param array the byte array.
     * @return if all the values in the array are zeros
     */
    private boolean areAllZeros(byte[] array) {
        for (byte b : array) {
            if (b != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * It deserializes a payload of a embedded transaction into a transact model.
     *
     * @param payload the payload as {@link DataInputStream}
     * @return the {@link Transaction} model.
     */
    public Transaction deserializeEmbedded(DataInputStream payload) {

        return ExceptionUtils.propagate(() -> {
            Validate.notNull(payload, "Payload must not be null");
            EmbeddedTransactionBuilder builder = TransactionBuilderFactory
                .createEmbeddedTransactionBuilder(payload);
            return toTransaction(builder);
        });
    }

    /**
     * It converts a {@link EmbeddedTransactionBuilder} to a {@link Transaction}
     *
     * @param builder the builder
     * @return the {@link Transaction} model.
     */
    private Transaction toTransaction(EmbeddedTransactionBuilder builder) {
        TransactionType transactionType = TransactionType
            .rawValueOf(SerializationUtils.shortToUnsignedInt(builder.getType().getValue()));
        NetworkType networkType = NetworkType
            .rawValueOf(SerializationUtils.byteToUnsignedInt(builder.getNetwork().getValue()));
        TransactionFactory factory = resolveSerializer(transactionType)
            .fromBodyBuilder(networkType, builder.getBody());
        factory
            .signer(
                SerializationUtils.toPublicAccount(builder.getSignerPublicKey(), networkType));
        factory.version(SerializationUtils.byteToUnsignedInt(builder.getVersion()));
        return factory.build();
    }


    /**
     * Interface of the serializer helper classes that know how to serialize/deserialize one type of
     * transaction from a payload.
     */
    interface TransactionSerializer {

        /**
         * @return the {@link TransactionType} of the transaction this helper handles.
         */
        TransactionType getTransactionType();

        /**
         * @return the transaction class this serializer handles.
         */
        Class getTransactionClass();

        /**
         * Subclasses would need to create the {@link TransactionFactory} for the handled {@link
         * TransactionType} with just the specific transaction values. Common values like maxFee and
         * deadline are handled at top level, subclasses won't need to duplicate the deserialization
         * efforts.
         *
         * @param networkType the network type
         * @param transactionBuilder the stream containing just the specific transaction values in
         * the right order.
         * @return the TransactionFactory of the transaction type this object handles.
         */
        TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder);

        /**
         * Subclasses would need to know how to serialize the internal components of a transaction,
         * the bytes that are serialized after the common attributes like max fee and duration.
         * Subclasses would use catbuffer's transaction body builders.
         *
         * @param transaction the transaction to be serialized
         * @return the catbuffer {@link Serializer}.
         */
        Serializer toBodyBuilder(T transaction);

    }

    private static class TransferTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.TRANSFER;
        }

        @Override
        public Class getTransactionClass() {
            return TransferTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            TransferTransactionBodyBuilder builder = ((TransferTransactionBodyBuilder) transactionBuilder);
            byte[] messageArray = builder.getMessage().array();
            MessageType messageType = MessageType
                .rawValueOf(SerializationUtils.byteToUnsignedInt(messageArray[0]));
            String messageHex = ConvertUtils.toHex(messageArray).substring(2);
            UnresolvedAddress recipient = SerializationUtils
                .toUnresolvedAddress(builder.getRecipientAddress());
            List mosaics = builder.getMosaics().stream()
                .map(SerializationUtils::toMosaic)
                .collect(Collectors.toList());
            Message message = Message.createFromPayload(messageType, messageHex);
            return TransferTransactionFactory.create(networkType,
                recipient, mosaics, message);
        }

        @Override
        public Serializer toBodyBuilder(TransferTransaction transaction) {
            return TransferTransactionBodyBuilder.create(
                new UnresolvedAddressDto(
                    SerializationUtils
                        .fromUnresolvedAddressToByteBuffer(transaction.getRecipient(),
                            transaction.getNetworkType())),
                getUnresolvedMosaicArray(transaction),
                getMessageBuffer(transaction));

        }

        /**
         * Gets mosaic array.
         *
         * @return Mosaic array.
         */
        private List getUnresolvedMosaicArray(
            TransferTransaction transaction) {
            // Create Mosaics
            final List unresolvedMosaicList =
                new ArrayList<>(transaction.getMosaics().size());
            //Sort mosaics first
            final List sortedMosaics = transaction.getMosaics().stream()
                .sorted(Comparator.comparing(m -> m.getId().getId()))
                .collect(Collectors.toList());

            for (final Mosaic mosaic : sortedMosaics) {
                final UnresolvedMosaicBuilder mosaicBuilder =
                    UnresolvedMosaicBuilder.create(
                        new UnresolvedMosaicIdDto(mosaic.getId().getIdAsLong()),
                        new AmountDto(mosaic.getAmount().longValue()));
                unresolvedMosaicList.add(mosaicBuilder);
            }
            return unresolvedMosaicList;
        }

        /**
         * Gets message buffer.
         *
         * @return Message buffer.
         */
        private ByteBuffer getMessageBuffer(TransferTransaction transaction) {
            final byte byteMessageType = (byte) transaction.getMessage().getType().getValue();
            final byte[] bytePayload = transaction.getMessage().getPayload()
                .getBytes(StandardCharsets.UTF_8);
            final ByteBuffer messageBuffer =
                ByteBuffer.allocate(bytePayload.length + 1 /* for the message type */);
            messageBuffer.put(byteMessageType);
            messageBuffer.put(bytePayload);
            return messageBuffer;
        }

    }

    private static class MosaicSupplyChangeTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MOSAIC_SUPPLY_CHANGE;
        }

        @Override
        public Class getTransactionClass() {
            return MosaicSupplyChangeTransaction.class;
        }


        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            MosaicSupplyChangeTransactionBodyBuilder builder = ((MosaicSupplyChangeTransactionBodyBuilder) transactionBuilder);
            UnresolvedMosaicId mosaicId = SerializationUtils
                .toUnresolvedMosaicId(builder.getMosaicId());
            MosaicSupplyChangeActionType action = MosaicSupplyChangeActionType
                .rawValueOf(builder.getAction().getValue());
            BigInteger delta = SerializationUtils.toUnsignedBigInteger(builder.getDelta());
            return MosaicSupplyChangeTransactionFactory
                .create(networkType, mosaicId, action, delta);
        }

        @Override
        public Serializer toBodyBuilder(MosaicSupplyChangeTransaction transaction) {
            return MosaicSupplyChangeTransactionBodyBuilder.create(
                new UnresolvedMosaicIdDto(transaction.getMosaicId().getId().longValue()),
                new AmountDto(transaction.getDelta().longValue()),
                MosaicSupplyChangeActionDto
                    .rawValueOf((byte) transaction.getAction().getValue()));

        }

    }

    private static class MosaicDefinitionTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MOSAIC_DEFINITION;
        }

        @Override
        public Class getTransactionClass() {
            return MosaicDefinitionTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            MosaicDefinitionTransactionBodyBuilder builder = (MosaicDefinitionTransactionBodyBuilder) transactionBuilder;
            MosaicNonce mosaicNonce = MosaicNonce
                .createFromBigInteger((long) builder.getNonce().getMosaicNonce());
            MosaicId mosaicId = SerializationUtils.toMosaicId(builder.getId());
            MosaicFlags mosaicFlags = MosaicFlags
                .create(builder.getFlags().contains(MosaicFlagsDto.SUPPLY_MUTABLE),
                    builder.getFlags().contains(MosaicFlagsDto.TRANSFERABLE),
                    builder.getFlags().contains(MosaicFlagsDto.RESTRICTABLE));
            int divisibility = SerializationUtils.byteToUnsignedInt(builder.getDivisibility());
            BlockDuration blockDuration = new BlockDuration(
                builder.getDuration().getBlockDuration());
            return MosaicDefinitionTransactionFactory
                .create(networkType, mosaicNonce, mosaicId, mosaicFlags, divisibility,
                    blockDuration);
        }

        @Override
        public Serializer toBodyBuilder(MosaicDefinitionTransaction transaction) {
            return MosaicDefinitionTransactionBodyBuilder.create(
                new MosaicIdDto(transaction.getMosaicId().getId().longValue()),
                new BlockDurationDto(transaction.getBlockDuration().getDuration()),
                new MosaicNonceDto(transaction.getMosaicNonce().getNonceAsInt()),
                getMosaicFlagsEnumSet(transaction),
                (byte) transaction.getDivisibility());

        }

        /**
         * Get the mosaic flags.
         *
         * @return Mosaic flags
         */
        private EnumSet getMosaicFlagsEnumSet(
            MosaicDefinitionTransaction transaction) {
            EnumSet mosaicFlagsBuilder = EnumSet.of(MosaicFlagsDto.NONE);
            if (transaction.getMosaicFlags().isSupplyMutable()) {
                mosaicFlagsBuilder.add(MosaicFlagsDto.SUPPLY_MUTABLE);
            }
            if (transaction.getMosaicFlags().isTransferable()) {
                mosaicFlagsBuilder.add(MosaicFlagsDto.TRANSFERABLE);
            }
            if (transaction.getMosaicFlags().isRestrictable()) {
                mosaicFlagsBuilder.add(MosaicFlagsDto.RESTRICTABLE);
            }
            return mosaicFlagsBuilder;
        }
    }

    private static class AccountLinkTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.ACCOUNT_LINK;
        }

        @Override
        public Class getTransactionClass() {
            return AccountLinkTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            AccountLinkTransactionBodyBuilder builder = (AccountLinkTransactionBodyBuilder) transactionBuilder;
            PublicAccount remoteAccount = SerializationUtils
                .toPublicAccount(builder.getRemotePublicKey(), networkType);
            AccountLinkAction linkAction = AccountLinkAction
                .rawValueOf(builder.getLinkAction().getValue());
            return AccountLinkTransactionFactory
                .create(networkType, remoteAccount, linkAction);
        }

        @Override
        public Serializer toBodyBuilder(AccountLinkTransaction transaction) {
            return AccountLinkTransactionBodyBuilder.create(
                SerializationUtils.toKeyDto(transaction.getRemoteAccount().getPublicKey()),
                AccountLinkActionDto.rawValueOf(transaction.getLinkAction().getValue()))
                ;

        }

    }

    private static class AccountMetadataTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.ACCOUNT_METADATA;
        }

        @Override
        public Class getTransactionClass() {
            return AccountMetadataTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            AccountMetadataTransactionBodyBuilder builder = (AccountMetadataTransactionBodyBuilder) transactionBuilder;
            PublicAccount targetAccount = SerializationUtils
                .toPublicAccount(builder.getTargetPublicKey(), networkType);
            BigInteger scopedMetadataKey = SerializationUtils
                .toUnsignedBigInteger(builder.getScopedMetadataKey());
            String value = SerializationUtils.toString(builder.getValue());
            return AccountMetadataTransactionFactory
                .create(networkType, targetAccount, scopedMetadataKey, value)
                .valueSizeDelta(SerializationUtils.shortToUnsignedInt(builder.getValueSizeDelta()));
        }

        @Override
        public Serializer toBodyBuilder(AccountMetadataTransaction transaction) {
            return AccountMetadataTransactionBodyBuilder.create(
                SerializationUtils.toKeyDto(transaction.getTargetAccount().getPublicKey()),
                transaction.getScopedMetadataKey().longValue(),
                (short) transaction.getValueSizeDelta(),
                ByteBuffer.wrap(MetadataTransaction.toByteArray(transaction.getValue()))
            );

        }

    }

    private static class MosaicMetadataTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MOSAIC_METADATA;
        }

        @Override
        public Class getTransactionClass() {
            return MosaicMetadataTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            MosaicMetadataTransactionBodyBuilder builder = (MosaicMetadataTransactionBodyBuilder) transactionBuilder;
            PublicAccount targetAccount = SerializationUtils
                .toPublicAccount(builder.getTargetPublicKey(), networkType);
            BigInteger scopedMetadataKey = SerializationUtils
                .toUnsignedBigInteger(builder.getScopedMetadataKey());
            String value = StringEncoder.getString(builder.getValue().array());
            UnresolvedMosaicId targetMosaicId = SerializationUtils
                .toUnresolvedMosaicId(builder.getTargetMosaicId());
            return MosaicMetadataTransactionFactory
                .create(networkType, targetAccount, targetMosaicId, scopedMetadataKey, value)
                .valueSizeDelta(SerializationUtils.shortToUnsignedInt(builder.getValueSizeDelta()));
        }

        @Override
        public Serializer toBodyBuilder(MosaicMetadataTransaction transaction) {
            return MosaicMetadataTransactionBodyBuilder.create(
                SerializationUtils.toKeyDto(transaction.getTargetAccount().getPublicKey()),
                transaction.getScopedMetadataKey().longValue(),
                new UnresolvedMosaicIdDto(transaction.getTargetMosaicId().getId().longValue()),
                (short) transaction.getValueSizeDelta(),
                ByteBuffer.wrap(MetadataTransaction.toByteArray(transaction.getValue()))
            );

        }

    }


    private static class NamespaceMetadataTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.NAMESPACE_METADATA;
        }

        @Override
        public Class getTransactionClass() {
            return NamespaceMetadataTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            NamespaceMetadataTransactionBodyBuilder builder = (NamespaceMetadataTransactionBodyBuilder) transactionBuilder;
            PublicAccount targetAccount = SerializationUtils
                .toPublicAccount(builder.getTargetPublicKey(), networkType);
            BigInteger scopedMetadataKey = SerializationUtils
                .toUnsignedBigInteger(builder.getScopedMetadataKey());
            String value = StringEncoder.getString(builder.getValue().array());
            NamespaceId targetNamespaceId = SerializationUtils
                .toNamespaceId(builder.getTargetNamespaceId());
            return NamespaceMetadataTransactionFactory
                .create(networkType, targetAccount, targetNamespaceId, scopedMetadataKey, value)
                .valueSizeDelta(SerializationUtils.shortToUnsignedInt(builder.getValueSizeDelta()));
        }

        @Override
        public Serializer toBodyBuilder(NamespaceMetadataTransaction transaction) {
            return NamespaceMetadataTransactionBodyBuilder.create(
                new KeyDto(transaction.getTargetAccount().getPublicKey().getByteBuffer()),
                transaction.getScopedMetadataKey().longValue(),
                new NamespaceIdDto(transaction.getTargetNamespaceId().getId().longValue()),
                (short) transaction.getValueSizeDelta(),
                ByteBuffer.wrap(MetadataTransaction.toByteArray(transaction.getValue())
                ));

        }

    }

    private static class NamespaceRegistrationTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.NAMESPACE_REGISTRATION;
        }

        @Override
        public Class getTransactionClass() {
            return NamespaceRegistrationTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            NamespaceRegistrationTransactionBodyBuilder builder = (NamespaceRegistrationTransactionBodyBuilder) transactionBuilder;

            NamespaceRegistrationType namespaceRegistrationType = NamespaceRegistrationType
                .rawValueOf(builder.getRegistrationType().getValue());
            String namespaceName = StringEncoder.getString(builder.getName().array());
            NamespaceId namespaceId = SerializationUtils
                .toNamespaceId(builder.getId());

            Optional duration =
                namespaceRegistrationType == NamespaceRegistrationType.ROOT_NAMESPACE ? Optional
                    .ofNullable(builder.getDuration()).map(
                        BlockDurationDto::getBlockDuration)
                    .map(SerializationUtils::toUnsignedBigInteger)
                    : Optional.empty();

            Optional parentId =
                namespaceRegistrationType == NamespaceRegistrationType.SUB_NAMESPACE ? Optional
                    .of(builder.getParentId())
                    .map(SerializationUtils::toNamespaceId)
                    : Optional.empty();

            return NamespaceRegistrationTransactionFactory
                .create(networkType, namespaceName, namespaceId, namespaceRegistrationType,
                    duration, parentId);
        }

        @Override
        public Serializer toBodyBuilder(NamespaceRegistrationTransaction transaction) {
            NamespaceRegistrationTransactionBodyBuilder txBuilder;
            ByteBuffer namespaceNameByteBuffer = ByteBuffer
                .wrap(StringEncoder.getBytes(transaction.getNamespaceName()));
            NamespaceIdDto namespaceIdDto = new NamespaceIdDto(
                transaction.getNamespaceId().getId().longValue());

            if (transaction.getNamespaceRegistrationType()
                == NamespaceRegistrationType.ROOT_NAMESPACE) {
                txBuilder =
                    NamespaceRegistrationTransactionBodyBuilder.create(
                        new BlockDurationDto(transaction.getDuration()
                            .orElseThrow(() -> new IllegalStateException("Duration is required"))
                            .longValue()),
                        namespaceIdDto,
                        namespaceNameByteBuffer);

            } else {
                txBuilder =
                    NamespaceRegistrationTransactionBodyBuilder.create(
                        new NamespaceIdDto(transaction.getParentId()
                            .orElseThrow(() -> new IllegalStateException("ParentId is required"))
                            .getId().longValue()),
                        namespaceIdDto,
                        namespaceNameByteBuffer);
            }
            return txBuilder;
        }

    }

    private static class SecretLockTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.SECRET_LOCK;
        }

        @Override
        public Class getTransactionClass() {
            return SecretLockTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            SecretLockTransactionBodyBuilder builder = (SecretLockTransactionBodyBuilder) transactionBuilder;

            Mosaic mosaic = SerializationUtils.toMosaic(builder.getMosaic());
            BigInteger duration = SerializationUtils
                .toUnsignedBigInteger(builder.getDuration().getBlockDuration());
            LockHashAlgorithmType hashAlgorithm = LockHashAlgorithmType.rawValueOf(
                SerializationUtils.byteToUnsignedInt(builder.getHashAlgorithm().getValue()));
            String secret = SerializationUtils.toHexString(builder.getSecret());
            UnresolvedAddress recipient = SerializationUtils
                .toUnresolvedAddress(builder.getRecipientAddress());
            return SecretLockTransactionFactory
                .create(networkType, mosaic, duration, hashAlgorithm, secret, recipient);
        }

        @Override
        public Serializer toBodyBuilder(SecretLockTransaction transaction) {
            UnresolvedMosaicIdDto mosaicId = new UnresolvedMosaicIdDto(
                transaction.getMosaic().getId().getIdAsLong());
            AmountDto amount = new AmountDto(transaction.getMosaic().getAmount().longValue());
            UnresolvedMosaicBuilder unresolvedMosaicBuilder = UnresolvedMosaicBuilder.create(
                mosaicId,
                amount);
            return SecretLockTransactionBodyBuilder
                .create(new Hash256Dto(getSecretBuffer(transaction)),
                    unresolvedMosaicBuilder,
                    new BlockDurationDto(transaction.getDuration().longValue()),
                    LockHashAlgorithmDto
                        .rawValueOf((byte) transaction.getHashAlgorithm().getValue()),
                    new UnresolvedAddressDto(
                        SerializationUtils
                            .fromUnresolvedAddressToByteBuffer(transaction.getRecipient(),
                                transaction.getNetworkType())))
                ;
        }

        /**
         * Gets secret buffer.
         *
         * @param transaction the transaction.
         * @return Secret buffer.
         */
        private ByteBuffer getSecretBuffer(SecretLockTransaction transaction) {
            final ByteBuffer secretBuffer = ByteBuffer.allocate(32);
            secretBuffer.put(Hex.decode(transaction.getSecret()));
            return secretBuffer;
        }
    }

    private static class SecretProofTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.SECRET_PROOF;
        }

        @Override
        public Class getTransactionClass() {
            return SecretProofTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            SecretProofTransactionBodyBuilder builder = (SecretProofTransactionBodyBuilder) transactionBuilder;

            LockHashAlgorithmType hashType = LockHashAlgorithmType.rawValueOf(
                SerializationUtils.byteToUnsignedInt(builder.getHashAlgorithm().getValue()));
            UnresolvedAddress recipient = SerializationUtils
                .toUnresolvedAddress(builder.getRecipientAddress());
            String secret = SerializationUtils.toHexString(builder.getSecret());
            String proof = SerializationUtils.toHexString(builder.getProof());
            return SecretProofTransactionFactory
                .create(networkType, hashType, recipient, secret, proof);
        }

        @Override
        public Serializer toBodyBuilder(SecretProofTransaction transaction) {
            return SecretProofTransactionBodyBuilder.create(
                new Hash256Dto(getSecretBuffer(transaction)),
                LockHashAlgorithmDto.rawValueOf((byte) transaction.getHashType().getValue()),
                new UnresolvedAddressDto(
                    SerializationUtils
                        .fromUnresolvedAddressToByteBuffer(transaction.getRecipient(),
                            transaction.getNetworkType())),
                getProofBuffer(transaction));
        }

        /**
         * Gets proof buffer
         *
         * @param transaction the transaction.
         * @return the secret buffer.
         */
        private ByteBuffer getSecretBuffer(SecretProofTransaction transaction) {
            final ByteBuffer secretBuffer = ByteBuffer.allocate(32);
            secretBuffer.put(Hex.decode(transaction.getSecret()));
            return secretBuffer;
        }

        /**
         * Gets proof buffer
         *
         * @param transaction the transaction.
         * @return Proof buffer.
         */
        private ByteBuffer getProofBuffer(SecretProofTransaction transaction) {
            final byte[] proofBytes = Hex.decode(transaction.getProof());
            return ByteBuffer.wrap(proofBytes);
        }
    }

    private static class AddressAliasTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.ADDRESS_ALIAS;
        }

        @Override
        public Class getTransactionClass() {
            return AddressAliasTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            AddressAliasTransactionBodyBuilder builder = (AddressAliasTransactionBodyBuilder) transactionBuilder;

            AliasAction aliasAction = AliasAction
                .rawValueOf(builder.getAliasAction().getValue());
            NamespaceId namespaceId = SerializationUtils
                .toNamespaceId(builder.getNamespaceId());
            Address address = SerializationUtils.toAddress(builder.getAddress());

            return AddressAliasTransactionFactory
                .create(networkType, aliasAction, namespaceId, address);
        }

        @Override
        public Serializer toBodyBuilder(AddressAliasTransaction transaction) {
            NamespaceIdDto namespaceIdDto = new NamespaceIdDto(
                transaction.getNamespaceId().getIdAsLong());
            AliasActionDto aliasActionDto = AliasActionDto
                .rawValueOf(transaction.getAliasAction().getValue());
            AddressDto addressDto = new AddressDto(SerializationUtils
                .fromUnresolvedAddressToByteBuffer(transaction.getAddress(),
                    transaction.getNetworkType()));
            return AddressAliasTransactionBodyBuilder
                .create(namespaceIdDto, addressDto, aliasActionDto);
        }

    }

    private static class MosaicAliasTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MOSAIC_ALIAS;
        }

        @Override
        public Class getTransactionClass() {
            return MosaicAliasTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            MosaicAliasTransactionBodyBuilder builder = (MosaicAliasTransactionBodyBuilder) transactionBuilder;

            AliasAction aliasAction = AliasAction
                .rawValueOf(builder.getAliasAction().getValue());
            NamespaceId namespaceId = SerializationUtils.toNamespaceId(builder.getNamespaceId());
            MosaicId mosaicId = SerializationUtils.toMosaicId(builder.getMosaicId());

            return MosaicAliasTransactionFactory
                .create(networkType, aliasAction, namespaceId, mosaicId);
        }

        @Override
        public Serializer toBodyBuilder(MosaicAliasTransaction transaction) {
            return MosaicAliasTransactionBodyBuilder.create(
                new NamespaceIdDto(transaction.getNamespaceId().getIdAsLong()),
                new MosaicIdDto(transaction.getMosaicId().getIdAsLong()),
                AliasActionDto.rawValueOf(transaction.getAliasAction().getValue())
            );
        }

    }

    private static class HashLockTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.HASH_LOCK;
        }

        @Override
        public Class getTransactionClass() {
            return HashLockTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            HashLockTransactionBodyBuilder builder = (HashLockTransactionBodyBuilder) transactionBuilder;

            Mosaic mosaic = SerializationUtils.toMosaic(builder.getMosaic());
            BigInteger duration = SerializationUtils
                .toUnsignedBigInteger(builder.getDuration().getBlockDuration());
            return HashLockTransactionFactory
                .create(networkType, mosaic, duration,
                    SerializationUtils.toHexString(builder.getHash()));
        }

        @Override
        public Serializer toBodyBuilder(HashLockTransaction transaction) {
            return HashLockTransactionBodyBuilder.create(
                UnresolvedMosaicBuilder.create(
                    new UnresolvedMosaicIdDto(transaction.getMosaic().getId().getIdAsLong()),
                    new AmountDto(transaction.getMosaic().getAmount().longValue())),
                new BlockDurationDto(transaction.getDuration().longValue()),
                new Hash256Dto(getHashBuffer(transaction)));
        }


        /**
         * Gets hash buffer.
         *
         * @param transaction the transaction.
         * @return Hash buffer.
         */
        private ByteBuffer getHashBuffer(HashLockTransaction transaction) {
            return ByteBuffer.wrap(Hex.decode(transaction.getHash()));
        }
    }


    private static class AccountAddressRestrictionTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.ACCOUNT_ADDRESS_RESTRICTION;
        }

        @Override
        public Class getTransactionClass() {
            return AccountAddressRestrictionTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            AccountAddressRestrictionTransactionBodyBuilder builder = (AccountAddressRestrictionTransactionBodyBuilder) transactionBuilder;

            long restrictionFlagsValue = builder.getRestrictionFlags().stream()
                .mapToLong(AccountRestrictionFlagsDto::getValue).sum();

            AccountRestrictionFlags restrictionFlags = AccountRestrictionFlags
                .rawValueOf((int) restrictionFlagsValue);

            List restrictionAdditions = builder.getRestrictionAdditions()
                .stream()
                .map(SerializationUtils::toUnresolvedAddress)
                .collect(Collectors.toList());

            List restrictionDeletions = builder.getRestrictionDeletions()
                .stream()
                .map(SerializationUtils::toUnresolvedAddress)
                .collect(Collectors.toList());

            return AccountAddressRestrictionTransactionFactory
                .create(networkType, restrictionFlags, restrictionAdditions, restrictionDeletions);
        }

        @Override
        public Serializer toBodyBuilder(AccountAddressRestrictionTransaction transaction) {

            EnumSet flags = transaction.getRestrictionFlags().getFlags()
                .stream().map(f -> AccountRestrictionFlagsDto.rawValueOf(
                    (short) f.getValue())).collect(Collectors
                    .toCollection(() -> EnumSet.noneOf(AccountRestrictionFlagsDto.class)));

            List restrictionAdditions = transaction.getRestrictionAdditions()
                .stream()
                .map(
                    a -> SerializationUtils.toUnresolvedAddress(a, transaction.getNetworkType()))
                .collect(Collectors.toList());

            List restrictionDeletions = transaction.getRestrictionDeletions()
                .stream()
                .map(
                    a -> SerializationUtils.toUnresolvedAddress(a, transaction.getNetworkType()))
                .collect(Collectors.toList());

            return AccountAddressRestrictionTransactionBodyBuilder.create(
                flags,
                restrictionAdditions, restrictionDeletions);
        }


    }

    private static class AccountMosaicRestrictionTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.ACCOUNT_MOSAIC_RESTRICTION;
        }

        @Override
        public Class getTransactionClass() {
            return AccountMosaicRestrictionTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            AccountMosaicRestrictionTransactionBodyBuilder builder = (AccountMosaicRestrictionTransactionBodyBuilder) transactionBuilder;
            long restrictionFlagsValues = builder.getRestrictionFlags().stream()
                .mapToLong(AccountRestrictionFlagsDto::getValue).sum();

            AccountRestrictionFlags restrictionFlags = AccountRestrictionFlags
                .rawValueOf((int) restrictionFlagsValues);

            List restrictionAdditions = builder.getRestrictionAdditions()
                .stream().map(SerializationUtils::toUnresolvedMosaicId)
                .collect(Collectors.toList());

            List restrictionDeletions = builder.getRestrictionDeletions()
                .stream().map(SerializationUtils::toUnresolvedMosaicId)
                .collect(Collectors.toList());

            return AccountMosaicRestrictionTransactionFactory
                .create(networkType, restrictionFlags, restrictionAdditions, restrictionDeletions);
        }

        @Override
        public Serializer toBodyBuilder(AccountMosaicRestrictionTransaction transaction) {

            EnumSet flags = transaction.getRestrictionFlags().getFlags()
                .stream().map(f -> AccountRestrictionFlagsDto.rawValueOf(
                    (short) f.getValue())).collect(Collectors
                    .toCollection(() -> EnumSet.noneOf(AccountRestrictionFlagsDto.class)));

            List restrictionAdditions = transaction.getRestrictionAdditions()
                .stream().map(a -> new UnresolvedMosaicIdDto(a.getIdAsLong()))
                .collect(Collectors.toList());

            List restrictionDeletions = transaction.getRestrictionDeletions()
                .stream().map(a -> new UnresolvedMosaicIdDto(a.getIdAsLong()))
                .collect(Collectors.toList());

            return AccountMosaicRestrictionTransactionBodyBuilder.create(
                flags,
                restrictionAdditions, restrictionDeletions);
        }

    }

    private static class AccountOperationRestrictionTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.ACCOUNT_OPERATION_RESTRICTION;
        }

        @Override
        public Class getTransactionClass() {
            return AccountOperationRestrictionTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            AccountOperationRestrictionTransactionBodyBuilder builder = (AccountOperationRestrictionTransactionBodyBuilder) transactionBuilder;

            long restrictionFlagsValue = builder.getRestrictionFlags().stream()
                .mapToLong(AccountRestrictionFlagsDto::getValue).sum();

            AccountRestrictionFlags restrictionFlags = AccountRestrictionFlags
                .rawValueOf((int) restrictionFlagsValue);

            List restrictionAdditions = builder.getRestrictionAdditions()
                .stream()
                .map(op -> TransactionType.rawValueOf(op.getValue()))
                .collect(Collectors.toList());

            List restrictionDeletions = builder.getRestrictionDeletions()
                .stream()
                .map(op -> TransactionType.rawValueOf(op.getValue()))
                .collect(Collectors.toList());

            return AccountOperationRestrictionTransactionFactory
                .create(networkType, restrictionFlags, restrictionAdditions, restrictionDeletions);
        }

        @Override
        public Serializer toBodyBuilder(AccountOperationRestrictionTransaction transaction) {

            EnumSet flags = transaction.getRestrictionFlags().getFlags()
                .stream().map(f -> AccountRestrictionFlagsDto.rawValueOf(
                    (short) f.getValue())).collect(Collectors
                    .toCollection(() -> EnumSet.noneOf(AccountRestrictionFlagsDto.class)));

            List restrictionAdditions = transaction.getRestrictionAdditions()
                .stream()
                .map(
                    a -> EntityTypeDto.rawValueOf((short) a.getValue()))
                .collect(Collectors.toList());

            List restrictionDeletions = transaction.getRestrictionDeletions()
                .stream()
                .map(
                    a -> EntityTypeDto.rawValueOf((short) a.getValue()))
                .collect(Collectors.toList());

            return AccountOperationRestrictionTransactionBodyBuilder.create(
                flags,
                restrictionAdditions, restrictionDeletions);
        }

    }

    private static class MosaicAddressRestrictionTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MOSAIC_ADDRESS_RESTRICTION;
        }

        @Override
        public Class getTransactionClass() {
            return MosaicAddressRestrictionTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            MosaicAddressRestrictionTransactionBodyBuilder builder = (MosaicAddressRestrictionTransactionBodyBuilder) transactionBuilder;

            UnresolvedMosaicId mosaicId = SerializationUtils
                .toUnresolvedMosaicId(builder.getMosaicId());
            BigInteger restrictionKey = SerializationUtils
                .toUnsignedBigInteger(builder.getRestrictionKey());
            UnresolvedAddress targetAddress = SerializationUtils
                .toUnresolvedAddress(builder.getTargetAddress());
            BigInteger newRestrictionValue = SerializationUtils
                .toUnsignedBigInteger(builder.getNewRestrictionValue());
            BigInteger previousRestrictionValue = SerializationUtils
                .toUnsignedBigInteger(builder.getPreviousRestrictionValue());
            return MosaicAddressRestrictionTransactionFactory
                .create(networkType, mosaicId, restrictionKey, targetAddress, newRestrictionValue)
                .previousRestrictionValue(previousRestrictionValue);
        }

        @Override
        public Serializer toBodyBuilder(MosaicAddressRestrictionTransaction transaction) {
            UnresolvedAddressDto unresolvedAddressDto = SerializationUtils
                .toUnresolvedAddress(transaction.getTargetAddress(),
                    transaction.getNetworkType());
            return MosaicAddressRestrictionTransactionBodyBuilder.create(
                new UnresolvedMosaicIdDto(transaction.getMosaicId().getIdAsLong()),
                transaction.getRestrictionKey().longValue(),
                transaction.getPreviousRestrictionValue().longValue(),
                transaction.getNewRestrictionValue().longValue(), unresolvedAddressDto
            );
        }

    }

    private static class MosaicGlobalRestrictionTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MOSAIC_GLOBAL_RESTRICTION;
        }

        @Override
        public Class getTransactionClass() {
            return MosaicGlobalRestrictionTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            MosaicGlobalRestrictionTransactionBodyBuilder builder = (MosaicGlobalRestrictionTransactionBodyBuilder) transactionBuilder;
            UnresolvedMosaicId mosaicId = SerializationUtils
                .toUnresolvedMosaicId(builder.getMosaicId());
            BigInteger restrictionKey = SerializationUtils
                .toUnsignedBigInteger(builder.getRestrictionKey());
            BigInteger newRestrictionValue = SerializationUtils
                .toUnsignedBigInteger(builder.getNewRestrictionValue());
            BigInteger previousRestrictionValue = SerializationUtils
                .toUnsignedBigInteger(builder.getPreviousRestrictionValue());
            MosaicRestrictionType newRestrictionType = MosaicRestrictionType
                .rawValueOf(builder.getNewRestrictionType().getValue());
            MosaicRestrictionType previousRestrictionType = MosaicRestrictionType
                .rawValueOf(builder.getPreviousRestrictionType().getValue());
            return MosaicGlobalRestrictionTransactionFactory
                .create(networkType, mosaicId, restrictionKey, newRestrictionValue,
                    newRestrictionType)
                .referenceMosaicId(
                    SerializationUtils.toUnresolvedMosaicId(builder.getReferenceMosaicId()))
                .previousRestrictionValue(previousRestrictionValue)
                .previousRestrictionType(previousRestrictionType);
        }

        @Override
        public Serializer toBodyBuilder(MosaicGlobalRestrictionTransaction transaction) {
            return MosaicGlobalRestrictionTransactionBodyBuilder.create(
                new UnresolvedMosaicIdDto(transaction.getMosaicId().getIdAsLong()),
                new UnresolvedMosaicIdDto(transaction.getReferenceMosaicId().getIdAsLong()),
                transaction.getRestrictionKey().longValue(),
                transaction.getPreviousRestrictionValue().longValue(),
                transaction.getNewRestrictionValue().longValue(),
                MosaicRestrictionTypeDto
                    .rawValueOf(transaction.getPreviousRestrictionType().getValue()),
                MosaicRestrictionTypeDto
                    .rawValueOf(transaction.getNewRestrictionType().getValue()));
        }

    }


    private static class MultisigAccountModificationTransactionSerializer implements
        TransactionSerializer {

        @Override
        public TransactionType getTransactionType() {
            return TransactionType.MULTISIG_ACCOUNT_MODIFICATION;
        }

        @Override
        public Class getTransactionClass() {
            return MultisigAccountModificationTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {

            MultisigAccountModificationTransactionBodyBuilder builder = (MultisigAccountModificationTransactionBodyBuilder) transactionBuilder;
            byte minApprovalDelta = builder.getMinApprovalDelta();
            byte minRemovalDelta = builder.getMinRemovalDelta();

            List publicKeyAdditions = builder.getPublicKeyAdditions()
                .stream()
                .map(op -> SerializationUtils.toPublicAccount(op, networkType))
                .collect(Collectors.toList());

            List publicKeyDeletions = builder.getPublicKeyDeletions()
                .stream()
                .map(op -> SerializationUtils.toPublicAccount(op, networkType))
                .collect(Collectors.toList());

            return MultisigAccountModificationTransactionFactory
                .create(networkType, minApprovalDelta, minRemovalDelta, publicKeyAdditions,
                    publicKeyDeletions);
        }

        @Override
        public Serializer toBodyBuilder(MultisigAccountModificationTransaction transaction) {

            List publicKeyAdditions = transaction.getPublicKeyAdditions()
                .stream()
                .map(
                    a -> SerializationUtils.toKeyDto(a.getPublicKey()))
                .collect(Collectors.toList());

            List publicKeyDeletions = transaction.getPublicKeyDeletions()
                .stream()
                .map(
                    a -> SerializationUtils.toKeyDto(a.getPublicKey()))
                .collect(Collectors.toList());

            return MultisigAccountModificationTransactionBodyBuilder.create(
                transaction.getMinRemovalDelta(),
                transaction.getMinApprovalDelta(),

                publicKeyAdditions, publicKeyDeletions);
        }


    }


    private static class AggregateTransactionSerializer implements
        TransactionSerializer {

        private final TransactionType transactionType;

        private final BinarySerializationImpl transactionSerialization;

        public AggregateTransactionSerializer(TransactionType transactionType,
            BinarySerializationImpl transactionSerialization) {
            this.transactionType = transactionType;
            this.transactionSerialization = transactionSerialization;
        }

        @Override
        public TransactionType getTransactionType() {
            return transactionType;
        }

        @Override
        public Class getTransactionClass() {
            return AggregateTransaction.class;
        }

        @Override
        public TransactionFactory fromBodyBuilder(NetworkType networkType,
            Serializer transactionBuilder) {
            AggregateTransactionBodyBuilder builder = (AggregateTransactionBodyBuilder) transactionBuilder;
            List transactions = builder.getTransactions().stream()
                .map(transactionSerialization::toTransaction
                ).collect(Collectors.toList());

            List cosignatures = builder.getCosignatures().stream()
                .map(cosignatureBuilder -> getAggregateTransactionCosignature(networkType,
                    cosignatureBuilder)).collect(Collectors.toList());
            return AggregateTransactionFactory.create(
                getTransactionType(), networkType,
                SerializationUtils.toHexString(builder.getTransactionsHash()), transactions,
                cosignatures);
        }

        private AggregateTransactionCosignature getAggregateTransactionCosignature(
            NetworkType networkType, CosignatureBuilder cosignatureBuilder) {
            PublicAccount signer =
                SerializationUtils.toPublicAccount(
                    cosignatureBuilder.getSignerPublicKey(), networkType);
            String cosignature = SerializationUtils
                .toHexString(cosignatureBuilder.getSignature().getSignature());
            return new AggregateTransactionCosignature(cosignature, signer);
        }


        @Override
        public Serializer toBodyBuilder(AggregateTransaction transaction) {

            List transactions = transaction.getInnerTransactions()
                .stream()
                .map(transactionSerialization::toEmbeddedTransactionBuilder
                ).collect(Collectors.toList());

            List cosignatures = transaction.getCosignatures().stream()
                .map(this::getCosignatureBuilder).collect(Collectors.toList());

            return AggregateTransactionBodyBuilder
                .create(SerializationUtils.toHash256Dto(transaction.getTransactionsHash()),
                    transactions, cosignatures);
        }

        private CosignatureBuilder getCosignatureBuilder(AggregateTransactionCosignature c) {
            return CosignatureBuilder
                .create(SerializationUtils.toKeyDto(c.getSigner().getPublicKey()),
                    SerializationUtils.toSignatureDto(c.getSignature()));
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy