この記事は株式会社Gincoのテックブログとして書いています。
この記事では、Astar NetworkのEVM (Ethereum Virtual Machine) とSubstrateとの互換性について紹介します。弊社プロダクトのGinco Enterprise WalletでもAstarを取り扱っています。
Astar Networkの概要
Astar Network(旧名Plasm Network)は、Polkadotエコシステムの一部であり、スケーラブルなマルチチェーンDApps(分散型アプリケーション)を開発・実行するためのプラットフォームです。AstarはSubstrateというブロックチェーンを作成するフレームワークを利用して構築されており、そのため、Polkadotと簡単に統合することが可能です。
また、EVMおよびWebAssembly(Wasm)の両環境をサポートしています。(公式サイト)
つまり、Astar Network上ではSolidityで書かれたスマートコントラクトの実行や、Rustなどの言語からコンパイルされたWebAssemblyで書かれたコントラクトが実行出来ます。
EVMとSubstrateの互換性について
Substrateではpalletという、言わばプラグインのようなものを入れる事により、機能を追加出来る仕様があります。
AstarではParity Technologies社が開発しているpallet-evmというpalletによりEVMとSubstrate間で資金を移動させることが出来ます。(ソースコード)
イメージとしては、Substrateの中に独立したEVMサンドボックスが存在しているような感じです。
アーキテクチャ図
英語ですが下記のAstar CTOの記事がとても分かりやすかったので紹介します。
https://medium.com/astar-network/using-astar-network-account-between-substrate-and-evm-656643df22a0
画像を見ると、Astarのアカウントは3種類あります。
- Nativeアカウント: Substrateベースのアカウント(ウォレット例: Polkadot.js)
- EVMデポジット: Substrateベースのアカウントから作成した受け取り用のEVMアカウント
- EVMアカウント: EVMベースのアカウント(ウォレット例: MetaMask)
(アカウントが3種類というと語弊がありますがここでは便宜上EVM Depositもアカウントとして扱います。)
ユースケースとしては下記の2パターンを想定して解説します。
- 資金をSubstrate → EVMで移動させる場合
- 資金をEVM → Substrateで移動させる場合
まずは、SubstrateベースのNativeアカウントとEVMベースのEVMアカウントをそれぞれPolkadot.jsとMetaMaskで作成します。
Nativeアカウントアドレス | EVMアカウントアドレス | |
Substrateベース | WSEDrP14YB15dDt8QcMqnA1KwoHwfvGpFbsFCVQxdwXTocW | |
EVMベース | 0xC374C947C506E051EA81d3DD572862fE7F49B358 |
1. 資金をSubstrate → EVMで移動させる場合
まずは資金をSubstrate → EVMで移動させる場合について考えます。
NativeアカウントアドレスからEVMアカウントアドレスへ送金するには下記の手順を踏む必要があります。
- 受け取り側のEVMアカウントアドレスからNativeアカウントアドレスを導出する。
- 導出したNativeアカウントアドレスに対し送金する。
Nativeアカウントアドレス | EVMアカウントアドレス | |
Substrateベース | WSEDrP14YB15dDt8QcMqnA1KwoHwfvGpFbsFCVQxdwXTocW | |
EVMベース | YqPg3Y5yVnDsZmPsaFDzZp8tjvNh5gHrza6ETijUyA1nQkd | 0xC374C947C506E051EA81d3DD572862fE7F49B358 |
ここで、Go言語実装を見ながらEVM → Substrateの導出方法について触れます。
// EVMAddressToSubstrateAddress
func EVMAddressToSubstrateAddress(address string) (string, error) {
if strings.HasPrefix(address, "0x") {
address = strings.ToLower(fmt.Sprintf("%s", address[2:])) // ① "0x"を取り除く
}
addressByte, err := hex.DecodeString(address) // ② byte形式にする
if err != nil {
return "", err
}
var raw []byte
prefix := []byte("evm:") // ③ "evm:" prefixを付けaddressに連結する
raw = append(prefix, addressByte...)
if len(raw) != 24 {
xerrors.Errorf("Invalid length: %d", raw)
}
h := blake2b.Sum256(raw) // ④ ハッシュ化する
return EncodeAstarAddress(h[:], false), nil
}
- "0x"を取り除く
- byte形式にする
- "evm:" prefixを付けaddressに連結する
- ハッシュ化する
この4ステップでEVMアドレスからSubstrateアドレスを導出することが出来ます。
ここで注目して欲しい点としては、アドレスの導出には秘密鍵を使用していないということです。
導出したSubstrateアドレスと元のEVMアドレスの残高は共有されるので、Substrateアドレスに対し、入金が起こると即時にEVMアドレスの残高にも反映されます。
また、前項で Substrateの中に独立したEVMサンドボックスが存在している
と記載しましたが、下記の仕様があります。
- 親であるSubstrate側のExplorerでは子であるEVMの取引履歴が確認できる(※)
- 子であるEVM側のExplorerでは親であるSubstrate側の取引履歴が確認出来ない
※ EVMの取引履歴は導出したSubstrateアドレス同士の取引履歴として記録されている
:::message 💡 まとめ
- 実際に起っている取引はWSEDrP14YB15dDt8QcMqnA1KwoHwfvGpFbsFCVQxdwXTocWからYqPg3Y5yVnDsZmPsaFDzZp8tjvNh5gHrza6ETijUyA1nQkdへの送金取引
- 0xC374C947C506E051EA81d3DD572862fE7F49B358とYqPg3Y5yVnDsZmPsaFDzZp8tjvNh5gHrza6ETijUyA1nQkdは残高を共有している
- EVMの取引履歴だけを追うと、0xC374C947C506E051EA81d3DD572862fE7F49B358取引履歴が無いが急に残高が増えたように見える(Substrate側には取引履歴がある)
- EVM側で作ったSubstrateのアドレスは入庫用で出庫はできない(秘密鍵がないため)
:::
2. 資金をEVM → Substrateで移動させる場合 (こっちが複雑)
次に資金をEVM → Substrateで移動させる場合です。
EVMアカウントアドレスからNativeアカウントアドレスへ送金するには下記の手順を踏む必要があります。
- 受け取り側のNativeアカウントアドレスからEVMデポジットアドレスを導出する。
Nativeアカウントアドレス | EVMデポジットアドレス | EVMアカウントアドレス | |
Substrateベース | WSEDrP14YB15dDt8QcMqnA1KwoHwfvGpFbsFCVQxdwXTocW | 0x161069bcb00759baaca58188c84ccf5f55cd787c | - |
EVMベース | YqPg3Y5yVnDsZmPsaFDzZp8tjvNh5gHrza6ETijUyA1nQkd | 0xC374C947C506E051EA81d3DD572862fE7F49B358 |
- 導出したEVMデポジットアドレスに対し送金する。
- NativeアカウントアドレスからEVMデポジットアドレスの残高を引き出すメソッドを呼び出す(pallet-evmのEvm.withdrawメソッド)
再びGo言語実装を確認します。
// EVMデポジットアドレスの生成
// SubstrateAddressToEVMDepositAddress
func SubstrateAddressToEVMDepositAddress(address string) (string, error) {
pubKey, _, err := DecodeAddress(address)
if err != nil {
return "", err
}
return fmt.Sprintf("0x%x", pubKey[0:20]), nil // ① pubkeyの前半20byteを抽出
}
- pubkeyの前半20byteを抽出
こちらはシンプルです。公開鍵の前半部分を抽出しているのみになります。
また、EVM → Substrateの場合と同様で秘密鍵は使用していないので出金はできません。
EVM → Substrateの場合と異なる点としては、導出したEVMデポジットアドレスと元のSubstrateアドレスは残高を共有していないという点です。
EVMデポジットアドレスに入金が発生した後、Evm.withdrawする必要があります。
なので、取引所でEvm.withdrawの対応がされていない場合、外部のEVMウォレットから取引所のNativeアカウントアドレスに入庫すると残高が引き出せない可能性があります。
Evm.withdrawの対応がされているかの確認をするか、EVMアカウントはEVMアカウントに入庫するようにしましょう。
:::message 💡 まとめ
Evm.withdraw
をしないと元のSubstrateのアドレスの残高は増えない- 資金の流れとしては、0x161069bcb00759baaca58188c84ccf5f55cd787cを更にSubstrateアドレスに変換したもの(Y6XEJRmafPJuEhspm9gtZoq2UuCNvKCeurVgd8KjZrJ9PJF)からWSEDrP14YB15dDt8QcMqnA1KwoHwfvGpFbsFCVQxdwXTocW
- Substrate側で作ったEVMのデポジットアドレスは入庫用で出庫はできない(秘密鍵がないため)
:::
まとめ
今回は、Astar NetworkのEVMとSubstrateの互換性について紹介しました。
Astar NetworkではEVMとSubstrateを利用出来るので便利な反面、きちんと仕様を理解をしないと、残高が引き出せないことがあります。当記事で少しでも理解の助けになれたら幸いです。
株式会社 Ginco ではブロックチェーンを学びたい方、ウォレットについて詳しくなりたい方を募集していますので下記リンクから是非ご応募ください。
参考文献
https://github.com/paritytech/frontier
https://medium.com/astar-network/using-astar-network-account-between-substrate-and-evm-656643df22a0