先までは、"愛記"についての記載で、どのようにブロックチェーンSNSに組み込んで実装していけばよいのか、概念的なところからアプローチ方法を記載していった。概念設計としてはひとまず終えた。次は、フェデレーションモデル全体の基本設計といえるところまで、基本設計書に着手できるようなところまで、概念を具体化していきたい。そして、それにつながるDApps側である「愛記システム」を、Pythonプログラムで開発していきたい。
愛の行動のPL,BSを決算書として、個人単位、市町村単位、で公表するような愛記システムというものを考えている。愛の行動のデータベースはブロックチェーンのプログラムであり、日々の愛の行動による愛貨の移動を決算書にまとめていきたい。なお、市町村のブロックチェーンのプログラムは以前にも記載している。その市町村のブロックチェーンのプログラムにつながる愛記システムを、DApps側であるPythonプログラムとして設計したい。その場合、基本設計をどのような手順で進めていけばよいか、詳しく見ていこう。
愛記システムを設計するための基本手順を以下に示す。このシステムは、Pythonを用いて市町村のブロックチェーンと連携し、個人および市町村単位での愛の行動のデータを収集、記録し、決算書(PL、BS)として公表するものである。
基本設計のステップ
- 要件定義
- アーキテクチャ設計
- データベース設計
- API設計
- ブロックチェーンインターフェース
- 決算書の生成
- フロントエンド開発
- テストとデプロイ
基本設計の各ステップを順番に進めることで、ブロックチェーンとDAppsとして繋がる「愛記システム」の詳細な設計が可能になる。各ステップでは、関係者との協議やレビューを通じて設計内容を確定していくことが重要である。
1.要件定義
まず、基本設計の最初のステップである要件定義をしていきたい。どのような機能が必要か、どのような問題を解決するのかを洗い出したい。要件定義はシステム設計の最初の重要なステップであり、システムが解決するべき問題と、必要な機能を明確に定義するプロセスである。以下に、愛記システムのプログラムに必要な機能と解決すべき問題を列挙してみよう。
機能要件
-
愛の行動の記録
-
愛貨の移動の記録
-
決算書の生成
-
個人および市町村単位でのデータの集約
-
データのブロックチェーンへの記録と取得
-
愛貨の管理
-
ユーザー管理
-
通知機能
-
レポート機能
-
ダッシュボード
非機能要件
-
セキュリティ
-
可用性
-
パフォーマンス
-
スケーラビリティ
-
ユーザビリティ
-
コンプライアンス
解決すべき問題
-
透明性と信頼性の確保
-
データの一元管理
-
愛の行動の促進
-
評価制度の確立
-
データのセキュリティとプライバシーの保護
これらの要件を基に、愛記システムの基本設計を進めていくことが重要である。次のステップでは、これらの要件を具体的なアーキテクチャ設計に反映していくことになる。まずは、要件定義の解決すべき問題を一つずつクリアにしていきたい。
透明性と信頼性の確保
1. データの完全性と一貫性の保証
-
データの完全性: すべての取引と愛の行動記録が正確かつ改ざんされていないことを保証する。
-
データの一貫性: システム全体でデータが統一されていることを確認する。
2. 取引の透明性の確保
-
公開取引データ: 取引データを公開し、誰でも確認できるようにする。
-
監査ログ: 取引や行動のすべての変更履歴を保持し、監査可能にする。
3. データのセキュリティとプライバシー保護
-
暗号化: データの送受信時および保存時に暗号化を行い、データの安全性を確保。
-
アクセス制御: データへのアクセスを制御し、必要な権限を持つユーザーのみに限定。
4. 取引の正当性の確認
-
署名の生成と検証: 取引データの署名を生成し、取引の正当性を検証。
-
取引の検証: 各取引を検証し、不正行為や二重支出を防止。
これらの項目を詳細に決定し、実装することで、愛記システムの透明性と信頼性を確保することができる。各項目については、具体的な技術要件や設計仕様を定義し、システム開発の各フェーズで反映させることが重要である。
公開取引データについて
取引データを公開し、誰でも確認できるようにするというが、公開取引データの基本設計をするにあたり、いつ、何を、どのように設計していけばいいのか、具体的にプログラムも含めて見ていこう。公開取引データの基本設計を行うにあたり、以下のステップで進める。
-
公開取引データの定義
- 公開すべき取引データの項目を定義する。例えば、取引ID、送信者、受信者、金額、タイムスタンプ、取引内容など。
- プライバシーに配慮し、公開するデータから個人情報を排除する。
-
データの公開方法
- 取引データを公開するAPIエンドポイントを設計する。
- 公開データを格納するデータベースまたはストレージを設計する。
-
データの整合性と完全性の確保
- 公開データの整合性チェックを実装する。
- 公開データが改ざんされていないことを保証するための仕組みを設計する。
-
透明性の確保
- 誰でも取引データを確認できるインターフェースを提供する。
- 取引データの照会ログを記録し、誰がいつデータを照会したかを追跡できるようにする。
取引データを公開するAPIエンドポイントの設計
APIエンドポイントの設計とは、外部システムやアプリケーションがデータを送受信するために利用するインターフェースを定義するプロセスである。具体的には、APIがどのような操作を提供するか、どのようなデータを受け取り、どのようなデータを返すかを設計することである。
・APIエンドポイントの基本的な構成要素
-
エンドポイントURL:
- APIのURLパスを定義する。
- 例: /transactions, /public_transactions
-
HTTPメソッド:
- APIがサポートする操作を定義する。
- 主なメソッド: GET, POST, PUT, DELETE
- 例:
- GET /public_transactions: 公開取引データの取得
- POST /transactions: 新しい取引の追加
-
リクエストパラメータ:
- クエリパラメータ、パスパラメータ、リクエストボディで受け取るデータを定義する。
- 例:
- POST /transactions: リクエストボディに取引データを含む
-
レスポンスフォーマット:
- APIが返すデータの形式を定義する。
- 一般的にはJSON形式で返される。
- 例:
- POST /transactions: リクエストボディ:
{
"transaction_id": "1234",
"sender": "0x1234",
"receiver": "0xabcd",
"amount": 100,
"timestamp": "2024-06-23T10:00:00Z",
"signature": "abcdef123456"
}
- GET /public_transactions のレスポンスボディ:
[
{
"transaction_id": "1234",
"sender": "0x12****34",
"receiver": "0xab****cd",
"amount": 100,
"timestamp": "2024-06-23T10:00:00Z",
"action_content": "Sample action",
"location": {
"latitude": 35.68,
"longitude": 139.69
},
"municipality": "City Name",
"signature": "e3b0c44298fc1c14...",
"proof_of_place": {
"latitude": 35.68,
"longitude": 139.69,
"timestamp": "2024-06-23T10:00:00Z"
},
"approver_info": {
"user_id": "0x12****34",
"public_key": "0x12****34",
"approval_timestamp": "2024-06-23T10:05:00Z",
"signature": "e3b0c44298fc1c14..."
},
"transaction_status": "approved",
"fee": 0.01,
"transaction_history": [],
"public_status": "public",
"currency_type": "love_currency",
"validator_info": {
"public_key": "0x12****34"
},
"fee_percentage": 5,
"notification_status": "sent",
"security_features": [],
"sender_receiver_info": {
"sender": "0x12****34",
"receiver": "0xab****cd"
},
"currency_list": ["love_currency"],
"balance_display": 1000,
"user_search": [],
"friends_list": [],
"qr_code_scan": "****abcd",
"transfer_form": {},
"transfer_history": [],
"real_time_value_info": {},
"value_chart": {},
"event_alerts": [],
"fee_change_notification": [],
"fee_details": {},
"currency_conversion": {},
"recipient_verification": {},
"user_feedback": {},
"transaction_id_display": "1234",
"progress_bar": {},
"transaction_history_link": "http://example.com/transaction/1234",
"user_support": {}
}
]
- POST /transactions: リクエストボディ:
上記の理論に基づき、DApps側であるPythonプログラムのAPIエンドポイントを設計し、実装する。
import hashlib
import datetime
import re
from pymongo import MongoClient, errors
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
client = MongoClient('mongodb://localhost:27017/')
db = client['blockchain_db']
# データ構造定義
class DataStructure:
def __init__(self):
self.transactions = db.transactions
self.users = db.users
self.approval_history = db.approval_history
data_structure = DataStructure()
# データの整合性要件
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 data['sender'] or not data['receiver']:
raise ValueError("Sender and Receiver must be specified")
if not data['timestamp']:
raise ValueError("Timestamp must be specified")
# トランザクションの匿名化
def anonymize_transaction(transaction):
"""トランザクションデータを匿名化"""
transaction['sender'] = mask_wallet_address(transaction['sender'])
transaction['receiver'] = mask_wallet_address(transaction['receiver'])
transaction['signature'] = hash_signature(transaction['signature'])
if 'proof_of_place' in transaction:
transaction['proof_of_place'] = mask_location(transaction['proof_of_place'])
if 'approver_info' in transaction:
transaction['approver_info']['public_key'] = mask_public_key(transaction['approver_info']['public_key'])
if 'transaction_history' in transaction:
transaction['transaction_history'] = anonymize_transaction_history(transaction['transaction_history'])
if 'validator_info' in transaction:
transaction['validator_info'] = mask_validator_info(transaction['validator_info'])
if 'notification_status' in transaction:
transaction['notification_status'] = anonymize_notification(transaction['notification_status'])
if 'security_features' in transaction:
transaction['security_features'] = anonymize_security(transaction['security_features'])
if 'sender_receiver_info' in transaction:
transaction['sender_receiver_info'] = anonymize_sender_receiver_info(transaction['sender_receiver_info'])
if 'user_search' in transaction:
transaction['user_search'] = anonymize_user_search(transaction['user_search'])
if 'friends_list' in transaction:
transaction['friends_list'] = anonymize_friends_list(transaction['friends_list'])
if 'qr_code_scan' in transaction:
transaction['qr_code_scan'] = mask_qr_code(transaction['qr_code_scan'])
if 'transfer_form' in transaction:
transaction['transfer_form'] = anonymize_transfer_form(transaction['transfer_form'])
if 'transfer_history' in transaction:
transaction['transfer_history'] = anonymize_transfer_history(transaction['transfer_history'])
if 'event_alerts' in transaction:
transaction['event_alerts'] = anonymize_event_alerts(transaction['event_alerts'])
if 'user_feedback' in transaction:
transaction['user_feedback'] = anonymize_user_feedback(transaction['user_feedback'])
if 'user_support' in transaction:
transaction['user_support'] = anonymize_user_support(transaction['user_support'])
return transaction
# APIエンドポイントの設計
@app.route('/transactions', methods=['POST'])
def add_transaction():
"""トランザクションを安全にデータベースに追加"""
data = request.json
try:
validate_input_data(data)
anonymized_data = anonymize_transaction(data)
with client.start_session() as session:
with session.start_transaction():
data_structure.transactions.insert_one(anonymized_data, session=session)
return jsonify({"message": "Transaction added successfully"}), 201
except (ValueError, errors.OperationFailure) as e:
return jsonify({"error": str(e)}), 400
@app.route('/public_transactions', methods=['GET'])
def get_public_transactions():
"""公開取引データを取得"""
transactions = data_structure.transactions.find()
public_transactions = [anonymize_transaction(transaction) for transaction in transactions]
return jsonify(public_transactions), 200
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
なお、APIエンドポイントの設計と、DApps側であるPythonプログラムと市町村のブロックチェーンのプログラムをつなぐAPIの設計は、関連性はあるが、目的や詳細が異なる。
・APIエンドポイントの設計
APIエンドポイントの設計は、クライアント(例:DAppsやフロントエンドアプリ)とサーバー(例:バックエンドAPI)間の通信を定義する。この設計では、どのエンドポイントがどのHTTPメソッドを使用し、どのようなデータを送受信するかを定義する。
・DAppsと市町村のブロックチェーンをつなぐAPIの設計
DApps側であるPythonプログラムと市町村のブロックチェーンをつなぐAPIの設計は、システム全体の統合を目指し、特に内部のサービス間でのデータのやり取りを定義する。これには、データの完全性と一貫性を確保するための通信プロトコルやエンドポイントが含まれる。
具体的な例:
DApps側のPythonプログラムで、市町村のブロックチェーンのプログラムと通信するAPIの設計を示す。
import hashlib
import datetime
import requests
import json
import re
# データ構造の定義
def mask_address(address):
return f"{address[:4]}****{address[-2:]}"
class Transaction:
def __init__(self, transaction_id, sender, receiver, amount, action_content, location, municipality, timestamp, signature, proof_of_place, approver_info, transaction_status, fee, currency_type):
self.transaction_id = transaction_id
self.sender = mask_address(sender)
self.receiver = mask_address(receiver)
self.amount = amount
self.action_content = action_content
self.location = location
self.municipality = municipality
self.timestamp = timestamp
self.signature = hashlib.sha256(signature.encode()).hexdigest()
self.proof_of_place = proof_of_place
self.approver_info = approver_info
self.transaction_status = transaction_status
self.fee = fee
self.currency_type = currency_type
class ProofOfPlace:
def __init__(self, latitude, longitude, timestamp):
self.latitude = round(latitude, 2) # 位置情報をぼかす
self.longitude = round(longitude, 2)
self.timestamp = timestamp
class ApproverInfo:
def __init__(self, user_id, public_key, approval_timestamp, signature):
self.user_id = user_id
self.public_key = mask_address(public_key)
self.approval_timestamp = approval_timestamp
self.signature = hashlib.sha256(signature.encode()).hexdigest()
# トランザクションデータの検証
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")
# 他のバリデーションチェックを追加
# トランザクションの生成
def create_transaction(data):
validate_input_data(data)
proof_of_place = ProofOfPlace(data['latitude'], data['longitude'], data['pop_timestamp'])
approver_info = ApproverInfo(data['approver_user_id'], data['approver_public_key'], data['approval_timestamp'], data['approver_signature'])
transaction = Transaction(
data['transaction_id'], data['sender'], data['receiver'], data['amount'],
data['action_content'], data['location'], data['municipality'], data['timestamp'],
data['signature'], proof_of_place, approver_info, data['transaction_status'],
data['fee'], data['currency_type']
)
return transaction
# トランザクションを市町村のブロックチェーンに送信
def send_transaction_to_blockchain(transaction):
url = "http://localhost:8080/api/transactions"
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(transaction.__dict__), headers=headers)
if response.status_code == 201:
print("Transaction sent successfully")
else:
print("Error sending transaction:", response.json())
# トランザクションデータの例
transaction_data = {
"transaction_id": "unique_transaction_id",
"sender": "0x1234567890abcdef",
"receiver": "0xabcdef1234567890",
"amount": 100.0,
"action_content": "Sample action content",
"location": "Sample location",
"municipality": "Sample municipality",
"timestamp": "2024-06-23T10:00:00Z",
"signature": "transaction_signature",
"latitude": 35.6895,
"longitude": 139.6917,
"pop_timestamp": "2024-06-23T10:00:00Z",
"approver_user_id": "approver_id",
"approver_public_key": "0x1234567890abcdef",
"approval_timestamp": "2024-06-23T10:05:00Z",
"approver_signature": "approver_signature",
"transaction_status": "pending",
"fee": 0.01,
"currency_type": "love_currency"
}
# トランザクションの生成と送信
transaction = create_transaction(transaction_data)
send_transaction_to_blockchain(transaction)
・市町村のブロックチェーンプログラム(Rust)側のAPIの設計を示すと下記のようになる。
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Transaction {
transaction_id: String,
sender: String,
receiver: String,
amount: f64,
timestamp: String,
signature: String,
action_content: String,
location: String,
municipality: String,
proof_of_place: ProofOfPlace,
approver_info: ApproverInfo,
transaction_status: String,
fee: f64,
currency_type: String,
}
#[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 {
transactions: Mutex<Vec<Transaction>>,
}
#[post("/api/transactions")]
async fn add_transaction(data: web::Data<AppState>, req: web::Json<Transaction>) -> impl Responder {
let mut transactions = data.transactions.lock().unwrap();
transactions.push(req.into_inner());
HttpResponse::Created().json("Transaction added")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let app_state = web::Data::new(AppState {
transactions: Mutex::new(Vec::new()),
});
HttpServer::new(move || {
App::new()
.app_data(app_state.clone())
.service(add_transaction)
})
.bind("0.0.0.0:8080")?
.run()
.await
}
・説明:
DApps側のPythonプログラム
-
データ構造と匿名化:
- トランザクションID、送信者、受信者、金額、取引内容、場所、市町村、タイムスタンプ、署名、PoP(Proof of Place)、承認者情報、取引の状態、手数料、愛貨の種類を含むトランザクションデータを定義。
- 個人を特定できる情報(送信者、受信者、公開鍵、署名など)を匿名化。
-
トランザクションの生成と送信:
- トランザクションデータの検証、生成、そして市町村のブロックチェーンに送信するロジックを実装。
市町村のブロックチェーンプログラム
- データ構造とAPIエンドポイント:
- Rustでデータ構造(トランザクション、PoP、承認者情報)を定義。
- Actix-webを使用して、トランザクションを受信し保存するエンドポイントを実装。
この設計により、DApps側のPythonプログラムと市町村のブロックチェーンのプログラム間のデータ通信が確立され、フェデレーションモデル全体の透明性と信頼性が向上する。
いかがであろうか、今回は公開取引データについて、APIエンドポイントの設計について記載した。セキュリティ面は考慮していないので、まだまだ基本設計の段階だが、考え方は理解できる。