2020/09/09
[ADA]cardano-serialization-libをPythonから呼べるようにしました
概要
ADAの決済やdepositのシステムを構築するのに、HDWalletを自作する必要が出てきたので、対応しました。(現在も改良中です)
Emurgo/cardano-serialization-libはcardanoのwalletの実装に使えるjavascript向けのライブラリで、
コア部分がrustで実装されています。
Expressなどのjavascriptベースのシステムであれば、全然こちらを利用するのが良いのですが・・
私は、backendのサーバーはpythonを普段利用しているので、このライブラリを改良してpythonで利用できるようにしようと思い、改造しました。
本家をforkして、kumanote/cardano-serialization-libに作成しています。
以下のような関数を作ろうかなと考えています。
- mnemonicからaddressを生成する(いったんEnterpriseのAddressだけで良いかなと思っています)
- 生成したaddressにあるUTXOを使って、Transactionを作成して署名する
実装
基本的には、本家のソースコードの踏襲なので、rust側のコードで主に変更があるのは、以下です。
- Javascript向けのコードは無くし、
- Pythonから呼べるようにする
本記事では、PythonからRustのコードを呼ぶ部分を記載しておきます。
Pyo3の組み込み
Cargo.toml
- crate-typeに
cdylib
を指定しておきます - pyo3を依存関係に追加します
[lib]
name = "cardano_serialization_lib_py"
crate-type = ["cdylib", "rlib"]
[dependencies.pyo3]
version = "0.11.1"
features = ["extension-module"]
- pymoduleを定義します。(python側はこの名前でimportできるようになります)
- pyfnを定義します
- PyResultを返却することに注意
#[pymodule]
fn cardano_serialization_lib(py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "generate_bip32_enterprise_address")]
pub fn generate_bip32_enterprise_address_py(
_py: Python, phrase: String, password: String, network: u8, account: u32, chains: u32, index: u32
) -> PyResult<String> {
let out = generate_bip32_enterprise_address(phrase, password, network, account, chains, index);
Ok(out)
}
Ok(())
}
fn generate_bip32_enterprise_address(phrase: String, password: String, network: u8, account: u32, chains: u32, index: u32) -> String {
let mnemonic = Mnemonic::from_phrase(phrase.as_str(), Language::English).unwrap();
let entropy = mnemonic.entropy();
let root_key = Bip32PrivateKey::from_bip39_entropy(&entropy, password.as_bytes());
let spend = root_key
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(account))
.derive(chains)
.derive(index)
.to_public();
let spend_raw_key: PublicKey = spend.into();
let spend_cred = StakeCredential::from_keyhash(&spend_raw_key.hash());
let address = EnterpriseAddress::new(network, &spend_cred).to_address();
address.to_bech32(None)
}
Build
私はMacで開発しているので、以下のコマンドでビルドしました
% cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup
Deploy
ビルドしてできた dylib
ファイルにシンボリックリンクを作成しておきます。
この時に pymodule
でつけた名前と同じファイル名.soという名前でシンボリックリンクを作成する必要があります。
(シンボリックリンクではなく、単純にファイルをコピーしてリネームしてもOKです。)
$ ln -s `pwd`/rust/target/release/libcardano_serialization_lib_py.dylib ./cardano_serialization_lib.so
Run
シンボリックリンクを作成した同一ディレクトリで python
を実行します。
import cardano_serialization_lib
phrase = "art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy"
password = ""
network = 0
account = 0
chains = 0
index = 0
address = cardano_serialization_lib.generate_bip32_enterprise_address(phrase, password, network, account, chains, index)
print(address)
addr_test1vpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg57c2qv
以上です。
関連する記事
Concordiumノードをローカルで動かしてみた
Concordiumの調査のために、ローカルでソースコードをビルドしてノードを動かしてみました
[Rust]axumとdragonflyを使ったWebsocket Chatのサンプル実装
redis互換のdragonflyをPUBSUBとして利用して、Websocket Chatアプリのサンプル実装を行いました。
[Rust]TiDBを使ったサンプルアプリケーションの実装
RustからTiDBを使ったアプリケーションの実装を行いました。
[Rust]Google Cloud Storageを利用する
GCSやNFSのファイルを扱えるpackageをRustで実装しました。