【Ethereum】solidity 基礎

memoryは処理中のみ保存し、storageは処理後、ブロックチェーンに保存される。

pragma solidity ^0.8.17;

contract Register {
    struct Data {
        string name;
        uint256 age;
        string hobby;
    }

    // 全ユーザのアドレスを保存
    address[] public users;

    mapping(address => Data) accounts;

    function registerAccount(string memory _name, uint256 _age, string memory _hobby) 
        public 
        returns (bool)
    {
        if (!isUserExist(msg.sender)) {
          users.push(msg.sender);
        }
        // msg.sender はトランザクション送信者
        accounts[msg.sender].name = _name;
        accounts[msg.sender].age = _age;
        accounts[msg.sender].hobby = _hobby;
        return true;
    }

    function isUserExist(address user) public view returns (bool) {
        for (uint256 i = 0; i < users.length; i++) {
            if (users[i] == user) {
                return true;
            }
        }
        return false;
    }

    function ViewAccount(address user) public view returns (string memory, uint256, string memory) {
        string memory _name = accounts[user].name;
        uint256 _age = accounts[user].age;
        string memory _hobby = accounts[user].hobby;

        return (_name, _age, _hobby);
    }
}

remixでデプロイのテストができる

metamaskアカウントでsepoliaにdeployする
https://sepolia.etherscan.io/

フロントエンド

import React from "react";
import Resister from "./Register.json";
import getWeb3 from "./getWeb3";
// import "./App.css";
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      web3: null,
      accounts: null,
      contract: null,
      name: null,
      age: null,
      hobby: null,
      address: "",
      outputName: null,
      outputAge: null,
      outputHobby: null,
    };
  }

  componentDidMount = async () => {
    try {
      const web3 = await getWeb3();

      const accounts = await web3.eth.getAccounts();
      const networkId = await web3.eth.net.getId();
      const deployedNetwork = Resister.networks[networkId];
      const instance = new web3.eth.Contract(
        Resister.abi,
        deployedNetwork && deployedNetwork.address
      );

      this.setState({ web3, accounts, contract: instance });
    } catch (error) {
      alert(
        `Failed to load web3, accounts, or contract. Check console for details.`
      );
      console.error(error);
    }

    const { accounts } = this.state;
    console.log(accounts);
  };

  // アカウント情報の登録
  writeRecord = async () => {
    const { accounts, contract, name, age, hobby } = this.state;
    const result = await contract.methods.registerAccount(name, age, hobby).send({
      from: accounts[0],
    });
    console.log(result);

    if (result.status === true) {
      alert('会員登録が完了しました。');
    }
  };

  // アカウント情報の読み込み
  viewRecord = async () => {
    const { contract, accounts } = this.state;
    console.log(contract);

    const result = await contract.methods.viewAccount(accounts[0]).call();
    console.log(result);

    const outputName = result[0];
    const outputAge = result[1];
    const outputHobby = result[2];
    this.setState({ outputName, outputAge, outputHobby });
  };

  handleChange = (name) => (event) => {
    this.setState({ [name]: event.target.value });
  };

  render() {
    return (
      <div className="App">
        <br />
        <form>
          <div>
            <label>氏名:</label>
            <input
              onChange={this.handleChange("name")} />
          </div>

          <div>
            <label>年齢:</label>
            <input
              onChange={this.handleChange("age")} />
          </div>

          <div>
            <label>趣味:</label>
            <input
              onChange={this.handleChange("hobby")} />
          </div>

          <button type='button' onClick={this.writeRecord}>
            会員登録
          </button>
        </form>

        <br />
        <br />

        <form>
          <label>検索したいアドレスを入力してください。</label>
          <input onChange={this.handleChange("address")} />

          <button type='button' onClick={this.viewRecord}>
            検索
            </button>
        </form>

        <br />
        <br />

        {this.state.outputName ? <p>氏名: {this.state.outputName}</p> : <p></p>}
        {this.state.outputAge ? <p>年齢: {this.state.outputAge}</p> : <p></p>}
        {this.state.outputHobby ? <p>趣味: {this.state.outputHobby}</p> : <p></p>}

      </div>

    );
  }
}

export default App;

ちょっと記事が古いので期待通りに動かないが、

【Python】準同型暗号(Homomorphic Encryption)

号化されたままのデータに対して演算(加算や乗算)を行える暗号方式

from phe import paillier

# 鍵生成
public_key, private_key = paillier.generate_paillier_keypair()

# 平文データ
a = 10
b = 20

# 暗号化
enc_a = public_key.encrypt(a)
enc_b = public_key.encrypt(b)

# 暗号上での加算(a + b)
enc_sum = enc_a + enc_b

# 暗号上でのスカラー乗算(a * 5)
enc_mul = enc_a * 5

# 復号
dec_sum = private_key.decrypt(enc_sum)
dec_mul = private_key.decrypt(enc_mul)

print("平文 a =", a, ", b =", b)
print("復号後 (a + b) =", dec_sum)
print("復号後 (a * 5) =", dec_mul)

$ python3 paillier.py
平文 a = 10 , b = 20
復号後 (a + b) = 30
復号後 (a * 5) = 50

【Python】シュノア証明

シュノア署名(Schnorr signature)は、楕円曲線暗号や整数群の上で定義される、シンプルかつ安全なデジタル署名方式

import hashlib
import random

p = 23
q = 11
g = 2

x = random.randint(1, q - 1)
y = pow(g, x, p)
print("秘密鍵 x = ", x)
print("公開鍵 y = ", y)

def H(r, m):
    data = str(r) + m
    h = hashlib.sha256(data.encode()).hexdigest()
    return int(h, 16) % q

def schnorr_sign(m):
    k = random.randint(1, q - 1)
    r = pow(g, k, p)
    e = H(r, m)
    s = (k + x * e) % q
    return (e, s)

def schnorr_verify(m, signature):
    e, s = signature
    g_s = pow(g, s, p)
    y_e = pow(y, e, p)
    r_prime = (g_s * pow(y_e, -1, p)) % p
    e_prime = H(r_prime, m)
    return e == e_prime

message = "hello"
signature = schnorr_sign(message)
print("署名:", signature)

valid = schnorr_verify(message, signature)
print("署名の検証結果:", valid)

$ python3 schnorr.py
秘密鍵 x = 10
公開鍵 y = 12
署名: (5, 1)
署名の検証結果: True

【Python】ゼロ知識証明

ゼロ知識証明とは、証明者がその事実を提示するだけで、その事実の内容を隠すことができる証明方法
Zero-knowledge Proofとしてよく使われるのが、ハミルトングラフの知識を持っていることや秘密のパスワードを証明せず知っていることを示す。

xを知っていおり、 y = x^2 mod p を示す。

import random

p = 23
x = 5
y = pow(x, 2, p)

print(" y =", y)

def prover_step():
    r = random.randint(1, p - 1)
    a = pow(r, 2, p)
    return r, a

def verifier_challenge():
    return random.randint(0, 1)

def prover_response(r, c):
    if c == 0:
        return r
    else:
        return (r * x) % p

def verifier_check(a, z, c):
    if c == 0:
        return pow(z, 2, p) == a
    else:
        return pow(z, 2, p) == (a * y) % p

r, a = prover_step()
print("Prover: 提出 a=", a)

c = verifier_challenge()
print("Verifier: チャレンジ c=", c)

z = prover_response(r, c)
print("Prover: 応答 z =", z)

ok = verifier_check(a, z, c)
print("Verifier: 検証結果 =", ok)

$ python3 zkp.py
y = 2
Prover: 提出 a= 4
Verifier: チャレンジ c= 1
Prover: 応答 z = 10
Verifier: 検証結果 = True

—– 実際の計算
r = ?,
a = 4,
c = 1
z = 10
10 * 10 % 23 = 5 * 2 / 23

x の値を知らせずに、y = x^2 mod p を証明している。証明の際には、c != 0 の時にのみyの値を利用する
zkpの概念は理解できたが、なぜこれが証明になるのかのロジックはよくわからない..

【Rust】スタックマシンで関数を実装する

変数と同じように、関数名とinstructionをhashmapに保存する

/x 2 def /double { x 2 * } fn

        "fn" => {
            let tmp = func(&mut stack, instructions.clone());
            for (key, value) in &tmp {
                funcs.insert(key.to_string(), value.to_vec());
            }
            println!("{:?}", funcs); 
        } 

// 省略

fn func(stack: &mut Vec<String>, instructions: Vec<String>) -> HashMap<String, Vec<String>> {
    let mut funcs: HashMap<String, Vec<String>> = HashMap::new();
    let name = stack.pop().unwrap();

    funcs.insert(name, instructions);
    funcs
}

{“double”: [“x”, “2”, “*”]}

関数名と式をhashmapに保存したので、後は関数名が書かれていた際に、関数の内容を呼び出すだけ


    } else if funcs.contains_key(word) {
      let _ = op_fn(&mut stack, funcs.get(word).unwrap(), vars.clone());
//

fn op_fn(stack: &mut Vec<String>, input: &Vec<String>, vars: HashMap<String, String>) {
    let mut words = input.clone();
    while let Some((&ref word, rest)) = words.split_first() {
        if word.is_empty() {
          break;
        }
        if let Ok(parsed) = word.parse::<i32>() {
          stack.push(parsed.to_string());
        } else if word.starts_with("/") {
          stack.push((word[1..]).to_string());
        } else if vars.contains_key(word) {
          stack.push(vars.get(word).unwrap().to_string());
        } else {
          match word.as_str() {
            "+" => add(stack),
            "-" => sub(stack),
            "*" => mul(stack),
            "/" => div(stack),
            "%" => r#mod(stack),
            "<" => lt(stack),
            ">" => rt(stack),
            "==" => equal(stack),
            "!=" => nequal(stack),
            "sqrt" => r#sqrt(stack),
            "if" => op_if(stack),
            _ => todo!(),
            }
        }
        words = rest.to_vec();
      }
}

/x 2 def /double { x 2 * } fn double
=> [“4”]

【Rust】スタックマシンでループ文を実装するための準備

ループで実行する処理文を記憶するために、”{ }”で囲まれている中身を取得してベクターで保存しておく。

fn main() {
    let mut stack = vec![];
    let mut instructions = vec![];

    let str = "1 2 + { 10 20 + }".to_string();
    let mut words: Vec<_> = str.split(" ").collect();

    while let Some((&word, mut rest)) = words.split_first() {
        if word.is_empty() {
            break;
        }
        if word == "{" {
            (instructions, rest) = parse_instruction(rest);
        } else if let Ok(parsed) = word.parse::<i32>() {
            stack.push(parsed.to_string());
        } else {
            match word {
              "+" => add(&mut stack),
              _ => panic!("{word:?} could not be parsed"),
            }
        }
        words = rest.to_vec();
    }
    println!("{:?}", stack);
    println!("{:?}", instructions);
}

fn parse_instruction<'src, 'a>(input: &'a [&'src str])-> (Vec<String>, &'a [&'src str]) {
    let mut tokens: Vec<String> = vec![];
    let mut words = input;

    while let Some((&word, mut rest)) = words.split_first() {
        if word.is_empty() {
            break;
        }
        if word == "}" {
            return (tokens, rest);
        } else {
            tokens.push(word.to_string())
        }
        words = rest;
    }
    (tokens, words)
}

fn add(stack: &mut Vec<String>) {
    let rhs: i32 = stack.pop().unwrap().parse::<i32>().unwrap();
    let lhs: i32 = stack.pop().unwrap().parse::<i32>().unwrap();
    stack.push((lhs + rhs).to_string());
}

Running `target/debug/sample`
[“3”]
[“10”, “20”, “+”]

【Rust】sqrtの計算

f64でsqrt()の関数があるので、f64に変換して計算する
https://doc.rust-lang.org/std/primitive.f64.html#method.sqrt

fn main() {
    let n = 10;
    let f = n as f64;
    let s = f.sqrt();
    println!("{}", s);
    println!("{}", s as i32);
}

これをスタックマシンに実装します。とりあえずi32で統一しているので、ここでは、sqrt後 as i32としています。

match word {
        "+" => add(&mut stack),
        "-" => sub(&mut stack),
        "*" => mul(&mut stack),
        "/" => div(&mut stack),
        "%" => r#mod(&mut stack),
        "<" => lt(&mut stack),
        ">" => rt(&mut stack),
        "sqrt" => r#sqrt(&mut stack),
        "if" => op_if(&mut stack),
        "def" => {  
          let tmp = op_def(&mut stack);
          for (key, value) in &tmp {
            vars.insert(key.to_string(), value.to_string());
          }
        },
        _ => panic!("{word:?} could not be parsed"),
      }
//

fn r#sqrt(stack: &mut Vec<String>) {
  let f: f64 = stack.pop().unwrap().parse::<f64>().unwrap();
  let s = f.sqrt();
  stack.push((s as i32).to_string());
}

【Rust】四則演算に括弧{} をつける

fn main() {

  let mut stack = vec![];

  // Reverse Polish Notation
  let str = "100 5 - { 1 2 + } +".to_string();

  let mut words: Vec<_> = str.split(" ").collect();
  while let Some((&word, mut rest)) = words.split_first() {
    if word.is_empty() {
      break;
    }
    if word == "{" {
      let value;
      (value, rest) = parse_block(rest);
      stack.push(value);
    } else if let Ok(parsed) = word.parse::<i32>() {
      stack.push(parsed);
    } else {
      match word {
        "+" => add(&mut stack),
        "-" => sub(&mut stack),
        "*" => mul(&mut stack),
        "/" => div(&mut stack),
        _ => panic!("{word:?} could not be parsed"),
      }
    }
    words = rest.to_vec();
  }
  println!("{:?}", stack); 
}

fn parse_block<'src, 'a>(input: &'a [&'src str]) -> (i32, &'a [&'src str]){
  let mut tokens: Vec<i32> = vec![];
  let mut words = input;

  while let Some((&word, mut rest)) = words.split_first() {
    if word.is_empty() {
      break;
    }
    if word == "}" {
      return (tokens[0], rest);

    } else if let Ok(parsed) = word.parse::<i32>() {
      tokens.push(parsed);
    } else {
      match word {
        "+" => add(&mut tokens),
        "-" => sub(&mut tokens),
        "*" => mul(&mut tokens),
        "/" => div(&mut tokens),
        _ => todo!(),
        // _ => panic!("{word:?} could not be parsed"),
        }
    }
    words = rest;
  }
  (tokens[0], words)
}

fn add(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs + rhs);
}

fn sub(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs - rhs);
}

fn mul(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs * rhs);
}

fn div(stack: &mut Vec<i32>) {
  let rhs = stack.pop().unwrap();
  let lhs = stack.pop().unwrap();
  stack.push(lhs / rhs);
}

[98]

【Rust】&[&str]の加算

&[&str]と&[&str]の加算の方法が不明だったので、一旦Stringに変えてformat!で加算する

fn main() {
    let str = "100 5 - { 1 2 + }".to_string();
    let mut words: Vec<_> = str.split(" ").collect();
    let Some((&word, mut rest)) = words.split_first() else { todo!() };

    println!("{:?}", rest);

    let mut tokens: Vec<_> = vec![];
    tokens.push(1);
    let mut str = "init".to_string();
    str = format!("{} {}", str, tokens[0].to_string());
    for r in rest {
        str = format!("{} {}", str, r);
        println!("{}", str);
    }
    println!("{:?}", str);   
}

“init 1 5 – { 1 2 + }”

うーん、なんか違う気がする..

【Rust】HashMapのValueへのアクセス

employee[“A”].clone().pop() ではなく、要素を指定せずにvalueをpop()したい場合

use std::collections::HashMap;

fn main() {
    let mut employee: HashMap<String, Vec<String>> = HashMap::new();
    employee.insert("A".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
    employee.insert("B".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
    for (key, mut value) in employee {
        println!("{:?}", value.pop());
    }
}

for e in employee ってしてしまうと、eからvalueの取り出し方法がわからなくなる。
これに結構時間かかりました。

vectorの要素のindexを取得する方法

        println!("{:?}", value.iter().position(|n| n == "Y"));
use std::collections::HashMap;

fn main() {

   let mut employee: HashMap<String, Vec<String>> = HashMap::new();
   employee.insert("A".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
   employee.insert("B".to_string(), vec!["X".to_string(), "Y".to_string(), "Z".to_string()]);
   employee.insert("C".to_string(), vec!["Y".to_string(), "Z".to_string(), "X".to_string()]);

   let mut department: HashMap<String, Vec<String>> = HashMap::new();
   department.insert("X".to_string(), vec!["C".to_string(), "A".to_string(), "B".to_string()]);
   department.insert("Y".to_string(), vec!["B".to_string(), "C".to_string(), "A".to_string()]);
   department.insert("Z".to_string(), vec!["A".to_string(), "B".to_string(), "C".to_string()]);

   let res = stable_match(employee, department);
   println!("{:?}", res);
}

fn stable_match(request_dict: HashMap<String, Vec<String>>, accept_dict: HashMap<String, Vec<String>>) -> HashMap<String, String>{
   let mut match_dict: HashMap<String, String> = HashMap::new();
   let req_len = request_dict.len();

   while match_dict.len() < req_len {
      'label: for (req_name, req_value) in &request_dict {
         for value in match_dict.values() {
            if value == req_name {
               break 'label;
            }
         }
         let acc_name = req_value.pop();
         if !match_dict.contains_key(&acc_name) {
            match_dict.insert(acc_name.unwrap(), req_name.to_string());
         } else {
            let rival_name = match_dict[&acc_name];
            if accept_dict[&acc_name].iter().position(|n| n == req_name) < accept_dict[&acc_name].iter().position(|n| n == rival_name) {
               match_dict.insert(acc_name.unwrap(), req_name.to_string());
            }
         }
      }
   }
   return match_dict;
}

うーむ、上手く書けん…