愛記システムの基本設計:DApps側である愛記システム 要件定義について⑦ | 続・ティール組織 研究会のブログ

続・ティール組織 研究会のブログ

ティール組織が話題になっているが、具現化するにはどうしたらよいか?
その研究を続けるにあたり、さらに次の形態である、続・ティール組織なるものまで視野に入れ、具体的な施策・行動内容を研究・支援する会。

先までは、"愛記"についての記載で、どのようにブロックチェーンSNSに組み込んで実装していけばよいのか、概念的なところからアプローチ方法を記載していった。概念設計としてはひとまず終えた。次は、フェデレーションモデル全体の基本設計といえるところまで、基本設計書に着手できるようなところまで、概念を具体化していきたい。そして、それにつながるDApps側である「愛記システム」を、Pythonプログラムで開発していきたい。

 

愛の行動のPL,BSを決算書として、個人単位、市町村単位、で公表するような愛記システムというものを考えている。愛の行動のデータベースはブロックチェーンのプログラムであり、日々の愛の行動による愛貨の移動を決算書にまとめていきたい。なお、市町村のブロックチェーンのプログラムは以前にも記載している。その市町村のブロックチェーンのプログラムにつながる愛記システムを、DApps側であるPythonプログラムとして設計したい。その場合、基本設計をどのような手順で進めていけばよいか、詳しく見ていこう。

 

愛記システムを設計するための基本手順を以下に示す。このシステムは、Pythonを用いて市町村のブロックチェーンと連携し、個人および市町村単位での愛の行動のデータを収集、記録し、決算書(PL、BS)として公表するものである。

基本設計のステップ

  1. 要件定義
  2. アーキテクチャ設計
  3. データベース設計
  4. API設計
  5. ブロックチェーンインターフェース
  6. 決算書の生成
  7. フロントエンド開発
  8. テストとデプロイ

基本設計の各ステップを順番に進めることで、ブロックチェーンとDAppsとして繋がる「愛記システム」の詳細な設計が可能になる。各ステップでは、関係者との協議やレビューを通じて設計内容を確定していくことが重要である。

1.要件定義

まず、基本設計の最初のステップである要件定義をしていきたい。どのような機能が必要か、どのような問題を解決するのかを洗い出したい。要件定義はシステム設計の最初の重要なステップであり、システムが解決するべき問題と、必要な機能を明確に定義するプロセスである。以下に、愛記システムのプログラムに必要な機能と解決すべき問題を列挙してみよう。

機能要件

  1. 愛の行動の記録

  2. 愛貨の移動の記録

  3. 決算書の生成

  4. 個人および市町村単位でのデータの集約

  5. データのブロックチェーンへの記録と取得

  6. 愛貨の管理

  7. ユーザー管理

  8. 通知機能

  9. レポート機能

  10. ダッシュボード

非機能要件

  1. セキュリティ

  2. 可用性

  3. パフォーマンス

  4. スケーラビリティ

  5. ユーザビリティ

  6. コンプライアンス

解決すべき問題

  1. 透明性と信頼性の確保

  2. データの一元管理

  3. 愛の行動の促進

  4. 評価制度の確率

  5. データのセキュリティとプライバシーの保護

これらの要件を基に、愛記システムの基本設計を進めていくことが重要である。次のステップでは、これらの要件を具体的なアーキテクチャ設計に反映していくことになる。まずは、要件定義の解決すべき問題を一つずつクリアにしていきたい。

透明性と信頼性の確保

愛貨の取引と愛の行動の記録を透明にし、信頼性を確保することが重要だ。以下に、具体的な項目とその決定プロセスを記載する。

・透明性と信頼性の確保に必要な項目と決定プロセス

1. データの完全性と一貫性の保証

  • データの完全性すべての取引と愛の行動記録が正確かつ改ざんされていないことを保証する。

  • データの一貫性: システム全体でデータが統一されていることを確認する。

2. 取引の透明性の確保

  • 公開取引データ: 取引データを公開し、誰でも確認できるようにする。

  • 監査ログ: 取引や行動のすべての変更履歴を保持し、監査可能にする。

3. データのセキュリティとプライバシー保護

  • 暗号化: データの送受信時および保存時に暗号化を行い、データの安全性を確保。

  • アクセス制御: データへのアクセスを制御し、必要な権限を持つユーザーのみに限定。

4. 取引の正当性の確認

  • 署名の生成と検証: 取引データの署名を生成し、取引の正当性を検証。

  • 取引の検証: 各取引を検証し、不正行為や二重支出を防止。

これらの項目を詳細に決定し、実装することで、愛記システムの透明性と信頼性を確保することができる。各項目については、具体的な技術要件や設計仕様を定義し、システム開発の各フェーズで反映させることが重要である。

 

・データの完全性について

まずはデータの完全性についてから順番に見ていこう。

項目: 取引ID、日時、送信者、受信者、愛貨の量、取引の内容、署名、ハッシュ値というが、いままで概念設計で記載してきた愛記システム、愛貨のやりとり、愛記の決算書を振り返ってみて、取引データは、取引ID、日時、送信者、受信者、愛貨の量、取引の内容、署名、ハッシュ値だけでいいのか、もっとたくさんあるのではと思ったので、今一度、取引データを検証してみる。前回の続きを記載する。

・取引データの完全性を保証するための詳細な項目

  1. 取引ID (Transaction ID)

  2. 日時 (Timestamp)

  3. 送信者 (Sender)

  4. 受信者 (Receiver)

  5. 愛貨の量 (Amount of Love Tokens)

  6. 取引の内容 (Transaction Content)

  7. 署名 (Signature)

  8. ハッシュ値 (Hash Value)

  9. 取引ステータス (Transaction Status)

  10. 取引のカテゴリ (Transaction Category)

  11. 証拠データ (Proof Data)

  12. 受信日時 (Received Timestamp

  13. ジオロケーションデータ (Geolocation Data

  14. 関連取引ID (Related Transaction ID

  15. メタデータ (Metadata

  16. ゼロ知識証明 (Zero-Knowledge Proof)

    • 取引の正当性を第三者に証明するためのゼロ知識証明データ。
    • 例: zk-SNARKsやzk-STARKsの証明データ。

      ゼロ知識証明 (Zero-Knowledge Proof, ZKP) を取引に組み込むための設計とプログラム例を示す。ここでは、zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge) を使った例を考える。

      ・基本設計のステップ

    • zk-SNARKs ライブラリの選定と準備:

      • Rust の zk-SNARKs ライブラリを選定する。例えば bellman ライブラリ。
      • 必要な依存関係を設定する。
    • 証明生成と検証のロジック:

      • zk-SNARKs 証明を生成するためのロジックを実装する。
      • zk-SNARKs 証明を検証するためのロジックを実装する。
    • トランザクションクラスの拡張:

      • zk-SNARKs 証明データをトランザクションに含めるためのフィールドを追加する。
    • トランザクション処理時の zk-SNARKs 証明生成と検証:

      • トランザクション生成時に zk-SNARKs 証明を生成し、トランザクションに含める。
      • トランザクション承認時に zk-SNARKs 証明を検証する。
    • プログラム例

      以下に、DApps側で「zk-SNARKs」を使用したトランザクションシステムのPythonプログラム例を示す。
      import requests
      import hashlib
      import json
      from datetime import datetime
      from py_ecc import bn128
      from py_ecc.bn128 import pairing, FQ, FQ2, G1, G2, Z1, Z2, is_on_curve, normalize
      from py_ecc.bn128.bn128_pairing import final_exponentiate

      # トランザションクラス
      class Transaction:
          def __init__(self, transaction_id, sender, receiver, amount, action_content, location, municipality, priority, comment=None, related_transaction_id=None):
              self.transaction_id = transaction_id
              self.sender = sender
              self.receiver = receiver
              self.amount = amount
              self.action_content = action_content
              self.location = location
              self.municipality = municipality
              self.timestamp = datetime.utcnow().isoformat()
              self.signature = None
              self.proof_of_place = None
              self.zk_snark_proof = None  # zk-SNARKs proof field
              self.priority = priority          # メタデータ: 取引の優先度
              self.comment = comment            # メタデータ: 取引のコメント
              self.related_transaction_id = related_transaction_id  # 関連取引ID

          def calculate_location_hash(self):
              latitude, longitude = self.location.split(',')
              return hashlib.sha256(f"{latitude.strip()}{longitude.strip()}".encode()).hexdigest()

          def generate_signature(self, private_key):
              message = f"{self.transaction_id}{self.sender}{self.receiver}{self.amount}{self.action_content}{self.location}{self.municipality}{self.timestamp}"
              hbs = HashBasedSignature(message)
              self.signature = hbs.generate_signature(private_key)

          def generate_zk_snark_proof(self, a, b, c):
              proof = create_zk_snark_proof(a, b, c)
              self.zk_snark_proof = proof

          def to_dict(self):
              return {
                  "transaction_id": self.transaction_id,
                  "sender": self.sender,
                  "receiver": self.receiver,
                  "amount": self.amount,
                  "action_content": self.action_content,
                  "location": self.location,
                  "municipality": self.municipality,
                  "timestamp": self.timestamp,
                  "signature": self.signature,
                  "proof_of_place": self.proof_of_place,
                  "zk_snark_proof": self.zk_snark_proof,
                  "priority": self.priority,
                  "comment": self.comment,
                  "related_transaction_id": self.related_transaction_id,
              }

      # HBS署名生成クラス
      class HashBasedSignature:
          def __init__(self, message):
              self.message = message

          def generate_signature(self, private_key):
              return hashlib.sha256(self.message.encode()).hexdigest()

          @staticmethod
          def verify_signature(message, signature, public_key):
              return signature == hashlib.sha256(message.encode()).hexdigest()

      # zk-SNARKs 証明を生成する関数
      def create_zk_snark_proof(a, b, c):
          if a * b != c:
              raise ValueError("Invalid inputs for zk-SNARK proof")
          
          G1_point = bn128.multiply(G1, a)
          G2_point = bn128.multiply(G2, b)
          proof = (G1_point, G2_point)
          return proof

      # zk-SNARKs 証明を検証する関数
      def verify_zk_snark_proof(proof, a, b, c):
          if not proof:
              return False

          G1_point, G2_point = proof
          pairing_result = pairing(G1_point, G2)
          pairing_target = final_exponentiate(pairing_result)
          
          return pairing_target == bn128.FQ12.one()

      # 市町村のポリゴンデータを取得する関数
      def fetch_municipality_polygon(municipality_name):
          response = requests.get(f"http://external-api.com/polygons?municipality={municipality_name}")
          if response.status_code == 200:
              return response.json()
          else:
              return None

      # PoPを検証する関数
      def verify_proof_of_place(municipality_name, latitude, longitude):
          polygon_data = fetch_municipality_polygon(municipality_name)
          if polygon_data:
              return is_inside_polygon(polygon_data["coordinates"], latitude, longitude)
          return False

      # ポリゴン内かどうかを検証する関数
      def is_inside_polygon(polygon_coordinates, latitude, longitude):
          is_inside = False
          j = len(polygon_coordinates) - 1
          for i in range(len(polygon_coordinates)):
              xi, yi = polygon_coordinates[i]
              xj, yj = polygon_coordinates[j]
              intersect = ((yi > longitude) != (yj > longitude)) and (latitude < (xj - xi) * (longitude - yi) / (yj - yi) + xi)
              if intersect:
                  is_inside = not is_inside
              j = i
          return is_inside

      # トランザクションを生成する関数
      def create_transaction(sender, receiver, amount, action_content, location, municipality, priority, private_key, comment=None, related_transaction_id=None):
          transaction_id = hashlib.sha256(f"{sender}{receiver}{amount}{action_content}{location}{municipality}{datetime.utcnow().isoformat()}".encode()).hexdigest()
          transaction = Transaction(transaction_id, sender, receiver, amount, action_content, location, municipality, priority, comment, related_transaction_id)
          transaction.generate_signature(private_key)

          latitude, longitude = map(float, location.split(','))
          if verify_proof_of_place(municipality, latitude, longitude):
              transaction.proof_of_place = {
                  "latitude": latitude,
                  "longitude": longitude,
                  "timestamp": transaction.timestamp
              }
          else:
              raise ValueError("Invalid Proof of Place")

          a, b, c = 3, 5, 15  # Example values
          transaction.generate_zk_snark_proof(a, b, c)

          return transaction

      # トランザクションをブロックチェーンネットワークに送信する関数
      def send_transaction(transaction, url):
          headers = {"Content-Type": "application/json"}
          response = requests.post(url, data=json.dumps(transaction.to_dict()), headers=headers)
          return response.json()

      # メインプログラム
      if __name__ == "__main__":
          sender = "UserA"
          receiver = "UserB"
          amount = 100.0
          action_content = "Helped someone carry groceries"
          location = "36.3,137.0"  # 緯度と経度
          municipality = "Kaga"
          priority = 1  # 優先度
          comment = "Urgent transaction"  # コメント
          private_key = "private_key_example"
          related_transaction_id = "previous_txn_id"  # 関連取引ID

          try:
              # トランザクションを生成
              transaction = create_transaction(sender, receiver, amount, action_content, location, municipality, priority, private_key, comment, related_transaction_id)

              # トランザクションをブロックチェーンネットワークに送信
              url = "http://localhost:8080/transactions"
              response = send_transaction(transaction, url)
              print(response)
          except ValueError as e:
              print(e)
       

      ・プログラムの説明:

    • HashBasedSignature クラス:

      • ハッシュベースの署名を生成および検証するクラス。

    • Transaction クラス:

      • トランザクションのデータ構造を定義。

      • トランザクションID、送信者、受信者、金額、行動内容、位置情報、市町村、タイムスタンプ、署名、PoP、zk-SNARKs証明、優先度、コメント、関連取引IDを含む。

    • create_zk_snark_proof 関数:

      • 簡単なzk-SNARKs証明を生成する関数。

    • verify_zk_snark_proof 関数:

      • zk-SNARKs証明を検証する関数。

    • fetch_municipality_polygon 関数:

      • 外部APIを呼び出して、市町村のポリゴンデータを取得する関数。

    • verify_proof_of_place 関数:

      • 市町村のポリゴンデータを使用して、指定した緯度経度がその市町村のポリゴン内にあるかを確認する関数。

    • is_inside_polygon 関数:

      • 特定の座標がポリゴン内にあるかどうかを判定する関数。

    • create_transaction 関数:

      • トランザクションを生成し、署名を生成し、PoPを検証し、zk-SNARKs証明を生成する関数。

    • send_transaction 関数:

      • トランザクションをブロックチェーンネットワークに送信する関数。

    • メインプログラム:

      • トランザクションを生成し、ブロックチェーンネットワークに送信するメインプログラム。
         

    • このPythonプログラムを使用して、DApps側で市町村のポリゴンデータを取得し、PoPを検証し、トランザクションを生成してブロックチェーンネットワークに送信することができる。zk-SNARKs証明や優先度、コメントなどのメタデータを使用することで、トランザクションの管理がより柔軟になる。
       
  17. 承認者情報 (Approver Information)

    • 取引を承認した個人または組織の情報。
    • 例: ユーザーID、公開鍵。

      承認者情報(Approver Information)は、取引を承認した個人または組織の情報を含む重要な要素である。この情報を利用することで、取引の透明性と信頼性を確保できる。以下に、基本設計からプログラムにいたる過程を示し、具体的なプログラムを提供する。

    • 承認者情報の定義:

      • ユーザーID
      • 公開鍵
      • 承認日時
      • 承認者の署名
    • データ構造の設計:

      • ApproverInfo 構造体を定義し、上記のフィールドを持つ。
    • 承認プロセスの設計:

      • 承認者が取引を承認する際に ApproverInfo を生成し、トランザクションに追加。
      • トランザクションが承認されるときに、承認者情報を検証し、記録する。これはDApps側であるPythonプログラムではく、ブロックチェーンのプログラムにて行われる。
    • スマートコントラクトと DApps 間の通信設計:

      • DApps からトランザクションの承認リクエストをスマートコントラクトに送信。
      • スマートコントラクトは承認者情報を検証し、承認結果を DApps に返す。
    • プログラムの実装

      DApps側では、取引を生成してブロックチェーンに送信するまでの処理を行い、承認プロセスはブロックチェーン側で実行される。以下は、DApps側で取引を生成して送信するPythonプログラムの例と、ブロックチェーン側で承認者情報を含む取引を処理するプログラムである。トランザクションの生成時と承認時の両方でPoP(Proof of Place)とPoH(Proof of History)のアルゴリズムが発動する。これにより、トランザクションの位置情報と履歴情報の正当性が確保され、透明性と信頼性が向上する。

      ・Pythonプログラム: DApps側での取引生成と送信
      import requests
      import hashlib
      import json
      from datetime import datetime

      # トランザションクラス
      class Transaction:
          def __init__(self, transaction_id, sender, receiver, amount, action_content, location, municipality, priority, comment=None, related_transaction_id=None):
              self.transaction_id = transaction_id
              self.sender = sender
              self.receiver = receiver
              self.amount = amount
              self.action_content = action_content
              self.location = location
              self.municipality = municipality
              self.timestamp = datetime.utcnow().isoformat()
              self.signature = None
              self.proof_of_place = None
              self.priority = priority          # メタデータ: 取引の優先度
              self.comment = comment            # メタデータ: 取引のコメント
              self.related_transaction_id = related_transaction_id  # 関連取引ID

          def calculate_location_hash(self):
              latitude, longitude = self.location.split(',')
              return hashlib.sha256(f"{latitude.strip()}{longitude.strip()}".encode()).hexdigest()

          def generate_signature(self, private_key):
              message = f"{self.transaction_id}{self.sender}{self.receiver}{self.amount}{self.action_content}{self.location}{self.municipality}{self.timestamp}"
              self.signature = hashlib.sha256((message + private_key).encode()).hexdigest()

          def to_dict(self):
              return {
                  "transaction_id": self.transaction_id,
                  "sender": self.sender,
                  "receiver": self.receiver,
                  "amount": self.amount,
                  "action_content": self.action_content,
                  "location": self.location,
                  "municipality": self.municipality,
                  "timestamp": self.timestamp,
                  "signature": self.signature,
                  "proof_of_place": self.proof_of_place,
                  "priority": self.priority,
                  "comment": self.comment,
                  "related_transaction_id": self.related_transaction_id,
              }

      # トランザクションを生成する関数
      def create_transaction(sender, receiver, amount, action_content, location, municipality, priority, private_key, comment=None, related_transaction_id=None):
          transaction_id = hashlib.sha256(f"{sender}{receiver}{amount}{action_content}{location}{municipality}{datetime.utcnow().isoformat()}".encode()).hexdigest()
          transaction = Transaction(transaction_id, sender, receiver, amount, action_content, location, municipality, priority, comment, related_transaction_id)
          transaction.generate_signature(private_key)
          return transaction

      # トランザクションをブロックチェーンネットワークに送信する関数
      def send_transaction(transaction, url):
          headers = {"Content-Type": "application/json"}
          response = requests.post(url, data=json.dumps(transaction.to_dict()), headers=headers)
          return response.json()

      # メインプログラム
      if __name__ == "__main__":
          sender = "UserA"
          receiver = "UserB"
          amount = 100.0
          action_content = "Helped someone carry groceries"
          location = "36.3,137.0"  # 緯度と経度
          municipality = "Kaga"
          priority = 1  # 優先度
          comment = "Urgent transaction"  # コメント
          private_key = "private_key_example"

          try:
              # トランザクションを生成
              transaction = create_transaction(sender, receiver, amount, action_content, location, municipality, priority, private_key, comment)

              # トランザクションをブロックチェーンネットワークに送信
              url = "http://localhost:8080/transactions"
              response = send_transaction(transaction, url)
              print(response)

          except ValueError as e:
              print(e)

      ・Rustプログラム: ブロックチェーン側での承認プロセス
      use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
      use serde::{Deserialize, Serialize};
      use sha2::{Sha256, Digest};
      use std::collections::HashMap;
      use std::sync::{Arc, Mutex};
      use chrono::Utc;

      #[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 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,
      }

      #[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>,
          related_transaction_id: Option<String>,
          priority: u8,
          comment: Option<String>,
          approver_info: Option<ApproverInfo>,
      }

      struct AppState {
          pending_transactions: Mutex<HashMap<String, Transaction>>,
          balances: Mutex<HashMap<String, f64>>,
          municipalities: Mutex<HashMap<String, Municipality>>,
      }

      #[post("/register_municipality")]
      async fn register_municipality(data: web::Data<AppState>, req: web::Json<Municipality>) -> impl Responder {
          let municipality = req.into_inner();
          let mut municipalities = data.municipalities.lock().unwrap();
          municipalities.insert(municipality.name.clone(), municipality);
          HttpResponse::Ok().json("Municipality registered")
      }

      #[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();
          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());
                      transaction.proof_of_history = Some(generate_proof_of_history());

                      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")
          }
      }

      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 generate_proof_of_history() -> String {
          let start = std::time::Instant::now();
          let mut hasher = Sha256::new();
          hasher.update("initial_seed".as_bytes());
          let mut result = hasher.finalize_reset();
          for _ in 0..1000000 {
              hasher.update(result);
              result = hasher.finalize_reset();
          }
          let duration = start.elapsed();
          format!("{} (computed in {:?})", hex::encode(result), duration)
      }

      #[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()),
          });

          HttpServer::new(move || {
              App::new()
                  .app_data(app_state.clone())
                  .service(register_municipality)
                  .service(receive_transaction)
                  .service(approve_transaction)
          })
          .bind("0.0.0.0:8080")?
          .run()
          .await
      }

      このコードは、取引の承認プロセスに承認者情報を追加し、取引の正当性と信頼性を確保するための基本設計と実装の一例である。具体的には、承認者の情報(ユーザーID、公開鍵、承認日時、署名)を取引に追加し、検証するプロセスを含んでいる。
       

  18. 取引費用 (Transaction Fee)

    • 取引にかかる手数料。
    • 例: 愛貨の量。
      愛貨にて手数料を支払いたい。その際、愛貨はお金とは逆の性質をもち、市町村に申請して最初に愛貨を持っている状態から、相手に渡すことで愛貨を減らすことができる。それゆえ、トランザクションの承認手数料とは、承認者の愛貨をやりとりされる愛貨の5%減額するということになる。そのトランザクションはメインチェーンにてペンディングされる。数日後、受信者が受信することで、メインチェーンでDPoSで選出された代表者が承認する際にも、5%の愛貨が承認者から減額される。そして、受信者へと愛貨がやりとりされる。つまり、承認者が二人いて、生成時と受信時だが、この二人から手数料の愛貨を減額するという仕組みだ。

      ・基本設計

    • 手数料の定義:

      • 愛貨の5%を取引手数料として設定する。
    • トランザクション生成時:

      • 取引の内容に基づいて、送信者からの愛貨を減額する。
    • トランザクション承認時・受信時:

      • 数日後、受信者が受信を実行すると、メインチェーンでDPoSにより選出された代表者がトランザクションを承認し、再度手数料を徴収してから、愛貨を受信者に送信する。
    • DApps側でのトランザクション生成:
      import hashlib
      import json
      import requests
      from datetime import datetime

      class Transaction:
          def __init__(self, transaction_id, sender, receiver, amount, action_content, location, municipality):
              self.transaction_id = transaction_id
              self.sender = sender
              self.receiver = receiver
              self.amount = amount
              self.action_content = action_content
              self.location = location
              self.municipality = municipality
              self.timestamp = str(datetime.now())
              self.signature = None
              self.proof_of_place = None
              self.proof_of_history = None
              self.related_transaction_id = None
              self.priority = 0
              self.comment = None
              self.fee = amount * 0.05  # 5%の手数料を設定

          def generate_signature(self, private_key):
              message = f"{self.transaction_id}{self.sender}{self.receiver}{self.amount}{self.action_content}{self.location}{self.municipality}{self.timestamp}"
              self.signature = hashlib.sha256(message.encode()).hexdigest()

          def to_dict(self):
              return {
                  "transaction_id": self.transaction_id,
                  "sender": self.sender,
                  "receiver": self.receiver,
                  "amount": self.amount,
                  "action_content": self.action_content,
                  "location": self.location,
                  "municipality": self.municipality,
                  "timestamp": self.timestamp,
                  "signature": self.signature,
                  "proof_of_place": self.proof_of_place,
                  "proof_of_history": self.proof_of_history,
                  "related_transaction_id": self.related_transaction_id,
                  "priority": self.priority,
                  "comment": self.comment,
                  "fee": self.fee
              }

          def send_transaction(self):
              url = "http://localhost:8080/transactions"
              headers = {"Content-Type": "application/json"}
              response = requests.post(url, headers=headers, data=json.dumps(self.to_dict()))
              return response.json()

      # トランザクション生成例
      transaction = Transaction(
          transaction_id="tx123",
          sender="userA",
          receiver="userB",
          amount=100,
          action_content="Helped with groceries",
          location="36.3,137.0",
          municipality="Kaga"
      )

      transaction.generate_signature("private_key")
      response = transaction.send_transaction()
      print(response)


      ・市町村のブロックチェーンでのトランザクション承認:
      use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
      use chrono::Utc;
      use serde::{Deserialize, Serialize};
      use sha2::{Digest, Sha256};
      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>,
          related_transaction_id: Option<String>,
          priority: u8,
          comment: Option<String>,
          fee: f64,
      }

      #[derive(Debug, Clone, Serialize, Deserialize)]
      struct ProofOfPlace {
          latitude: f64,
          longitude: f64,
          timestamp: String,
      }

      struct AppState {
          pending_transactions: Mutex<HashMap<String, Transaction>>,
          balances: Mutex<HashMap<String, f64>>,
          municipalities: Mutex<HashMap<String, Municipality>>,
      }

      #[post("/register_municipality")]
      async fn register_municipality(data: web::Data<AppState>, req: web::Json<Municipality>) -> impl Responder {
          let municipality = req.into_inner();
          let mut municipalities = data.municipalities.lock().unwrap();
          municipalities.insert(municipality.name.clone(), municipality);
          HttpResponse::Ok().json("Municipality registered")
      }

      #[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();
          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, String)>) -> impl Responder {
          let (transaction_id, approver_id) = 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;
                      let fee = transaction.fee;
                      *balances.entry(transaction.sender.clone()).or_insert(0.0) -= amount;
                      *balances.entry(transaction.receiver.clone()).or_insert(0.0) += amount;

                      // 手数料を送信者へ還元
                      *balances.entry(transaction.sender.clone()).or_insert(0.0) += fee;

                      // 承認時のPoPとPoHを記録
                      transaction.proof_of_place = Some(proof_of_place.clone());
                      transaction.proof_of_history = Some(generate_proof_of_history());

                      // トランザクションをメインチェーンにペンディング
                      send_to_mainchain(&transaction);

                      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")
          }
      }

      #[post("/receive")]
      async fn receive_transaction(data: web::Data<AppState>, req: web::Json<(String, String)>) -> impl Responder {
          let (transaction_id, receiver_id) = req.into_inner();
          let mut balances = data.balances.lock().unwrap();

          // メインチェーンからトランザクションを取得
          if let Some(transaction) = fetch_from_mainchain(&transaction_id) {
              let fee = transaction.fee;
              let total_amount = transaction.amount + fee;
              *balances.entry(receiver_id.clone()).or_insert(0.0) += total_amount;

              // 受信者が手数料を受け取る
              *balances.entry("approver".to_string()).or_insert(0.0) -= fee;

              HttpResponse::Ok().json("Transaction received")
          } else {
              HttpResponse::BadRequest().json("Transaction not found on mainchain")
          }
      }

      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 generate_proof_of_history() -> String {
          let start = std::time::Instant::now();
          let mut hasher = Sha256::new();
          hasher.update("initial_seed".as_bytes());
          let mut result = hasher.finalize_reset();
          for _ in 0..1000000 {
              hasher.update(result);
              result = hasher.finalize_reset();
          }
          let duration = start.elapsed();
          format!("{} (computed in {:?})", hex::encode(result), duration)
      }

      fn send_to_mainchain(transaction: &Transaction) {
          // メインチェーンにトランザクションを送信するロジック
      }

      fn fetch_from_mainchain(transaction_id: &str) -> Option<Transaction> {
          // メインチェーンからトランザクションを取得するロジック
          None
      }

      #[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()),
          });

          HttpServer::new(move || {
              App::new()
                  .app_data(app_state.clone())
                  .service(register_municipality)
                  .service(receive_transaction)
                  .service(approve_transaction)
                  .service(receive_transaction)
          })
          .bind("0.0.0.0:8080")?
          .run()
          .await
      }

      ・説明:
      ・手数料の定義:トランザクション生成時に、送信金額の5%を手数料として計算し、feeフィールドに追加している。
      ・トランザクション生成時:トランザクション生成時に承認者から手数料を減額し、トランザクションをメインチェーンにペンディングする。
      ・トランザクションの受信時:受信者がトランザクションを受信する際、受信者は送信金額+手数料分の愛貨を受け取る。メインチェーンの承認者のバランスから手数料分の愛貨が減額される。
       

  19. 関連アクションID (Related Action ID)

    • 取引に関連する愛の行動の識別情報。
    • 例: アクションID。
  20. 愛の行動レベル (Love Action Level)

    • 取引に関連する愛の行動のレベル。
    • 例: レベル1~10。

これらの項目を包括的に管理することで、愛記システムの透明性と信頼性を確保し、すべての取引と愛の行動が正確かつ改ざんされていないことを保証できる。各項目について詳細な仕様を定義し、システム全体で統一的に管理することが重要である。

 

 

いかがであろうか、まだ項目の途中だが、次回に続きを記載したい。