先までは、"愛記"についての記載で、どのようにブロックチェーンSNSに組み込んで実装していけばよいのか、概念的なところからアプローチ方法を記載していった。概念設計としてはひとまず終えた。次は、フェデレーションモデル全体の基本設計といえるところまで、基本設計書に着手できるようなところまで、概念を具体化していきたい。そして、それにつながるDApps側である「愛記システム」を、Pythonプログラムで開発していきたい。
愛の行動のPL,BSを決算書として、個人単位、市町村単位、で公表するような愛記システムというものを考えている。愛の行動のデータベースはブロックチェーンのプログラムであり、日々の愛の行動による愛貨の移動を決算書にまとめていきたい。なお、市町村のブロックチェーンのプログラムは以前にも記載している。その市町村のブロックチェーンのプログラムにつながる愛記システムを、DApps側であるPythonプログラムとして設計したい。その場合、基本設計をどのような手順で進めていけばよいか、詳しく見ていこう。
愛記システムを設計するための基本手順を以下に示す。このシステムは、Pythonを用いて市町村のブロックチェーンと連携し、個人および市町村単位での愛の行動のデータを収集、記録し、決算書(PL、BS)として公表するものである。
基本設計のステップ
- 要件定義
- アーキテクチャ設計
- データベース設計
- API設計
- ブロックチェーンインターフェース
- 決算書の生成
- フロントエンド開発
- テストとデプロイ
基本設計の各ステップを順番に進めることで、ブロックチェーンとDAppsとして繋がる「愛記システム」の詳細な設計が可能になる。各ステップでは、関係者との協議やレビューを通じて設計内容を確定していくことが重要である。
1.要件定義
まず、基本設計の最初のステップである要件定義をしていきたい。どのような機能が必要か、どのような問題を解決するのかを洗い出したい。要件定義はシステム設計の最初の重要なステップであり、システムが解決するべき問題と、必要な機能を明確に定義するプロセスである。以下に、愛記システムのプログラムに必要な機能と解決すべき問題を列挙してみよう。
機能要件
-
愛の行動の記録
-
愛貨の移動の記録
-
決算書の生成
-
個人および市町村単位でのデータの集約
-
データのブロックチェーンへの記録と取得
-
愛貨の管理
-
ユーザー管理
-
通知機能
-
レポート機能
-
ダッシュボード
非機能要件
-
セキュリティ
-
可用性
-
パフォーマンス
-
スケーラビリティ
-
ユーザビリティ
-
コンプライアンス
解決すべき問題
-
透明性と信頼性の確保
-
データの一元管理
-
愛の行動の促進
-
評価制度の確立
-
データのセキュリティとプライバシーの保護
これらの要件を基に、愛記システムの基本設計を進めていくことが重要である。次のステップでは、これらの要件を具体的なアーキテクチャ設計に反映していくことになる。まずは、要件定義の解決すべき問題を一つずつクリアにしていきたい。
透明性と信頼性の確保
1. データの完全性と一貫性の保証
-
データの完全性: すべての取引と愛の行動記録が正確かつ改ざんされていないことを保証する。
-
データの一貫性: システム全体でデータが統一されていることを確認する。
2. 取引の透明性の確保
-
公開取引データ: 取引データを公開し、誰でも確認できるようにする。
-
監査ログ: 取引や行動のすべての変更履歴を保持し、監査可能にする。
3. データのセキュリティとプライバシー保護
-
暗号化: データの送受信時および保存時に暗号化を行い、データの安全性を確保。
-
アクセス制御: データへのアクセスを制御し、必要な権限を持つユーザーのみに限定。
4. 取引の正当性の確認
-
署名の生成と検証: 取引データの署名を生成し、取引の正当性を検証。
-
取引の検証: 各取引を検証し、不正行為や二重支出を防止。
これらの項目を詳細に決定し、実装することで、愛記システムの透明性と信頼性を確保することができる。各項目については、具体的な技術要件や設計仕様を定義し、システム開発の各フェーズで反映させることが重要である。
データの一貫性について
1. データの一貫性
システム全体でデータが統一されていることを確認するために、次の点に注意する。
- トランザクションのアトミック性: トランザクションが完全に成功するか、まったく成功しないかを保証する。
- データの整合性チェック: データが一貫性を保つための整合性チェックを設計する。
- データの同期: 分散システム間でデータの同期を適切に行う。
2. データの完全性
データが損なわれないようにするために、次の点に注意する。
- データの暗号化: データの機密性を確保するために、保存時および通信時にデータを暗号化する。
- データのバリデーション: 入力データをバリデーションして、不正なデータがシステムに入らないようにする。
3. 監査ログ
すべての操作を監査ログに記録し、後から検証可能にする。では、以下に、データの完全性と一貫性を確保するための具体的なプログラム例を示す。
・データのバリデーション:
フェデレーションモデルにおいて、データのバリデーションとは、何をどのように、どんなタイミングでどのフェーズでおこなうのか、詳しく見ていこう。以下に、データのバリデーションを行う具体的なポイントと、そのタイミングを示す。
・データバリデーションのフェーズとタイミング
-
データ入力フェーズ(DApps側)
- ユーザー入力データのバリデーション: ユーザーからの入力データを受け取る際に、そのデータが正しい形式であることを確認する。(音声入力時も含む)
- タイミング: ユーザーがデータを入力し送信ボタンを押す直前。
-
トランザクション生成フェーズ(DApps側)
- トランザクションデータのバリデーション: トランザクションを生成する際に、そのデータが正しい形式であり、一貫性があることを確認する。
- タイミング: トランザクションが生成され、ブロックチェーンに送信される前。
-
ブロックチェーン受信フェーズ(市町村ブロックチェーン)
- トランザクションの検証: ブロックチェーンがトランザクションを受信した際に、そのデータが正しい形式であることを確認する。
- タイミング: トランザクションがブロックチェーンに追加される前。
-
ブロック生成フェーズ(市町村ブロックチェーン)
- データの整合性チェック: ブロック生成時に、そのブロック内のデータが一貫していることを確認する。
- タイミング: ブロックが生成され、チェーンに追加される前。
-
メインチェーンへの同期フェーズ
- データの再バリデーション: 市町村ブロックチェーンがメインチェーンにデータを同期する際に、そのデータが正しい形式であり、一貫性があることを再確認する。
- タイミング: 市町村ブロックチェーンからメインチェーンにデータが送信される前。
・プログラム例
以下に、DApps側のPythonプログラムと市町村ブロックチェーンのRustプログラムのバリデーションロジックを示す。
DApps側のPythonプログラム:
import re
import hashlib
import datetime
from pymongo import MongoClient, errors
import speech_recognition as sr
# MongoDBのクライアント設定
client = MongoClient('mongodb://localhost:27017/')
db = client['blockchain_db']
def convert_speech_to_text(audio_file):
"""音声ファイルをテキストに変換"""
recognizer = sr.Recognizer()
try:
with sr.AudioFile(audio_file) as source:
audio = recognizer.record(source)
text = recognizer.recognize_google(audio)
return text
except sr.UnknownValueError:
raise ValueError("Could not understand audio")
except sr.RequestError as e:
raise ValueError(f"Could not request results from Google Speech Recognition service; {e}")
def validate_input_data(data):
"""ユーザー入力データのバリデーション"""
if not re.match(r'^[a-zA-Z0-9]+$', data['transaction_id']):
raise ValueError("Invalid Transaction ID")
if not isinstance(data['amount'], (int, float)) or data['amount'] <= 0:
raise ValueError("Invalid Amount")
if not re.match(r'^[a-zA-Z0-9]+$', data['public_key']):
raise ValueError("Invalid Public Key")
if not re.match(r'^[a-zA-Z0-9]+$', data['signature']):
raise ValueError("Invalid Signature")
def verify_signature(transaction_data, public_key, signature):
"""署名を検証する疑似関数"""
return hashlib.sha256(str(transaction_data + public_key).encode()).hexdigest() == signature
def check_transaction_consistency(transaction):
"""トランザクションの整合性をチェックする"""
if not verify_signature(transaction['data'], transaction['public_key'], transaction['signature']):
raise ValueError("Invalid Signature")
if transaction['timestamp'] > datetime.datetime.now().timestamp():
raise ValueError("Future timestamp")
def add_transaction(transaction):
"""トランザクションを安全にデータベースに追加する"""
try:
with client.start_session() as session:
with session.start_transaction():
check_transaction_consistency(transaction)
try:
db.transactions.insert_one(transaction, session=session)
print("Transaction added successfully.")
except errors.OperationFailure as e:
print(f"Transaction failed: {e}")
raise
except ValueError as e:
print(f"Error adding transaction: {e}")
# メイン関数
def main():
audio_file = "path_to_audio_file.wav" # 音声ファイルのパスを指定
try:
# 音声をテキストに変換
text_data = convert_speech_to_text(audio_file)
print(f"Recognized Text: {text_data}")
# データのバリデーション
input_data = {
'transaction_id': text_data, # ここでは認識されたテキストをトランザクションIDとして使用
'amount': 100.0, # サンプルデータ
'public_key': 'user123publickey', # サンプルデータ
'signature': 'validsignature123', # サンプルデータ
'timestamp': datetime.datetime.now().timestamp(),
'data': text_data # サンプルデータとして認識されたテキストを使用
}
validate_input_data(input_data)
add_transaction(input_data)
except ValueError as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
・説明:
-
音声入力から文字に変換する関数 (convert_speech_to_text)
- 音声ファイルを読み込み、GoogleのSpeech Recognition APIを使って文字に変換する。
- 変換できない場合やエラーが発生した場合には適切なエラーメッセージを返す。
-
データのバリデーション関数 (validate_input_data)
- トランザクションIDが正しい形式かどうか、愛貨額が正の数値かどうか、公開鍵や署名が正しい形式かどうかなど、基本的なバリデーションチェックを行う。
- これにより、不正なデータがシステムに入ることを防ぐ。
-
トランザクションの整合性を確認する関数 (check_transaction_consistency)
- 署名の検証やタイムスタンプの確認を行う。
-
トランザクションを安全にデータベースに追加する関数 (add_transaction)
- トランザクションデータを検証し、MongoDBのトランザクションを使用してデータの一貫性を保ちつつデータベースに追加する。
- 例外が発生した場合、エラーメッセージを表示する。
-
メイン関数 (
main
)- 音声ファイルを指定して文字に変換し、変換されたテキストデータを使用してトランザクションデータを生成し、バリデーションを行った後、データベースに追加する。
このプログラムは、音声入力をテキストに変換し、そのテキストデータをバリデーションした上で、トランザクションとしてデータベースに追加する流れを示している。
・市町村ブロックチェーンのRustプログラム
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use pqcrypto_ntru::ntruhps4096821::*;
use serde::{Deserialize, Serialize};
use chrono::Utc;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
// データ構造定義
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Polygon {
coordinates: Vec<(f64, f64)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Municipality {
name: String,
polygon: Polygon,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Transaction {
transaction_id: String,
sender: String,
receiver: String,
amount: f64,
action_content: String,
location: String,
municipality: String,
timestamp: String,
signature: String,
proof_of_place: Option<ProofOfPlace>,
approver_info: Option<ApproverInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ProofOfPlace {
latitude: f64,
longitude: f64,
timestamp: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ApproverInfo {
user_id: String,
public_key: String,
approval_timestamp: String,
signature: String,
}
struct AppState {
pending_transactions: Mutex<HashMap<String, Transaction>>,
balances: Mutex<HashMap<String, f64>>,
municipalities: Mutex<HashMap<String, Municipality>>,
blocks: Mutex<Vec<Block>>, // ブロックチェーンを保持するためのベクター
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Block {
index: u64,
timestamp: String,
transactions: Vec<Transaction>,
previous_hash: String,
hash: String,
}
// トランザクション受信
#[post("/transactions")]
async fn receive_transaction(data: web::Data<AppState>, req: web::Json<Transaction>) -> impl Responder {
let transaction = req.into_inner();
let mut pending_transactions = data.pending_transactions.lock().unwrap();
// トランザクションの検証フェーズ
if !verify_transaction(&transaction) {
return HttpResponse::BadRequest().json("Invalid transaction");
}
pending_transactions.insert(transaction.transaction_id.clone(), transaction);
HttpResponse::Ok().json("Transaction received")
}
// 承認処理
#[post("/approve")]
async fn approve_transaction(data: web::Data<AppState>, req: web::Json<(String, ApproverInfo)>) -> impl Responder {
let (transaction_id, approver_info) = req.into_inner();
let mut pending_transactions = data.pending_transactions.lock().unwrap();
let mut balances = data.balances.lock().unwrap();
if let Some(mut transaction) = pending_transactions.remove(&transaction_id) {
if let Some(proof_of_place) = &transaction.proof_of_place {
if verify_proof_of_place(&data, proof_of_place) {
let amount = transaction.amount;
*balances.entry(transaction.sender.clone()).or_insert(0.0) -= amount;
*balances.entry(transaction.receiver.clone()).or_insert(0.0) += amount;
transaction.approver_info = Some(approver_info);
// 承認時のPoPとPoHを記録
transaction.proof_of_place = Some(proof_of_place.clone());
// ブロック生成フェーズ
if !add_transaction_to_block(data.clone(), transaction.clone()) {
return HttpResponse::InternalServerError().json("Failed to add transaction to block");
}
HttpResponse::Ok().json("Transaction approved")
} else {
HttpResponse::BadRequest().json("Invalid Proof of Place")
}
} else {
HttpResponse::BadRequest().json("Proof of Place not found")
}
} else {
HttpResponse::BadRequest().json("Transaction not found")
}
}
// PoP検証
fn verify_proof_of_place(data: &web::Data<AppState>, pop: &ProofOfPlace) -> bool {
let municipalities = data.municipalities.lock().unwrap();
for municipality in municipalities.values() {
if is_inside_polygon(&municipality.polygon, pop.latitude, pop.longitude) {
return true;
}
}
false
}
// ポリゴン内確認
fn is_inside_polygon(polygon: &Polygon, latitude: f64, longitude: f64) -> bool {
let mut is_inside = false;
let mut j = polygon.coordinates.len() - 1;
for i in 0..polygon.coordinates.len() {
let (xi, yi) = polygon.coordinates[i];
let (xj, yj) = polygon.coordinates[j];
let intersect = ((yi > longitude) != (yj > longitude))
&& (latitude < (xj - xi) * (longitude - yi) / (yj - yi) + xi);
if intersect {
is_inside = !is_inside;
}
j = i;
}
is_inside
}
// トランザクションの検証
fn verify_transaction(transaction: &Transaction) -> bool {
// トランザクションID、金額、署名の検証など
if transaction.transaction_id.is_empty() || transaction.amount <= 0.0 || transaction.signature.is_empty() {
return false;
}
true
}
// ブロック生成フェーズ
fn add_transaction_to_block(data: web::Data<AppState>, transaction: Transaction) -> bool {
let mut blocks = data.blocks.lock().unwrap();
let previous_block = blocks.last().cloned();
let new_block = Block {
index: previous_block.map_or(0, |b| b.index + 1),
timestamp: Utc::now().to_string(),
transactions: vec![transaction],
previous_hash: previous_block.map_or(String::new(), |b| b.hash),
hash: String::new(), // 一旦空で作成し、後でハッシュを計算する
};
if !verify_block(&new_block) {
return false;
}
// ハッシュの計算
let block_hash = calculate_block_hash(&new_block);
let mut final_block = new_block.clone();
final_block.hash = block_hash;
blocks.push(final_block);
true
}
// ブロックの検証
fn verify_block(block: &Block) -> bool {
// データの整合性チェックなど
!block.transactions.is_empty() && !block.timestamp.is_empty()
}
// ブロックのハッシュを計算
fn calculate_block_hash(block: &Block) -> String {
let block_data = format!("{}{}{:?}{}", block.index, block.timestamp, block.transactions, block.previous_hash);
let mut hasher = Sha256::new();
hasher.update(block_data.as_bytes());
let result = hasher.finalize();
format!("{:x}", result)
}
// メインチェーンへの同期フェーズ
fn sync_to_main_chain(data: web::Data<AppState>) -> bool {
let blocks = data.blocks.lock().unwrap();
for block in blocks.iter() {
if !verify_block(block) {
return false;
}
// メインチェーンにデータを送信する処理を追加
}
true
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let app_state = web::Data::new(AppState {
pending_transactions: Mutex::new(HashMap::new()),
balances: Mutex::new(HashMap::new()),
municipalities: Mutex::new(HashMap::new()),
blocks: Mutex::new(Vec::new()),
});
HttpServer::new(move || {
App::new()
.app_data(app_state.clone())
.service(receive_transaction)
.service(approve_transaction)
})
.bind("0.0.0.0:8080")?
.run()
.await
}
・説明:
-
トランザクションの検証フェーズ
- トランザクション受信時に、そのデータが正しい形式であるかを確認する。
- 検証に失敗した場合、エラーレスポンスを返す。
-
ブロック生成フェーズ
- 承認されたトランザクションをブロックに追加する際に、ブロック内のデータの整合性を確認する。
- ブロックのハッシュを計算して追加する。
-
メインチェーンへの同期フェーズ
- 市町村のブロックチェーンからメインチェーンにデータを同期する前に、ブロックの整合性を再確認する。
- このフェーズでは、実際にメインチェーンにデータを送信する処理を追加する。
このプログラムは、市町村のブロックチェーンでのトランザクションの受信、検証、承認、ブロック生成、そしてメインチェーンへの同期を行うための基本的な構造を提供します。これにより、システム全体でデータの一貫性と整合性を確保し、不正なデータがシステムに入るのを防ぐ。
いかがであろうか、これでデータのバリデーションについて記載した。ここは結構重要なフェーズだ。しっかりとチェックをかけていくことが後々の正確な処理に繋がっていくのだから。