さぁ、独自ノード立てるぞ!
って事で何が必要でどうやって作るか聞いてみた!
以下では「独自ノードを作るために必要な機能リストアップ→フォルダ/ファイル構成→具体的な構築手順」を、プログラミング初心者でもできるだけ理解できるよう、できるだけ丁寧に解説します。
最終的に「ゲーム開発に必要な独自ノード」を Substrate リポジトリをベースに用意する方針です。
1. 独自ノードに必要な機能リスト
このゲームは「スマートコントラクト機能(pallet-contracts)を使って、ブロックチェーン上でアイテム/NFTをやりとりする」想定です。
したがって、最低限、次の機能・パレットが必要となります。
1. pallet-balances
• 通貨バランスを管理するパレット。
• ゲーム内でCTトークンや手数料を扱う場合に必須。
2. pallet-contracts
• ink! スマートコントラクトを呼び出すためのパレット。
• ゲームロジックやNFT管理等をインクコントラクトで実装するなら必須。
3. pallet-timestamp
• ブロックごとの時刻管理に使われるパレット。
• 多くの場合、ContractsやBalancesとあわせて必須。
4. pallet-transaction-payment
• トランザクション手数料の計算に必要。
• コントラクト呼び出しのGas計算にも影響。
5. (オプション) pallet-randomness-collective-flip
• ランダム要素が必要な場合に使用。
• ゲームの抽選やランダムイベント時に役立つ。
2. フォルダ/ファイル構成
Substrate 公式リポジトリをクローンしている前提で、独自ノードを新たに作る場合、以下のような構成を推奨します。
my_bevy_game/ ← ゲームプロジェクトのルートディレクトリ(任意の名前)
┣ node/ ← ノード起動周り (chain_spec.rs, service.rs 等)
┃ ┣ src/
┃ ┃ ┣ chain_spec.rs
┃ ┃ ┣ service.rs
┃ ┃ ┗ main.rs ← 実行エントリポイント
┃ ┣ Cargo.toml ← node 用のCargo設定
┃ ┗ README.md (任意)
┣ runtime/
┃ ┣ src/
┃ ┃ ┗ lib.rs ← Runtimeのメインロジック (pallet-contracts等のConfig)
┃ ┣ Cargo.toml ← runtime 用のCargo設定
┃ ┗ README.md (任意)
┣ .gitignore (必要なら)
┣ README.md (必要なら)
┗ ほかリソースファイル
さらに、Substrate リポジトリをどこに置くかには2パターンがあります:
1. Substrateリポジトリとは別フォルダ
• substrate/ は ~/substrate などに配置し、それを“ライブラリ・参考用”として扱う。
• my_bevy_game/ は独立したフォルダとして上記構成にする。
• 必要な Substrate のクレートは、Cargo.toml で git = "https://github.com/paritytech/substrate.git" 参照などによって取得。
2. Substrateリポジトリ内に置く
• substrate/ の下に my_bevy_game/ というサブフォルダを作り、その中に node/ runtime/ を用意する。
• ただしこちらは Substrate本体のアップデートと競合しやすいので、あまり推奨はされません。
多くのチュートリアルやプロジェクトは最初のパターン(Substrateはグローバルなライブラリ扱い、実際のプロジェクトは別フォルダ)をとることが多いです。
3. 具体的な構築方法
ここでは「Substrateリポジトリを参考にしつつ、独自ノード用のフォルダを用意する」方法を示します。
「プログラミング知識ゼロ」という視点で、どこで何を使い、何をするかを詳しく書きます。
3-1. 下準備
1. Substrateリポジトリをすでにクローンしている → ~/substrateなどに配置
2. Rustツールチェーン, cargo, wasmターゲット, cargo-contract などがセットアップ済み
3. Docker / Node.js / Polkadot.js Extension などは後々使うためにインストール済みでOK
3-2. プロジェクトフォルダの作成
# WSLターミナルなどを開いてホームディレクトリへ移動
cd ~
# ゲームプロジェクト用のフォルダを作成
mkdir my_bevy_game
cd my_bevy_game
# 以下、node/ と runtime/ フォルダを手動で作る:
mkdir node
mkdir runtime
cd node
mkdir src
cd ../runtime
mkdir src
# 必要なら README.md と .gitignore を作る
ディレクトリ階層は、前述のように最終的にはこうなります:
my_bevy_game/
┣ node/
┃ ┗ src/
┗ runtime/
┗ src/
3-3. Node(起動周り)のセットアップ
(A) Cargo.toml(node用)
• my_bevy_game/node/Cargo.toml を作ります。最低限下記のような構成:
[package]
name = "my-node"
version = "0.1.0"
edition = "2021"
[dependencies]
# Substrate関連の依存を指定
# 例: 公式のノードテンプレートと同じバージョンに合わせる
sp-core = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.xx" }
sp-runtime = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.xx" }
# ...
# runtimeとの連携を行うには別途手順(後述)
[features]
default = ["std"]
std = []
• 実際には公式 Node Template の Cargo.toml をコピーし、不要な部分を削除するのが一番楽です。
• バージョン番号は、Substrateリポジトリ内のCargo.tomlに合わせてください。(polkadot-v0.9.xx など)
(B) ソースファイル: main.rs, service.rs, chain_spec.rs など
• これらはSubstrate公式の Node Template や substrate/bin/node/ を参考にコピー&修正するのが普通です。
• メインの流れは、「main.rsで CLIのパース → service.rsが起動処理を担当 → chain_spec.rsでDev用/Local用チェーンスペックを定義」という構成です。
典型例: my_bevy_game/node/src/main.rs
fn main() -> sc_cli::Result<()> {
// SubstrateのCLIを初期化する処理
// → Node Templateやbin/nodeのmainを参考にコピペし、名称やパスだけ変える
my_node::command::run()
}
3-4. Runtime(ブロックチェーンロジック)のセットアップ
(A) Cargo.toml(runtime用)
• my_bevy_game/runtime/Cargo.toml で、pallet-contractsなど必要パレットを依存に追加。
[package]
name = "my-runtime"
version = "0.1.0"
edition = "2021"
[dependencies]
frame-system = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.xx" }
pallet-balances = { git = "...", branch = "..." }
pallet-contracts = { git = "...", branch = "..." }
pallet-timestamp = { git = "...", branch = "..." }
pallet-transaction-payment = { git = "...", branch = "..." }
# ほかに使いたいpalletを追加
sp-runtime = { git = "..." }
sp-core = { git = "..." }
sp-std = { git = "...", default-features = false }
[features]
default = ["std"]
std = [
"frame-system/std",
"pallet-balances/std",
"pallet-contracts/std",
"pallet-timestamp/std",
"pallet-transaction-payment/std",
# ほか
]
ここで branch = "polkadot-v0.9.xx" の部分は、あなたの Substrate リポジトリのバージョンに合わせてください。
(B) lib.rs にパレットConfig & construct_runtime!
• my_bevy_game/runtime/src/lib.rs が、あなたのチェーンの中心。
• ここに、pallet-contractsのConfig実装などを書きます。
#![cfg_attr(not(feature = "std"), no_std)]
use sp_runtime::traits::BlakeTwo256;
use frame_support::{
construct_runtime, parameter_types,
traits::{KeyOwnerProofSystem, Randomness},
weights::Weight,
};
use pallet_contracts::{weights::SubstrateWeight};
pub type Balance = u128; // 例: 通貨の型
pub type BlockNumber = u32;
pub type Signature = sp_core::sr25519::Signature;
parameter_types! {
pub const BlockHashCount: BlockNumber = 2400;
// コントラクトに関する定数
pub const DepositPerByte: Balance = 0;
pub const DepositPerItem: Balance = 0;
// ...
}
// ---------- System Config ----------
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type AccountId = sp_core::sr25519::Public; // or AccountId32
type Call = Call;
type Lookup = frame_system::IdentityLookup<Self::AccountId>;
type Index = u64;
type BlockNumber = BlockNumber;
type Hash = sp_core::H256;
type Hashing = BlakeTwo256;
type Header = sp_runtime::generic::Header<BlockNumber, BlakeTwo256>;
type Event = Event;
// ...
}
// ---------- Balances Config ----------
impl pallet_balances::Config for Runtime {
type MaxLocks = ();
type Balance = Balance;
type Event = Event;
// ...
}
// ---------- Contracts Config ----------
impl pallet_contracts::Config for Runtime {
type Currency = Balances; // バランスパレット
type Time = Timestamp; // タイムスタンプ
type Randomness = RandomnessCollectiveFlip; // 必要なら
type Event = Event;
type Call = Call;
type CallStack = [pallet_contracts::Frame<Self>; 31];
type WeightPrice = pallet_transaction_payment::Pallet<Self>;
type WeightInfo = SubstrateWeight<Self>;
type DepositPerByte = DepositPerByte;
type DepositPerItem = DepositPerItem;
// ...
}
// ---------- Timestamp Config ----------
impl pallet_timestamp::Config for Runtime {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = 5; // 例: 5s/2 = 2.5s
type WeightInfo = ();
}
// ---------- Transaction Payment Config ----------
impl pallet_transaction_payment::Config for Runtime {
type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, ()>;
type WeightToFee = (); // 単純に全部0にしたいなら ()
type FeeMultiplierUpdate = ();
}
// ---------- construct_runtime! ----------
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system,
Balances: pallet_balances,
Contracts: pallet_contracts,
Timestamp: pallet_timestamp,
TransactionPayment: pallet_transaction_payment,
RandomnessCollectiveFlip: pallet_randomness_collective_flip,
}
);
• これで pallet-contracts を利用できるRuntime が定義されます。
• コンパイルには他にも多くの型定義が必要ですが、Node Templateを参考にコピー&調整するのが一般的です。
3-5. プロジェクト全体のビルド&起動
1. Runtimeのビルド
cd ~/my_bevy_game/runtime
cargo build --release
• 成功すれば target/release/ 以下に Wasm バイナリや依存クレートが生成されます。
• ここでエラーが出たら Cargo.toml のバージョンや Config 実装を再確認しましょう。
2. ノードのビルド
cd ~/my_bevy_game/node
cargo build --release
• こちらも target/release/my-node(などのファイル名)ができればOK。
3. 起動してみる
./target/release/my-node --dev
• ブロック生成ログが流れ出し、「Starting consensus…」などの表示があれば起動成功。
4. Polkadot.js Appsで接続確認
• ブラウザで polkadot.js.org/apps
• 上部ネットワーク設定で Local Node (ws://127.0.0.1:9944) を選択
• 接続できればブロックが増えていく
• 左メニューに「Developer → Contracts」があれば pallet-contracts が有効になっている証拠
3-6. ink! コントラクトとの連携 (簡易チェック)
1. cargo-contractインストール (事前済みなら不要)
cargo install cargo-contract --force
2. 簡単な flipper コントラクトをビルド
cd ~
cargo contract new flipper
cd flipper
cargo contract build
3. Polkadot.js Appsで「Add New Code」→ .contract ファイルをアップロード
4. Instantiate(インスタンス生成)して flip() / get() メソッドを呼び出し
• 動作すれば、あなたの独自ノード上でインクコントラクトが利用できることを確認できます。
まとめ
1. 必要な機能リスト:
• pallet-balances, pallet-contracts, pallet-timestamp, pallet-transaction-payment など
2. プロジェクト構成:
• node/ と runtime/ に分け、Node Templateを参考にしたフォルダ&ファイルを用意
3. 手順 (超ざっくり)
1. runtime/lib.rs で pallet-contracts::Config を含む各パレットConfig実装
2. construct_runtime! マクロに追加
3. ノード側(node/src)で起動ロジック (main.rs, service.rs, chain_spec.rs) を設定
4. ビルド → --dev で起動 → Polkadot.js Apps 接続 → Contractsタブが使えればOK
4. プログラミング初心者が成功するコツ:
• Node Template や 公式Substrateの bin/node をコピーして始める
• エラーが出るたび、ChatGPTにエラーメッセージを貼り付けて質問
• バージョンミスマッチに注意。branch = "polkadot-v0.9.xx" の部分を全て揃える
こうして「独自ノード」が完成すれば、あとは「ink! でゲームロジック(NFTや装備、生産など)を実装 → デプロイ → フロントエンドから呼び出す」というフローでゲーム開発が進められます。ゆっくり着実にステップを踏んでみてください。
これ見ててあれ?
これしっかり目に設計せんとあかんやつやんってなっていろいろ相談した。
で、ステージ管理者=ノード運営にする案で進めようと相談したところインセンティブ設計が重要とのことなのでこのゲームにおいてのインセンティブ設計をしてもらったのがこちら
以下は「ステージ管理者がノード運営者としても活動する」ことを前提にしたインセンティブ設計の一案です。ゲーム設計・トークン経済・開発体制などにより変わりますが、1つの例としてご参考ください。プログラミング初心者の方でもイメージしやすいよう、なるべく分かりやすく説明します。
前提
• ステージ管理者:
• マップNFTを保有し、ステージ(ダンジョンなど)の構築・運営を行う。
• 参加費を設定し、クリア報酬を提供。
• プレイヤーからステージ参加費を回収し、一部を収益として受け取る。
• ノード運営者(バリデータ/オーソリティ):
• ブロックチェーンのブロック生成・取引検証を行う。
• ネットワークの安定性・セキュリティを支える。
• 本案では、「ステージ管理者=ノード運営者」という形を取り、1人2役を担う。
• トークン構造:
1. 通貨トークン(CT)
• ステージ参加費、素材・アイテム取引、装備製作コストなどに用いられるメイン通貨。
2. ガバナンストークン(GT)
• 投票・ガバナンス・信頼度算定に影響するトークン。
3. 報酬トークン(本提案の中で新設するか、CTで賄うか検討)
• ノード運営のインセンティブ用に別途発行するか、既存トークン(CT/GT)で賄うかを設計。
1. 基本コンセプト
• 「ステージ運営」×「ノード運営」=ゲームの根幹を支える重要役割
• ステージ管理がなければ遊ぶダンジョンが作られず、ノード運営がなければブロックチェーンが動かない。
• これらを1人(または1組織)が担うため、それに見合う十分なインセンティブを与えて、継続運営を促す。
• インセンティブの2本立て
1. ステージ参加費や収益: 従来どおりステージ管理者が回収する。
2. チェーン運営報酬: ノード運営報酬をブロック報酬や手数料収益から得られる。
2. 具体案
2-1. ブロック報酬
• ブロック報酬:ブロックが生成されるたびに、一定量のトークンが新規発行されて「ブロックを生成したノード運営者」に支払われる仕組み。
• 通常のPoS(Proof of Stake)チェーンでは、ステーキングに応じてブロック報酬が割り当てられますが、本ゲームの場合、ステージ管理者が一定のガバナンス権限を持ち、ノードに割り当てられると考えます。
実装例
1. CTでのブロック報酬
• CTをインフレ発行して、1ブロックあたり少量をノード運営者へ付与。
• インフレ率が上がりすぎると通貨価値に影響するので、投票で報酬率を調整できる仕組みを設ける。
2. GTでのブロック報酬
• ガバナンストークンをブロック報酬として配布。
• ゲームガバナンスに積極的に関わってもらいやすくなる。
• ただしGTの発行上限やバーンの有無など、設計が難しい面がある。
利点: ノード運営者がブロックを生成するごとに安定した収益を得られるため、維持コスト(サーバー代など)をまかなえる。
注意: インフレ率を過度に上げるとCT/GTの希薄化が起こる。バランス調整が必要。
2-2. トランザクション手数料の分配
• 取引手数料: 各トランザクション(ステージ参加費支払い、アイテム売買、装備強化など)がチェーン上で行われる際、微量の手数料(CTまたは別トークン)が発生。
• 分配方法: 発生した手数料を下記のように分割するイメージ
1. ノード運営者: 50%
2. ステージ管理者(同一人物の場合は合計になる): 20%
3. 開発チーム/開発者プール: 20%
4. ガバナンス予備金: 10%
• こうしたルールをRuntime側、またはスマートコントラクトで定義して、自動的に分配させる。
利点: ブロック報酬とは別に「ゲームが活発になればなるほどトランザクションが増え、ノード運営者が安定したインカムを得られる」。
注意: 手数料が高すぎるとユーザーの負担が大きくなりすぎるため、適度な料率が必要。
2-3. ステージ参加費との連携
• ステージ参加費の一部を「ノード運営報酬」としてプールする方法もあります。
• たとえば「プレイヤーがステージ参加に 100 CT を支払う → うち10 CTをノード運営プールへ」
• 実際にはトランザクション手数料に含まれるように設計すれば運用が簡単。
利点: ステージ管理者が単に全額を得るのではなく、ゲーム全体を支えるブロックチェーン運営にも貢献している、という意識づけになる。
注意: ステージ管理者=ノード運営者であっても、仕組み上、参加費が分配される流れを明示しておくことで透明性が高まる。
2-4. 信頼度(TrustScore)と報酬倍率
• このゲームには信頼度(または管理者信頼度)という概念がある想定です。
• ノード運営報酬に「信頼度連動のボーナス」を設定するのもアリです:
1. ベース報酬: ブロック報酬 or 手数料分配
2. 信頼度ボーナス: 信頼度が高いほど報酬が1.1倍~2倍になるなど
• こうすることで、ステージの品質・公平性・ガバナンス投票への積極参加などを促せます。
2-5. 罰則(ペナルティ)の設計
• ノードを頻繁に停止してしまったり、悪意ある行動(不正ブロック生成)をした場合などのために、ペナルティメカニズムを用意すると良いです。
• たとえば、稼働率が一定以下だと報酬ボーナスを失う/ガバナンストークンが没収される。
• 不正検知を行ったコミュニティからのレポートでペナルティを課す仕組みも検討。
3. この仕組みのメリット・デメリット
メリット
1. ステージ管理者に強いモチベーション
• ステージ運営収益 + ノード運営収益が合わさり、安定的なインカムが得られる。
• ゲームを盛り上げようと頑張るほど手数料やブロック報酬が増える。
2. ゲーム全体の安定稼働
• 多くのステージ管理者が自発的にノードを立てれば、ブロックチェーンの分散度・安定性が上がる。
3. 信頼度システムとの連動
• ステージ品質やガバナンス参加により信頼度が高い管理者にはより高い報酬を与えられる。
• ゲーム内の公平性・健全性を維持するインセンティブとなる。
デメリット / 注意点
1. 技術的ハードル
• ステージ管理者がノードを運営するにはサーバー設定やセキュリティ知識が必要。
• 運営コストがかさむため、十分な報酬設計をしないと参加してもらえない可能性がある。
2. インフレ率・手数料設定のバランス
• 報酬が過剰だとトークン価値が下がり、プレイヤーの負担が増える。
• 報酬が低すぎるとノードが少なくなり、ネットワークが脆弱になる。
3. 複雑化
• ステージ管理者にノード運営を必須にすると、ゲームの参加ハードルが高くなりすぎるかもしれない。
• ある程度、別ロール(ノード専門運営者)も認める選択肢があってもよい。
4. まとめ:インセンティブ案の一例
1. ブロック報酬をCTまたはGTで付与
2. トランザクション手数料分配 → ノード運営者(ステージ管理者)へ一定割合
3. ステージ参加費の一部をノード報酬プールに回す
4. 信頼度に応じて報酬ボーナス (1.0~2.0倍など)
5. ペナルティメカニズム:停止・不正行為・低品質運営で報酬減
この組み合わせにより、
• 「ゲームを盛り上げるステージ運営」をすればするほど、トランザクションが増えて手数料・ブロック報酬が増える → 運営者も嬉しい
• プレイヤーから見れば、しっかり運用されているステージ・ノードほど信頼度が高く、ゲーム全体も安全
• ガバナンスで報酬率やインフレ率を調整しながら、トークン価値が大きく崩れないようにする
という流れが自然に生まれます。
もちろん、実際のゲーム規模や経済状況に合わせたチューニング(手数料率やインフレ率の設定)は必須ですが、「ステージ管理者 = ノード運営者」に報酬を集中させることで、ゲームの核となる運営体が強力に支えられる設計になるでしょう。
なんだかいい感じな気がするよ!
この設定を元にもう少し煮詰めてから作り始めよう!