FABRKNT
Reth Fundamentals — Alloy で動かす最初の一歩
Alloyを使う
レッスン 2 / 11·CONTENT12 分25 XP
コース
Reth Fundamentals — Alloy で動かす最初の一歩
レッスンの役割
CONTENT
順序
2 / 11

レッスン2 — Alloyの基本型と署名

問い

Alloy で Ethereum を扱うときの最初の道具立て — アドレス / U256 / B256 / 署名。型システムが「これは Address、これは uint256」と区別してくれるので、誤って混ぜることがない。本レッスンで Alloy 基本型を見て、Signer で鍵を作る。

原理(最小モデル)

  • 3 primitive 型. Address(20 バイト、コントラクト or EOA)/ U256(256 ビット符号なし整数、wei 金額)/ B256(32 バイト、ハッシュやスロットキー)。
  • .parse::<Address>(). 文字列 → Address、checksum 検証込み、不正なら Err
  • U256 リテラル. U256::from(1_000_000)(u64 から)/ "1000000".parse()?(文字列から)/ parse_ether("1")?(ETH 単位)。
  • Signer トレイト. 鍵を持って署名を作る抽象、PrivateKeySigner::random() で新規鍵、.address() で公開アドレス取得。
  • Ledger / AWS KMS 等もSigner で抽象化. 後の Inside Alloy で詳細、ここでは「Signer = 署名できる何か」で十分。

具体例 + ステップで組み立てる

Alloyの基本型と署名

最初に Alloy を直接触る。Alloyは「EthereumをRustで扱うためのライブラリ群」で、Rethも内部でこれを活用している。

1. プロジェクトの準備

cargo new hello_alloy
cd hello_alloy

Cargo.toml[dependencies] に追記する。

[dependencies]
alloy = { version = "1.0", features = ["full"] }
tokio = { version = "1", features = ["full"] }
eyre = "0.6"

Tip: バージョンは執筆時点のもの。実際は最新版を crates.io で確認する。eyre は読みやすいエラー出力向けのクレートである。

2. メッセージに署名する — 実例

これは alloy-rs/examplessign_message.rs 全体:

//! Example of signing a message with a signer.

use alloy::signers::{local::PrivateKeySigner, Signer};
use eyre::Result;

#[tokio::main]
async fn main() -> Result<()> {
    // ランダムなシグナーを作る
    let signer = PrivateKeySigner::random();

    // 任意でチェーンIDを設定(EIP-155 リプレイ攻撃防止)
    let signer = signer.with_chain_id(Some(1337));

    // 署名するメッセージ
    let message = b"hello";

    // 非同期に署名
    let signature = signer.sign_message(message).await?;

    println!("Signature produced by {}: {:?}", signer.address(), signature);
    println!("Signature recovered address: {}", signature.recover_address_from_msg(&message[..])?);

    Ok(())
}

src/main.rs にコピーして cargo run する。ランダム生成シグナーのアドレス、署名、検証で復元したアドレス(一致するはず)が表示される。

sequenceDiagram
    participant Signer as PrivateKeySigner
    participant Msg as メッセージバイト
    participant Hash as EIP-191 ハッシュ
    participant Sig as 署名
    participant Verify as recover_address_from_msg

    Signer->>Msg: "hello" を取得
    Msg->>Hash: prefix + keccak256
    Hash->>Sig: sign(privkey, hash)
    Sig-->>Verify: 署名 + 元メッセージ
    Verify->>Hash: prefix付きで再ハッシュ
    Verify-->>Signer: 復元したアドレス

3. このコードが教えること

PrivateKeySigner::random()

セキュアRNGで新しい鍵ペア生成。本番資金には絶対に使わないこと — テストと学習用。本番では環境変数、暗号化キーストア、ハードウェアウォレットから読み込む。

with_chain_id(Some(1337))

EIP-155 はチェーンIDを署名に含めるため、チェーンAで署名したtxをチェーンBへ再送できなくする。本番では必須1337 はローカルAnvilの典型的チェーンIDである。

sign_message(message).await

EIP-191 ("Ethereum signed message" プレフィックス)を実装。JSON-RPCの personal_signwindow.ethereum.request("personal_sign", ...) が返すものと同じ。async なのは、ハードウェアウォレット(Ledger/Trezor)が応答に時間がかかるため — ローカルシグナーも同じインターフェースで差し替え可能。

signature.recover_address_from_msg(&message[..])

検証側。署名と元メッセージから、署名者のアドレスを復元。これが 「Sign in with Ethereum」の作り方 — サーバーがnonceを発行、ユーザーが署名、サーバーがアドレスを復元。パスワード不要。

4. address! マクロ

use alloy::primitives::address;

let recipient = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045");

address!手続きマクロ で、コンパイル時に走る。1桁タイポや長さミスは コンパイル時に失敗 し、送信時の実行時エラーにしない。Expertティアで内部実装を正確に扱う。

なぜ「型」が重要なのか

Solidityも address 型を持ちますが、Rustの型システムはより厳格:

  • U256 を期待する関数に u64 を渡すとコンパイル時に止まる
  • Address[u8; 20] 互換ではなく、専用の型
  • AddressB256 を混同するとコンパイルエラー
  • これが「金融資産を扱うコードの安全性」につながります

練習

例を改造して:

  1. 同じメッセージ異なる2つのチェーンID で署名 — 署名は異なるはず
  2. recover_address_from_msg改ざんされたメッセージ に対して呼ぶ — 復元アドレスが一致しない。それがEIP-191の改ざん耐性

次は ResultOption? — Provider に触れる前に押さえるべきエラーハンドリングの語彙。

まとめ(3行)

  • 3 primitive = Address 20 バイト / U256 256 ビット / B256 32 バイト、型システムが「これは Address、これは wei」を区別。
  • .parse() で文字列 → 型、U256::from() で数値 → U256、parse_ether("1") で ETH 単位、お金は f64 でなく U256。
  • Signer = 署名できる何か、PrivateKeySigner::random() で新規鍵、.address() で公開アドレス、次は Result/Option/?。