6
3

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 19

Rustを使ってSymbolブロックチェーンのトランザクションを送信する

Last updated at Posted at 2022-05-23

この記事は「自分の得意なプログラミング言語でSymbolブロックチェーンを動かす方法」をRustで実践したものです。
なお私自身はRustを勉強して2,3日程度ですので、拙い書き方の点ご了承ください。

依存ライブラリ

[dependencies]
ed25519-dalek = "1"
hex = "0.4.3"
rand = "0.7.3"
base32 = "0.4.0"
ureq = "*"
sha3 = "0.10.1"
chrono = "*"

extern と use

extern crate ed25519_dalek;
extern crate hex;
extern crate sha3;
extern crate rand;
extern crate base32;
extern crate ureq;
extern crate chrono;

use chrono::{DateTime, Local};
use ed25519_dalek::*;
use hex::FromHex;
use sha3::{Digest,Sha3_256};
use rand::rngs::OsRng;

アカウント作成

let mut csprng = OsRng{};
let keypair: Keypair = Keypair::generate(&mut csprng);
println!("Secret key: {}",hex::encode(keypair.secret.as_bytes()));
println!("Public key: {}",hex::encode(keypair.public.as_bytes()));

アカウント復元

let secret_key: &[u8] = b"94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c7";
//let public_key: &[u8] = b"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb";
let sec_bytes: Vec<u8> = FromHex::from_hex(secret_key).unwrap();

let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
let public: PublicKey = (&secret).into();
let keypair: Keypair  = Keypair{ secret: secret, public: public };

println!("Secret key: {}",hex::encode(keypair.secret.as_bytes()));
println!("Public key: {}",hex::encode(keypair.public.as_bytes()));

トランザクション構築

let dt: DateTime<Local> = Local::now();
let timestamp: i64 = dt.timestamp();
let deadline_time = ((timestamp  + 7200) - 1637848847) * 1000;

let reserved1 = "00000000";
//let signature = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
let signer = hex::encode(keypair.public.as_bytes());
let reserved2 = "00000000";
let v: u8 = 1;
let version = v.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
let nt: u8 = 152;
let network_type = nt.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
let tt: u16 = 16724;
let tx_type = tt.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
let deadline = deadline_time.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
let f: u64 = 300000000;
let fee = f.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

let ba = "TBIL6D6RURP45YQRWV6Q7YVWIIPLQGLZQFHWFEQ";
let recipient_address = hex::encode(base32::decode(base32::Alphabet::RFC4648{padding:true}  ,ba).unwrap());

let mc: u8 = 1;
let mosaic_count = mc.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
let tx_reserved = "0000000000";

let mi: u64 = 0x3A8416DB2D53B6C8;
let mosaic_id = mi.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

let ma: u64 = 100;
let mosaic_amount = ma.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

let m = "Hello Rust! Welcome to Symbol world!";
let message = "00".to_owned() + &m.as_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

let ms: u16 =  (message.chars().count() / 2).try_into().unwrap();
let message_size = ms.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

トランザクション署名

let verifiable_data = version.to_owned()
	+ &network_type
	+ &tx_type
	+ &fee
	+ &deadline
	+ &recipient_address
	+ &message_size
	+ &mosaic_count
	+ &tx_reserved
	+ &mosaic_id
	+ &mosaic_amount
	+ &message;

println!("verifiable_data: {}", verifiable_data);

let data = "7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836".to_owned()
	+ &verifiable_data;

let msg_bytes: Vec<u8> = FromHex::from_hex(data).unwrap();
let signature: Signature = keypair.sign(&msg_bytes);

println!("Signature: {}",signature.to_string());

トランザクションの通知

ノードにトランザクションを通知します。

let ts:u32  =  (verifiable_data.chars().count() / 2 + 108).try_into().unwrap();
let tx_size = ts.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

let tx_buffer = tx_size.to_owned()
	+ &reserved1
	+ &signature.to_string()
	+ &signer
	+ &reserved2
	+ &verifiable_data;

let json_request = format!(r#"{{"payload":"{}"}}"#, tx_buffer);
let r = ureq::put("https://sym-test-02.opening-line.jp:3001/transactions")
    .set("Content-Type", "application/json")
    .send_string(&json_request);
println!("{}", json_request);
println!("{:?}", r);

確認

トランザクションのハッシュ値を出力してノードに照会します。


let hash_payload = signature.to_string().to_owned()
	+ &signer
	+ &"7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836"
	+ &verifiable_data;
let hash_bytes: Vec<u8> = FromHex::from_hex(hash_payload).unwrap();
let mut hasher = Sha3_256::new();
hasher.update(hash_bytes);
let tx_hash = hasher.finalize().iter().map(|n| format!("{:02X}", n)).collect::<String>();

println!("transactionStatus: https://sym-test-02.opening-line.jp:3001/transactionStatus/{}", tx_hash);
println!("confirmed: https://sym-test-02.opening-line.jp:3001/transactions/confirmed/{}", tx_hash);
println!("explorer: https://testnet.symbol.fyi/transactions/{}", tx_hash);

複雑なトランザクション送信についてはまた後日に。

全ソースコード

extern crate ed25519_dalek;
extern crate hex;
extern crate sha3;
extern crate rand;
extern crate base32;
extern crate ureq;
extern crate chrono;

use chrono::{DateTime, Local};
use ed25519_dalek::*;
use hex::FromHex;
use sha3::{Digest,Sha3_256};
use rand::rngs::OsRng;


fn main() {
let mut csprng = OsRng{};
	let keypair: Keypair = Keypair::generate(&mut csprng);
	println!("Secret key: {}",hex::encode(keypair.secret.as_bytes()));
	println!("Public key: {}",hex::encode(keypair.public.as_bytes()));

	let secret_key: &[u8] = b"94ee0f4d7fe388ac4b04a6a6ae2ba969617879b83616e4d25710d688a89d80c7";
	//let public_key: &[u8] = b"5f594dfc018578662e0b5a2f5f83ecfb1cda2b32e29ff1d9b2c5e7325c4cf7cb";
	let sec_bytes: Vec<u8> = FromHex::from_hex(secret_key).unwrap();

	let secret: SecretKey = SecretKey::from_bytes(&sec_bytes[..SECRET_KEY_LENGTH]).unwrap();
	let public: PublicKey = (&secret).into();
	let keypair: Keypair  = Keypair{ secret: secret, public: public };

	println!("Secret key: {}",hex::encode(keypair.secret.as_bytes()));
	println!("Public key: {}",hex::encode(keypair.public.as_bytes()));

	let dt: DateTime<Local> = Local::now();
	let timestamp: i64 = dt.timestamp();
	let deadline_time = ((timestamp  + 7200) - 1637848847) * 1000;

	let reserved1 = "00000000";
	//let signature = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
	let signer = hex::encode(keypair.public.as_bytes());
	let reserved2 = "00000000";
	let v: u8 = 1;
	let version = v.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
	let nt: u8 = 152;
	let network_type = nt.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
	let tt: u16 = 16724;
	let tx_type = tt.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
	let deadline = deadline_time.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
	let f: u64 = 300000000;
	let fee = f.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	let ba = "TBIL6D6RURP45YQRWV6Q7YVWIIPLQGLZQFHWFEQ";
	let recipient_address = hex::encode(base32::decode(base32::Alphabet::RFC4648{padding:true}  ,ba).unwrap());

	let mc: u8 = 1;
	let mosaic_count = mc.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();
	let tx_reserved = "0000000000";

	let mi: u64 = 0x3A8416DB2D53B6C8;
	let mosaic_id = mi.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	let ma: u64 = 100;
	let mosaic_amount = ma.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	let m = "Hello Rust! Welcome to Symbol world!";
	let message = "00".to_owned() + &m.as_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	let ms: u16 =  (message.chars().count() / 2).try_into().unwrap();
	let message_size = ms.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	let verifiable_data = version.to_owned()
		+ &network_type
		+ &tx_type
		+ &fee
		+ &deadline
		+ &recipient_address
		+ &message_size
		+ &mosaic_count
		+ &tx_reserved
		+ &mosaic_id
		+ &mosaic_amount
		+ &message;

	println!("verifiable_data: {}", verifiable_data);

	let data = "7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836".to_owned()
		+ &verifiable_data;

	let msg_bytes: Vec<u8> = FromHex::from_hex(data).unwrap();
	let signature: Signature = keypair.sign(&msg_bytes);

	println!("Signature: {}",signature.to_string());

	let ts:u32  =  (verifiable_data.chars().count() / 2 + 108).try_into().unwrap();
	let tx_size = ts.to_le_bytes().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	let tx_buffer = tx_size.to_owned()
		+ &reserved1
		+ &signature.to_string()
		+ &signer
		+ &reserved2
		+ &verifiable_data;



	let json_request = format!(r#"{{"payload":"{}"}}"#, tx_buffer);
	let r = ureq::put("https://sym-test-02.opening-line.jp:3001/transactions")
	    .set("Content-Type", "application/json")
	    .send_string(&json_request);
	println!("{}", json_request);
	println!("{:?}", r);


	let hash_payload = signature.to_string().to_owned()
		+ &signer
		+ &"7fccd304802016bebbcd342a332f91ff1f3bb5e902988b352697be245f48e836"
		+ &verifiable_data;
	let hash_bytes: Vec<u8> = FromHex::from_hex(hash_payload).unwrap();
	let mut hasher = Sha3_256::new();
	hasher.update(hash_bytes);
	let tx_hash = hasher.finalize().iter().map(|n| format!("{:02X}", n)).collect::<String>();

	println!("transactionStatus: https://sym-test-02.opening-line.jp:3001/transactionStatus/{}", tx_hash);
	println!("confirmed: https://sym-test-02.opening-line.jp:3001/transactions/confirmed/{}", tx_hash);
	println!("explorer: https://testnet.symbol.fyi/transactions/{}", tx_hash);
}

6
3
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
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?