ubuntuにipfsのインストール

$ wget https://dist.ipfs.io/go-ipfs/v0.5.0/go-ipfs_v0.5.0_linux-arm64.tar.gz
$ tar xvzf go-ipfs_v0.5.0_linux-arm64.tar.gz
$ cd go-ipfs/
$ sudo ./install.sh
Moved ./ipfs to /usr/local/bin
$ ipfs version
ipfs version 0.5.0
$ ipfs init
initializing IPFS node at /home/vagrant/.ipfs
generating 2048-bit RSA keypair…done
peer identity: QmWKa24RSJMHNZNSWiB5FuUkLQFvkM7L8ZqhmUKaUbLAJF
to get started, enter:

ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
$ ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
Hello and Welcome to IPFS!

██╗██████╗ ███████╗███████╗
██║██╔══██╗██╔════╝██╔════╝
██║██████╔╝█████╗ ███████╗
██║██╔═══╝ ██╔══╝ ╚════██║
██║██║ ██║ ███████║
╚═╝╚═╝ ╚═╝ ╚══════╝

If you’re seeing this, you have successfully installed
IPFS and are now interfacing with the ipfs merkledag!

——————————————————-
| Warning: |
| This is alpha software. Use at your own discretion! |
| Much is missing or lacking polish. There are bugs. |
| Not yet secure. Read the security notes for more. |
——————————————————-

Check out some of the other files in this directory:

./about
./help
./quick-start <-- usage examples ./readme <-- this file ./security-notes $ ls ~/.ipfs blocks config datastore datastore_spec keystore version

【Rust】getblockのCLIコマンド機能を作る

CLIでgetblockと引数にblock heightのi32を受け取って、ブロック情報を表示します。

#[derive(Subcommand)]
enum Command {
  Getnewaddress,
  Getinfo,
  Getbalance(BalanceArgs),
  Getaccount,
  Getblock(BlockArgs),
}

fn main() {
   let args = App::parse();
   match args.command {
      Command::Getnewaddress => getnewaddress(),
      Command::Getinfo => getinfo(),
      Command::Getbalance(args) => args.run(),
      Command::Getaccount => getaccount(),
      Command::Getblock(args) => args.run(),
   }
}

#[derive(Args)]
struct BlockArgs {
  height: i32,
}

impl BlockArgs {
  fn run(&self) {
    let _ = cli::get_block(self.height);
  }
}

$ ./target/debug/ruscal getblock 8
Block { height: 8, version: “0.1.0”, time: 2025-02-25T06:48:54.561868021Z, transactions: [SignedTransaction { version: “0.1.0”, time: 2025-02-25T06:47:49.809920514Z, sender: “0410f87429e89498e928d00b6a6186fdc9ccbde2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0”, receiver: “1FsHydJvr3nKj3KcwFhSozBf2WBKMf9jeo”, amount: 50, nft_data: “”, nft_origin: “”, op_code: “”, signature: “8A5216CD62C309A4923E9088CA9284AFC7C0261D89754FCE84735EC34A193A089B7A4C62FC32F91AE0B83549D575CE3D5EBAC1C4B9A61F0EF67D1B851F3F3E53” }, SignedTransaction { version: “0.1.0”, time: 2025-02-25T06:48:45.817409745Z, sender: “0410f87429e89498e928d00b6a6186fdc9ccbde2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0”, receiver: “1FsHydJvr3nKj3KcwFhSozBf2WBKMf9jeo”, amount: 60, nft_data: “”, nft_origin: “”, op_code: “”, signature: “6E73E636DDA5A5F360280442BEA308CD3C38C7A4C769BC8D4965B5106F9F6647D15B0382DE90ECFB84AE462BB81F26099DB1F8A9F0276541C44C60376FF31D16” }], hash: “0000ddb477024b0fb97c02e34bfad38eaee9891524759ba8801e06283b12cb9c”, nonce: “270778”, merkle: “713f5eb6109b03eb22cf8236171040f6bbf6171fbb2513b782b8b9fa9f41807f”, miner: “MTYwLjE2LjExNy44MA==”, validator: “MTYwLjE2LjExNy44MA==”, op: “” }

同じような容量で、gettransactionも同様に作ります。
$ ./target/debug/ruscal gettransaction 8A5216CD62C309A4923E9088CA9284AFC7C0261D89754FCE84735EC34A193A089B7A4C62FC32F91AE0B83549D575CE3D5EBAC1C4B9A61F0EF67D1B851F3F3E53

transactionの送信の場合

#[derive(Args)]
struct MoveArgs {
  pub_key: String,
  address: String,
  amount: i32
}

impl MoveArgs {
  fn run(&self) {
    // transaction送信関数を呼び出す
  }
}

$ ./target/debug/ruscal move de2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0 12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD 1000

なるほど、大分わかってきました。

【Rust】getnewaddressのAPI機能を作る

### コマンドライン引数の書き方

fn main() {

   let command_name = std::env::args().nth(0).unwrap_or("CLI".to_string());
   let name = std::env::args().nth(1).unwrap_or("World".to_string());

   println!("Hello {} via {}!", name, command_name);
}

$ cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/ruscal`
Hello World via target/debug/ruscal!

### 上記を応用する

fn main() {
   let args = App::parse();
   match args.command {
    Command::Getnewaddress => getnewaddress(),
   }
}

fn getnewaddress() {
  address::get_address();
}
use p256::{
    ecdsa::{SigningKey},
    pkcs8::EncodePrivateKey, PublicKey,SecretKey};
use rand_core::{OsRng};
use std::{fs::File, io::Write, path::Path};
use ripemd::{Ripemd160};
use sha2::{Digest, Sha256};

pub fn get_address() {
    init_secret();
    let contents = std::fs::read_to_string("./config/tmp.pem")
        .expect("something went wrong reading the file");
    let secret_pem = contents.trim();

    let secret_key = secret_pem.parse::<SecretKey>().unwrap();
    let _private_key_serialized = hex::encode(&secret_key.to_bytes());

    let public_key = secret_key.public_key();
    let _public_key_serialized = hex::encode(&public_key.to_sec1_bytes());
    let address = create_address(&public_key);  
    println!("{}", address);
}

pub fn init_secret() {
    let path = Path::new("./config/tmp.pem");
    if path.is_file() {
        // println!("The private key, public key, and address have already been created.");
    } else {
        let secret_key = SigningKey::random(&mut OsRng);
        let secret_key_serialized = secret_key
            .to_pkcs8_pem(Default::default())
            .unwrap()
            .to_string();
        let mut file = File::create("./config/tmp.pem").expect("file not found.");
        writeln!(file, "{}", secret_key_serialized).expect("can not write.");
        println!("created a private key.");
    }
}

pub fn create_address(public_key: &PublicKey) -> String {

    let vk = public_key.to_sec1_bytes();
    let mut hasher = Sha256::new();
    hasher.update(vk);
    let hashed_sha256 = hasher.finalize();

    let mut hasher = Ripemd160::new();
    hasher.update(hashed_sha256);
    let account_id = hasher.finalize();

    let mut payload = account_id.to_vec();
    payload.insert(0, 0x00);

    let mut hasher = Sha256::new();
    hasher.update(&payload);
    let hash = hasher.finalize();

    let mut hasher = Sha256::new();
    hasher.update(hash);
    let checksum = hasher.finalize();

    payload.append(&mut checksum[0..4].to_vec());

    const ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    let address = base_x::encode(ALPHABET, &payload);

    return address;
}

$ cargo run getnewaddress
Compiling ruscal v0.1.0 (/home/vagrant/dev/rust/api)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.92s
Running `target/debug/ruscal getnewaddress`
1BnpTXdSbSgMZwWYq7cdU3LPSw1g4YhyHZ

【IPFS】Merkle DAGをPythonで書く

import hashlib

class Node:
    def __init__(self, data=None, children=None):
        self.data = data
        self.children = children or []
        self.hash = self.compute_hash()

    def compute_hash(self):
        m = hashlib.sha256()
        if self.data:
            m.update(self.data.encode('utf-8'))
        for child in self.children:
            m.update(child.hash.encode('utf-8'))
        return m.hexdigest()

    def __repr__(self):
        return f"Node(hash={self.hash[:10]}, data={self.data})"

# ノードを作成
leaf1 = Node(data="block A")
leaf2 = Node(data="block B")
leaf3 = Node(data="block C")

# 同じ子を共有するノード
intermediate1 = Node(children=[leaf1, leaf2])
intermediate2 = Node(children=[leaf2, leaf3])

# DAGのルート
root = Node(children=[intermediate1, intermediate2])

# 表示
print("Merkle DAG:")
print("Root:", root)

$ python3 merkledag.py
Merkle DAG:
Root: Node(hash=b7026ffa9b, data=None)

同じハッシュを持つノードを共有
leaf2 は2つの親ノードに共有されており、DAGならでは構造となっている。IPFSやGitで使われる構造

【Ethereum】erc20とethをswap

pragma solidity ^0.8.0;

interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract SimpleSwap {
    address public owner;
    IERC20 public token;
    uint256 public rate;

    constructor(address _tokenAddress, uint256 _rate) {
        owner = msg.sender;
        token = IERC20(_tokenAddress);
        rate = _rate;
    }

    receive() external payable {
        uint256 tokenAmount = msg.value * rate;
        require(token.transfer(msg.sender, tokenAmount), "Token transfer failed");
    }

    function swapTokenForETH(uint256 tokenAmount) external {
        uint256 ethAmount = tokenAmount / rate;
        require(address(this).balance >= ethAmount, "Not enough ETH in contract");

        require(token.transferFrom(msg.sender, address(this), tokenAmount), "Token transfer failed");

        (bool success, ) = msg.sender.call{value: ethAmount}("");
        require(success, "ETH transfer failed");
    }

    function withdrawETH() external {
        require(msg.sender == owner, "Not owner");
        payable(owner).transfer(address(this).balance);
    }

    function withdrawTokens() external {
        require(msg.sender == owner, "Not owner");
        uint256 balance = tokenBalance();
        require(token.transfer(owner, balance), "Token withdrawal failed");
    }

    function tokenBalance() public view returns (uint256) {
        return token.balanceOf(address(this));
    }
}

【Ethereum】送信者が特定の条件の元、ethを送金

sendではなく、recipient.callで送信してますね。

pragma solidity ^0.8.0;

constract ConditionTransfer {
    address public owner;
    address payable public recipient;
    uint256 public unlockTime;

    constructor(address payable _recipient, uint256 _unlockTime) {
        owner = msg.sender;
        recipient = _recipient;
        unlockTime = _unlockTime;
    }

    receive() external payable {}

    function release() external {
        require(msg.sender == owner, "Only owner can release funds");
        require(block.timestamp >= unlockTime, "Funds are locked");

        uint256 amount = address(this).balance;
        require(amount > 0, "No funds to send");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

【Python】kecchak256

kecchak256は、文字列をランダムな256ビットの16進数にマッピングする機能
ethereumで使用されている

$ pip3 install pysha3

import sha3

data = b'Hello, keccak256!'

keccak = sha3.keccak_256()
keccak.update(data)
hash_hex = keccak.hexdigest()

print("keccak256 hash:", hash_hex)

$ python3 keccak256.py
keccak256 hash: 0fa5c2f72a50589fff02f2f5105ba40d97c01e114df6fd00cc4b1fc0afbec43a

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