7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

nemAdvent Calendar 2021

Day 15

PluginTransactionを作成してDockerビルドしsymbol-bootstrapで起動する

Last updated at Posted at 2022-05-06

CatapultでPluginTransactionを作成する

この記事の続きです

前回に続いてType違いのTransferTransactionを作成します.

今回はタイプ違いのトランザクションを追加してみることだけを目指していたので詳しい動作については自分自身も未だ理解できておらず,説明ができないので今回は省略させていただきます.個人的な目標としてはこれらの動作をしっかり把握して新しいトランザクションを作ってみたいです.

Transactionを追加する

TransfervTransactionの構造を定義する

TransferTransactionのタイプ違い(中身は同じ)なトランザクションを作成するのでclient/catapult/plugins/txes/transfer/src/model/TransferTransaction.hの中身をコピーしてTransfervTransactionとして書き換えます.前回の記事で作成したものと同じ構造にします.

client/catapult/plugins/txes/transfer/src/model/TransfervTransaction.h
#pragma once
#include "TransferEntityType.h"
#include "catapult/model/Mosaic.h"
#include "catapult/model/Transaction.h"

namespace catapult { namespace model {

#pragma pack(push, 1)

	/// Binary layout for a transferv transaction body.
	template<typename THeader>
	struct TransfervTransactionBody : public THeader {
	private:
		using TransactionType = TransfervTransactionBody<THeader>;

	public:
		DEFINE_TRANSACTION_CONSTANTS(Entity_Type_Transferv, 1)

	public:
		/// Recipient address.
		UnresolvedAddress RecipientAddress;

		/// Message size in bytes.
		uint16_t MessageSize;

		/// Number of mosaics.
		uint8_t MosaicsCount;

		/// Reserved padding to align Mosaics on 8-byte boundary.
		uint32_t TransferTransactionBody_Reserved1;
		uint8_t TransferTransactionBody_Reserved2;

		// followed by mosaics data if MosaicsCount != 0
		DEFINE_TRANSACTION_VARIABLE_DATA_ACCESSORS(Mosaics, UnresolvedMosaic)

		// followed by message data if MessageSize != 0
		DEFINE_TRANSACTION_VARIABLE_DATA_ACCESSORS(Message, uint8_t)

	private:
		template<typename T>
		static auto* MosaicsPtrT(T& transaction) {
			return transaction.MosaicsCount ? THeader::PayloadStart(transaction) : nullptr;
		}

		template<typename T>
		static auto* MessagePtrT(T& transaction) {
			auto* pPayloadStart = THeader::PayloadStart(transaction);
			return transaction.MessageSize && pPayloadStart
					? pPayloadStart + transaction.MosaicsCount * sizeof(UnresolvedMosaic)
					: nullptr;
		}

	public:
		/// Calculates the real size of transferv \a transaction.
		static constexpr uint64_t CalculateRealSize(const TransactionType& transaction) noexcept {
			return sizeof(TransactionType) + transaction.MessageSize + transaction.MosaicsCount * sizeof(UnresolvedMosaic);
		}
	};

	DEFINE_EMBEDDABLE_TRANSACTION(Transferv)

#pragma pack(pop)
}}

Pluginを追加する

Transactionの動作を定義する

client/catapult/plugins/txes/transfer/src/plugins/TransfervTransactionPlugin.cpp
#include "TransfervTransactionPlugin.h"
#include "src/model/TransferNotifications.h"
#include "src/model/TransfervTransaction.h"
#include "catapult/model/NotificationSubscriber.h"
#include "catapult/model/TransactionPluginFactory.h"

using namespace catapult::model;

namespace catapult { namespace plugins {

	namespace {
		template<typename TTransaction>
		void Publish(const TTransaction& transaction, const PublishContext& context, NotificationSubscriber& sub) {
			auto padding = transaction.TransferTransactionBody_Reserved1 << 8 | transaction.TransferTransactionBody_Reserved2;
			sub.notify(InternalPaddingNotification(padding));
			sub.notify(AccountAddressNotification(transaction.RecipientAddress));
			sub.notify(AddressInteractionNotification(context.SignerAddress, transaction.Type, { transaction.RecipientAddress }));

			const auto* pMosaics = transaction.MosaicsPtr();
			for (auto i = 0u; i < transaction.MosaicsCount; ++i) {
				sub.notify(BalanceTransferNotification(
						context.SignerAddress,
						transaction.RecipientAddress,
						pMosaics[i].MosaicId,
						pMosaics[i].Amount));
			}

			if (transaction.MessageSize) {
				sub.notify(TransferMessageNotification(
						transaction.SignerPublicKey,
						transaction.RecipientAddress,
						transaction.MessageSize,
						transaction.MessagePtr()));
			}

			if (transaction.MosaicsCount)
				sub.notify(TransferMosaicsNotification(transaction.MosaicsCount, pMosaics));
		}
	}

	DEFINE_TRANSACTION_PLUGIN_FACTORY(Transferv, Default, Publish)
}}

Pluginとして登録する

client/catapult/plugins/txes/transfer/src/plugins/TransferPlugin.cpp
#include "TransferPlugin.h"
#include "TransferTransactionPlugin.h"
#include "src/config/TransferConfiguration.h"
#include "src/observers/Observers.h"
#include "src/validators/Validators.h"
#include "catapult/config/CatapultDataDirectory.h"
#include "catapult/config/CatapultKeys.h"
#include "catapult/crypto/OpensslKeyUtils.h"
#include "catapult/model/Address.h"
#include "catapult/plugins/PluginManager.h"
+ #include "TransfervTransactionPlugin.h"

namespace catapult { namespace plugins {

	void RegisterTransferSubsystem(PluginManager& manager) {
		manager.addTransactionSupport(CreateTransferTransactionPlugin());
+ 		manager.addTransactionSupport(CreateTransfervTransactionPlugin());

		auto config = model::LoadPluginConfiguration<config::TransferConfiguration>(manager.config(), "catapult.plugins.transfer");
		manager.addStatelessValidatorHook([config](auto& builder) {
			builder.add(validators::CreateTransferMessageValidator(config.MaxMessageSize));
			builder.add(validators::CreateTransferMosaicsValidator());
		});
		if (!manager.userConfig().EnableDelegatedHarvestersAutoDetection)
			return;
		auto encryptionPrivateKeyPemFilename = config::GetNodePrivateKeyPemFilename(manager.userConfig().CertificateDirectory);
		auto encryptionPublicKey = crypto::ReadPublicKeyFromPrivateKeyPemFile(encryptionPrivateKeyPemFilename);
		auto recipient = model::PublicKeyToAddress(encryptionPublicKey, manager.config().Network.Identifier);
		auto dataDirectory = config::CatapultDataDirectory(manager.userConfig().DataDirectory);
		manager.addObserverHook([recipient, dataDirectory](auto& builder) {
			builder.add(observers::CreateTransferMessageObserver(0xE201735761802AFE, recipient, dataDirectory.dir("transfer_message")));
		});
	}
}}
extern "C" PLUGIN_API
void RegisterSubsystem(catapult::plugins::PluginManager& manager) {
	catapult::plugins::RegisterTransferSubsystem(manager);
}
client/catapult/plugins/txes/transfer/src/plugins/TransfervTransactionPlugin.h
#pragma once
#include "catapult/plugins.h"
#include <memory>

namespace catapult { namespace model { class TransactionPlugin; } }

namespace catapult { namespace plugins {

	/// Creates a transfer transaction plugin.
	PLUGIN_API
	std::unique_ptr<model::TransactionPlugin> CreateTransfervTransactionPlugin();
}}

TransactionTypeを追加する

今回はTransferTransactionのタイプ違いを作成するのでEntityTypeに新しいタイプを定義します.

client/catapult/plugins/txes/transfer/src/model/TransferEntityType.h
#pragma once
#ifndef CUSTOM_ENTITY_TYPE_DEFINITION
#include "catapult/model/EntityType.h"
namespace catapult { namespace model {
#endif

	/// Transfer transaction.
	DEFINE_TRANSACTION_TYPE(Transfer, Transfer, 0x1);
+ 	//add Transferv
+ 	DEFINE_TRANSACTION_TYPE(Transfer, Transferv, 0x2);

#ifndef CUSTOM_ENTITY_TYPE_DEFINITION
}}

これらはgithubにて公開しています.もしよければ参考にしてみてください.

Dockerビルドで環境構築を省力化する

これまで,PluginTransactionの検証にはcatapultをマニュアルビルドし,起動する方法を私はとっていました.しかしこれにはmongodbのセットアップなど設定や,プロセスの管理が大変です.そこでCatapultをdockerビルドし,Symbol-bootstrapで起動することで作業を簡略化します.
作業前に,symbol-bootstrapでノードを構築する場合と同じ環境(doker,docker-compose,nodejs)をインストールしておいてください.また,dockerhubのアカウントを取得してください.

Dockerビルド

こちらの手順に従います.

まず,そのままでは動かないコードがあるのでこれを修正します(2022/05/06現在,今後修正されると思います).
今回はうにやさんの情報を元にさせていただきました.本当にありがとうございます.

dockerhubにログインします

docker login

出力ファイルの設定を行います.ファイルを次のように書き換えてください

$ vi jenkins/catapult/runDockerBuild.py
- '--source-path=/catapult-src/client/catapult',
+ '--source-path=/catapult-src',
- environment_manager.copy_tree_with_symlinks(source_path / 'client/catapult' / folder_name, folder_name)
+ environment_manager.copy_tree_with_symlinks(source_path / '' / folder_name, folder_name)
- 	destination_image_name = f'symbolplatform/{destination_repository}:{destination_image_label}'
+ destination_image_name = f'(dockerhubのユーザー名)/(お好みのレポジトリの名前):{destination_image_label}'

設定が終わったら次のコマンドを実行します.

python3 jenkins/catapult/runDockerBuild.py \
    --compiler-configuration jenkins/catapult/configurations/gcc-10.yaml \
    --build-configuration jenkins/catapult/configurations/release-private.yaml \
    --operating-system ubuntu \
    --user "$(id -u):$(id -g)" \
    --source-path client/catapult \
    --destination-image-label latest

成功するとイメージが作成されます.次のコマンドで確認ができます.

docker images

これをdockerhubに登録します

docker push (dockerhubのユーザー名)/(お好みのレポジトリの名前):latest

bootstrapの設定を書き換える

bootstrapには公式の作成したcatapultのdockerのイメージが指定されており,必要に応じてこれをDL,使用するようになっています.そこでこのイメージの参照先を公式から自分の作成したものに切り替えます.npmのグローバルインストール先を探し,symbol-bootstrapの設定を書き換えます.おそらく/usr/libがグローバルインストール先として表示されると思います.

npm list -g | head -1
cd /usr/lib/node_modules/symbol-bootstrap/presets
vi shared.yml

symbolServerImageを書き換えます

/node_modules/symbol-bootstrap/presets/shared.yml
...
symbolServerImage: (dockerhubのユーザー名)/(お好みのレポジトリの名前)
...

起動する

あとはプライベートネットワークを起動するだけです.ノードを展開しても良いディレクトリで次のコマンドを実行してください.実行後,パスワードを設定すれば完了です.プリセットにはbootstrapを使用します.testnetやmainnetにした場合,PluginTransaactionのTXは承認されないままになってしまうので注意してください.

symbol-bootstrap start -d -p bootstrap

今までのようなマニュアルビルドの作業をしなくてもこれだけでdualノードが構築できてしまいます.

TXを投げる

前回作成したSDKを利用してTXを投げてみましょう.作成したノードのgenerationhashを知るためにhttp://(URL):3000/network/propertiesにアクセスし,generationhashおよびepochAdjustmentを確認します.
確認後,前回作成したSDKの/src/symbol/Network.jsを確認し次のように書き換えます

Network.TESTNET = new Network('testnet', 0x98, new Hash256('ここにgenerationhash'));

また,ネットワークのepochAdjustmentを TX送信の際に利用するようにしてください.
Tを投げてみて承認されれば成功です!

PluginTransactionを試す場合はTXを投げるための通貨が必要になると思います.Nemesisで配布された通貨が必要な場合はtarget内にaddresses.ymlがあるのでこれをsymbol-bootstarp decrypt --source addresses.yml --destination decrypt.ymlで復号すればOKです.単に送金のないTXを投げるだけであればノードのminfeemultiplierを0に変更するのもありです.

その他

  • dockerビルドしたものをdockerhubに登録することで別のユーザーがイメージを取得し,ノードを構築することも当然可能になります.(既存のネットワークを拡張する場合はseed,設定値が別途必要になります)dockerビルドでPluginTransactionを搭載したノードを公開するのは結構便利な手段になると思います.
  • 未だ私も勉強中の身ではあるのですが,PluginTransactionによって従来にはなかった取引を実現し,Catapultをより強力かつ便利なネットワークにすることが可能だと考えています.この記事だけではPluginTransactionを勉強するには不十分ですが,もしよろしければcatapultについて研究して是非PluginTransactionを開発してみてください!
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?