【Ethereum】ERC721

ERC721は非代替トークンのNFT規格の一つ

pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC721, Ownable {
    constructor(address initialOwner)
        ERC721("HFToken", "HogeFuga") // MyToken..トークン名 MTK..トークン名の略称
        Ownable(initialOwner)
    {}

    function safeMint(address to, uint256 tokenId) public onlyOwner {
        _safeMint(to, tokenId);
    }
}

【Ethereum】Solidityの基礎2

# Solidityのオブジェクト
## msg オブジェクト
– msg.sender: address
– msg.value: eth value
– msg.gas
– msg.data

## txオブジェクト
– tx.gasprice
– tx.origin: transaction address

## blockオブジェクト
– block.coinbase
– block.difficulty
– block.gaslimit
– block.number
– block.timestamp

## addressオブジェクト
– address.balance
– address.transfer
– address.send

## 関数
addmod, mulmod, keccak256, sha3, sha256, erecover, selfdestruct, this

## その他
– external: コントラクトの外部
– internal: コントラクトの内部
– view: データの閲覧のみ
– pure: 読み取りも行わない
– event: ブロックチェーン上で何かが生じた時、web上やアプリに伝えることができる仕組み

## ECR20

pragma solidity ^0.8.17;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes calldata _extraData) external; }

contract TokenERC20 {
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    uint256 public totalSupply;

    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed _owner, address indexed _sender, uint256 _value);
    event Burn(address indexed from, uint256 value);

    constructor(
        uint256 initialSupply,
        string memory tokenName,
        string memory tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
        name = tokenName;
        symbol = tokenSymbol;
    }

    function _transfer(address _from, address _to, uint _value) internal {
        require(_to != address(0));
        require(balanceOf[_from] = _value);
        require(balanceOf[_to] + _value = balanceOf[_to]);
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        emit Transfer(_from, _to, _value);
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        _transfer(msg.sender, _to, _value);
        return true;
    }

    function approve(address _spender, uint256 _value) public
        returns (bool success) {
            allowance[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value);
            return true;
        }

    function approveAndCall(address _spender, uint256 _value, bytes memory _extraData)
        public 
        returns (bool success) {
            tokenRecipient spender = tokenRecipient(_spender);
            if (approve(_spender, _value)) {
                spender.receiveApproval(msg.sender, _value, address(this), _extraData);
                return true;
            }
        }

    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] = _value);
        balanceOf[msg.sender] -= _value;
        totalSupply -= _value;
        emit Burn(msg.sender, _value);
        return true;
    }

    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] = _value);
        require(_value = allowance[_from][msg.sender]);
        balanceOf[_from] -= _value;
        allowance[_from][msg.sender] -= _value;
        totalSupply -= _value;
        emit Burn(_from, _value);
        return true;
    }
}

from solidity:
TypeError: No matching declaration found after argument-dependent lookup.
–> ecr20.sol:31:8:
|

ん? 何故だ…

【Ethereum】トレーサビリティのsmart contractを考える

単純にアドレスに保存するだけであれば、mapping(address => Data) accounts;とすれば良さそう。
ただし、この場合は、商品ごとにアドレスを持っていないといけない。
緯度経度やstatusの管理は、フロントエンド側(React)で実装か…
memoryとstorageの仕様詳細が欲しいところ。。。

pragma solidity ^0.8.17;

contract Traceability {

    struct Data {
        uint256 id;
        string name;
        string latitude;
        string longitude;
        string status;
    }

    address[] public users;

    mapping(address => Data) accounts;

    function registerAccount(uint256 _id, string memory _name, string memory latitude, string memory longitude, string memory status)
        public 
        returns (bool)
    {
        accounts[msg.sender].id = _id;
        accounts[msg.sender].name = _name;
        accounts[msg.sender].latitude = latitude;
        accounts[msg.sender].longitude = longitude;
        accounts[msg.sender].status = status;
        
        users.push(msg.sender);
        return true;
    }

    function ViewAccount(address user) public view returns (uint256, string memory, string memory, string memory, string memory) {
        uint256 _id = accounts[user].id;
        string memory _name = accounts[user].name;
        string memory _latitude = accounts[user].latitude;
        string memory _longitude = accounts[user].longitude;
        string memory _status = accounts[user].status;

        return (_id, _name, _latitude, _longitude, _status);
    }

}

ethの受け取り

pragma solidity ^0.8.17;

contract RecvEther {
    address public sender;

    uint public recvEther;

    function () payable {
        sender = msg.sender;
        recvEther += msg.value;
    }
}

【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());
}