Open37

Blockchain学習メモ

Blockchain技術をキャッチアップする。2018年くらいまでは追ってたがその後のスマートコントラクトの隆盛とかNFTについて全然追えてないのでその辺りを中心に。

LINEのBlockchainの資料が全て日本語でかつスマートコントラクトをGUIから作れたりして手軽そうなのでまずはこれを触ってみることにした。ドキュメントで用語の解説とかもしてくれるから初心者でも雰囲気を掴めると思う。

LINE BlockChainサービスの作り方

リソース

チュートリアル手順

  • まずLinkCinemaのチュートリアルをざっとやってみる

準備

  • LINE Businessアカウントでプロバイダーを作成する
  • チャンネルを作成する
    • 審査
      • 2日くらいで通過する
    • BlockChainを選ぶ
  • チャンネルにBlockChainサービスを作成する
    • API KeyとSecretが手に入る。一度しか表示されないのでメモする。
  • サービス用のWalletを作成する
    • Secretが手に入る。一度しか表示されないのでメモする。
  • ServiceToken(=サービス内通貨)を作成する
    • ContractIdが作られる
    • BTC,ETH,XRP,NEMとかみたいなやつのサービス内版
  • ItemToken(=クーポン券とか)を作成する
    • ContractIdが作られる
    • TokenId
      • ItemTokenを発行する時に作成される一意な識別子
      • ContractId+TokenType+TokenIndexを組み合わせて作成される
  • (任意)transactionを取得してみる
    • このPython3のコードをtutorial_request.pyとして保存して使うと良い
    • pip3 install requestsはしておく
    • SERVICE_API_KEYとかSERVICE_API_SECRETは補完してくれ
実際のコード
# walletのトランザクション一覧取得
# SERVER_URL=https://test-api.blockchain.line.me SERVICE_API_KEY= SERVICE_API_SECRET=  WALLET_ADDRESS= python3 tutorial_request.py
 
# 特定のトランザクション取得
# SERVER_URL=https://test-api.blockchain.line.me SERVICE_API_KEY= SERVICE_API_SECRET= TRANSACTION_HASH= python3 tutorial_request.py
 
# NFTの鋳造
# SERVER_URL=https://test-api.blockchain.line.me SERVICE_API_KEY= SERVICE_API_SECRET= OWNER_ADDRESS=  OWNER_SECRET= ITEM_CONTRACT_ID= python3 tutorial_request.py
 
# NFTをユーザーウォレットへ送信
# SERVER_URL=https://test-api.blockchain.line.me SERVICE_API_KEY= SERVICE_API_SECRET= OWNER_ADDRESS=  OWNER_SECRET= ITEM_CONTRACT_ID= RECEPIENT_ADDRESS= python3 tutorial_request.py

import hmac
import hashlib
import base64
import os
import requests
import random
import string
import time
import json


class RequestBodyFlattener:

    def __flatten_key_value(self, key, value):
        if (isinstance(value, str)):
            return f"{key}={value}"

        if (isinstance(value, list)):
            l_key_value = {}
            for index, ele in enumerate(value):
                for lkey in list(ele.keys() | l_key_value.keys()):
                    if lkey in ele.keys():
                        lvalue = ele[lkey]
                    else:
                        lvalue = ""

                    if (lkey in l_key_value.keys()):
                        l_key_value[lkey] = f"{l_key_value[lkey]},{lvalue}"
                    else:
                        l_key_value[lkey] = f"{',' * index}{lvalue}"
            return "&".join("%s=%s" % (f"{key}.{lkey}", lvalue) for (lkey, lvalue) in sorted(l_key_value.items()))

    def flatten(self, body: dict = {}):
        sorted_body = sorted(body.items())
        return "&".join(self.__flatten_key_value(key, value) for (key, value) in sorted_body)


class SignatureGenerator:

    def __createSignTarget(self, method, path, timestamp, nonce, parameters: dict = {}):
        signTarget = f'{nonce}{str(timestamp)}{method}{path}'
        if(len(parameters) > 0):
            signTarget = signTarget + "?"

        return signTarget

    def generate(self, secret: str, method: str, path: str, timestamp: int, nonce: str, query_params: dict = {}, body: dict = {}):
        body_flattener = RequestBodyFlattener()
        all_parameters = {}
        all_parameters.update(query_params)
        all_parameters.update(body)

        signTarget = self.__createSignTarget(method.upper(), path, timestamp, nonce, all_parameters)

        if (len(query_params) > 0):
            signTarget += '&'.join('%s=%s' % (key, value) for (key, value) in query_params.items())

        if (len(body) > 0):
            if (len(query_params) > 0):
                signTarget += "&" + body_flattener.flatten(body)
            else:
                signTarget += body_flattener.flatten(body)

        raw_hmac = hmac.new(bytes(secret, 'utf-8'), bytes(signTarget, 'utf-8'), hashlib.sha512)
        result = base64.b64encode(raw_hmac.digest()).decode('utf-8')

        return result


class LinkMovie:
    # walletのtransactionを10件取得する
    def GET_v1_wallets_walletAddress_transactions(self):
        server_url = os.environ['SERVER_URL']
        service_api_key = os.environ['SERVICE_API_KEY']
        service_api_secret = os.environ['SERVICE_API_SECRET']
        wallet_address = os.environ['WALLET_ADDRESS']

        nonce = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(8))
        timestamp = int(round(time.time() * 1000))

        path = '/v1/wallets/' + str(wallet_address) + '/transactions'

        query_params = {
            'limit': 10,
            'orderBy': 'desc',
            'page': 1
        }

        headers = {
            'service-api-key': service_api_key,
            'nonce': nonce,
            'timestamp': str(timestamp)
        }

        signatureGenerator = SignatureGenerator()
        signature = signatureGenerator.generate(service_api_secret, 'GET', path, timestamp, nonce, query_params)
        headers['signature'] = signature

        res = requests.get(server_url + path, params=query_params, headers=headers)
        data = res.json()
        return json.dumps(data, indent=2)

    # 特定のtransactionを取得する
    def GET_v1_wallets_specific_transactions(self):
        server_url = os.environ['SERVER_URL']
        service_api_key = os.environ['SERVICE_API_KEY']
        service_api_secret = os.environ['SERVICE_API_SECRET']
        transaction_hash = os.environ['TRANSACTION_HASH']

        nonce = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(8))
        timestamp = int(round(time.time() * 1000))

        path = '/v1/transactions/' + str(transaction_hash)

        query_params = {}

        headers = {
            'service-api-key': service_api_key,
            'nonce': nonce,
            'timestamp': str(timestamp)
        }

        signatureGenerator = SignatureGenerator()
        signature = signatureGenerator.generate(service_api_secret, 'GET', path, timestamp, nonce, query_params)
        headers['signature'] = signature

        res = requests.get(server_url + path, params=query_params, headers=headers)
        data = res.json()
        return json.dumps(data, indent=2)

    # 鋳造する
    def POST_v1_item_tokens_contractId_non_fungibles_tokenType_mint(self):
        owner_address = os.environ['OWNER_ADDRESS']
        owner_secret = os.environ['OWNER_SECRET']
        item_contract_id = os.environ['ITEM_CONTRACT_ID']
        server_url = os.environ['SERVER_URL']
        server_url = os.environ['SERVER_URL']
        service_api_key = os.environ['SERVICE_API_KEY']
        service_api_secret = os.environ['SERVICE_API_SECRET']

        nonce = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(8))
        timestamp = int(round(time.time() * 1000))

        path = '/v1/item-tokens/' + str(item_contract_id) + '/non-fungibles/10000001/mint'

        # 自分自身のwalletに送る
        request_body = {
            'ownerAddress': owner_address,
            'ownerSecret': owner_secret,
            'toAddress': owner_address,
            'name': 'MovieTicket',
            'meta': str(round(time.time() * 10000000))
        }

        headers = {
            'service-api-key': service_api_key,
            'nonce': nonce,
            'timestamp': str(timestamp),
            'Content-Type': 'application/json'
        }

        signatureGenerator = SignatureGenerator()
        signature = signatureGenerator.generate(service_api_secret, 'POST', path, timestamp, nonce, body=request_body)
        headers['signature'] = signature

        res = requests.post(server_url + path, headers=headers, json=request_body)
        data = res.json()
        return json.dumps(data, indent=2)

    # OwnerWalletからUserWalletにアイテムトークンを転送
    def POST_v1_wallets_walletAddress_item_tokens_contractId_non_fungibles_tokenType_tokenIndex_transfer(self):
        token_id = '00000001'
        owner_address = os.environ['OWNER_ADDRESS']
        owner_secret = os.environ['OWNER_SECRET']
        item_contract_id = os.environ['ITEM_CONTRACT_ID']
        recepient_address = os.environ['RECEPIENT_ADDRESS']
        server_url = os.environ['SERVER_URL']
        service_api_key = os.environ['SERVICE_API_KEY']
        service_api_secret = os.environ['SERVICE_API_SECRET']

        nonce = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(8))
        timestamp = int(round(time.time() * 1000))

        path = '/v1/wallets/' + str(owner_address) + '/item-tokens/' + str(item_contract_id) + \
            '/non-fungibles/10000001/' + str(token_id) + '/transfer'

        request_body = {
            'walletSecret': owner_secret,
            'toAddress': recepient_address
        }

        headers = {
            'service-api-key': service_api_key,
            'nonce': nonce,
            'timestamp': str(timestamp),
            'Content-Type': 'application/json'
        }

        signatureGenerator = SignatureGenerator()
        signature = signatureGenerator.generate(service_api_secret, 'POST', path, timestamp, nonce, body=request_body)
        headers['signature'] = signature

        res = requests.post(server_url + path, headers=headers, json=request_body)
        data = res.json()
        return json.dumps(data, indent=2)


linkMovie = LinkMovie()
# 雑だけど
if os.environ.get('RECEPIENT_ADDRESS') != None:
    print(linkMovie.POST_v1_wallets_walletAddress_item_tokens_contractId_non_fungibles_tokenType_tokenIndex_transfer())
elif os.environ.get('OWNER_ADDRESS') != None:
    print(linkMovie.POST_v1_item_tokens_contractId_non_fungibles_tokenType_mint())
elif os.environ.get('TRANSACTION_HASH') != None:
    print(linkMovie.GET_v1_wallets_specific_transactions())
else:
    print(linkMovie.GET_v1_wallets_walletAddress_transactions())

NFTの作成

Test Coinの作成

  • NFTの購入などにはCachewというTestNetのTest Coinを使う
  • GUIからのみ作成できる

LINE BITMAX Walletに登録する

  • LINE BITMAX WalletはLINE IDを基盤としたサービスユーザーのためのブロックチェーンウォレット
  • Test CoinやアイテムトークンなどLINE BlockChainのすべてのトークンを扱う
  • テストユーザーに100人まで登録できる。
  • テストユーザーに発行したTest Coinを送信可能

アイテムトークンの購入

  • 送金
    • ユーザーWallet -> サービスWallet
  • アイテムの転送
    • サービスWallet -> ユーザーWallet

サービストークンの付与

  • アイテムトークンの転送同様、API or GUI(assets -> details -> Sendページ)から特定のアドレスを指定して送信する

FT

  • 上記NFTの場合と基本的に操作でできる。性質や利用シーンが異なるだけ。

Composableトークン

  • 親子関係を結んだトークンをいいます。親と子は1対多で紐付き、このように紐付けられたトークンはまるで1つのトークンのように取り扱うことができます。代表例が、キャラクターに装備を装着することです。キャラクターといろんな装備がそれぞれのトークンであれば、キャラクターは親、各装備は子になります。 Composableトークンで親子関係になるのようにすることを結合(attach)といい、関係を断つことを分離(detach)といいます。

  • アイテムトークンを2つ(A,B)用意する。親になるアイテムトークンAに、子になるアイテムトークンBをAPIで結合する。
  • 転送は親のアイテムトークンを送るだけで良い。

LINEのBlockchainで雰囲気が掴めたのでスマートコントラクトの中心となる本家Ethereumのドキュメントを一通り目を通す。既知の内容も多いので気になったことだけメモした。

Ethereum

リソース

やること

  • Ethereumの全貌を把握する

メモ

  • EVM

    • Ethereum Virtual Machine, or EVM
    • いわゆるEthereumのブロックチェーン本体のネットワーク
  • EVM storage

    • Ethereumを使ったアプリのコード(dapps)をアップする場所
  • ガス代

    • Ethereumでのトランザクションに必要となる計算力の総量に比例してかかるコスト
      • 悪意のあるdappsが無限ループとかを含めて実行しようとしても実行側にめっちゃコストがかかるのでそういった攻撃に抑止力がある
  • 単位

    • 実用的にはETHより小さい単位の方が使いやすいのでWeiとGWeiが使われたりする
      • Wei: 10**-18
      • GWei: 10**-9
  • 取引履歴

  • dApps

    • P2Pネットワーク上で実行されるアプリ
    • Web2とWeb3
      • Web2.0の次としてのWeb3.0。非中央集権の分散型BlockChain上に構築されるインターネット。dAppsはWeb3.0のアプリ。
  • アカウントの種類 => Walletとは違う。Walletはトランザクションを作ったりアカウントを管理するやつ。

    • 個人所有のもの
      • private keyを持った個人のやつ
      • 個人間送金とかで使う
    • コントラクト
      • smart contractのやつ
    • Ethereumネットワーク上の「個人」と「法人」みたいな違い。(で合ってる?)
  • transaction

    • ユーザーがblockをtransactionを作成 -> poolされてネットワークにbroadcastされる -> minerに検証される -> chainに追加される
  • ブロック

    • transactionのまとまり。Ethereumだと15秒ごとにコミットされる。
  • EVM

    • state machine: Y(S, T)= S' (S=old state, T=new transaction, S'=new state)
    • state: modified Merkle Patricia Trieのデータ構造を利用してる
    • Image
  • Gas

    • ガス代: ネットワーク上のスパムや脅威を防ぐために必要
    • 例(LONDON Upgrade前)
      • Let's say Alice had to pay Bob 1 ETH. In the transaction the gas limit is 21,000 units and the gas price is 200 gwei.

      • Total fee would have been: Gas units (limit) * Gas price per unit i.e 21,000 * 200 = 4,200,000 gwei or 0.0042 ETH>

      • When Alice sent the money, 1.0042 ETH would be deducted from Alice's account. Bob would be credited 1.0000 ETH. Miner would receive 0.0042 ETH.

    • 例(LONDON Upgrade後)
      • Calculating the total transaction fee works as follows: Gas units (limit) * (Base fee + Tip)

      • Let’s say Jordan has to pay Taylor 1 ETH. In the transaction the gas limit is 21,000 units and the base fee is 100 gwei. Jordan includes a tip of 10 gwei.

      • Using the formula above we can calculate this as 21,000 * (100 + 10) = 2,310,000 gwei or 0.00231 ETH.

      • When Jordan sends the money, 1.00231 ETH will be deducted from Jordan's account. Taylor will be credited 1.0000 ETH. Miner receives the tip of 0.00021 ETH. Base fee of 0.0021 ETH is burned.

      • Additionally, Jordan can also set a max fee (maxFeePerGas) for the transaction. The difference between the max fee and the actual fee is refunded to Jordan, i.e. refund = max fee - (base fee + priority fee). Jordan can set a maximum amount to pay for the transaction to execute and not worry about overpaying "beyond" the base fee when the transaction is executed.

    • 21000 units of gasが必要なのに20000 units of gasしか指定しなかった場合は20000だけminerに消費されるがtransactionは失敗され戻ってこない。
    • なぜガス代が上がるのか?
      • トランザクション数がめっちゃ増えてるから。処理能力に限りがあるから早く処理してもらうために高いfeeを払う必要がある。
    • Ethereum 2.0でPoS(Proof of Stake)が導入されるとガス代は下がるはず
    • Ethereum2.0のPoS
      • 32ETH以上(=1000万円くらい)持ってる人がminerになれる
  • Node

    • 分散ネットワークのNode。具体的には世界中の人たちのPCとかサーバーとかそれぞれ。
    • サービスとしてのノードというのもある
      • 自前でNodeを持たずにサービスプロバイダのAPIやGUIを使ってNodeを利用できる。PasSとかIasSに近いのかな。
      • 主なサービス一覧はここ
  • ネットワーク

    • Public
      • MainNetとTestNetがある
      • dappsは必ずTestNetでデプロイして調整する必要あり。
    • Private
      • Development Network
  • Ethereumアプリ開発をする時の技術スタック

  • 言語

    • Solidity
      • C++,Javascripライク
      • リソースが一番多い。
    • Vyper
      • Pythonライク
    • Yul
      • EVMの中間表現
      • 上級者向け
    • Fe
  • スマートコントラクトプログラミングの全容

OpenseaにEthereumとPolygonという通貨が対応してて気になった。Ethereum完全互換のブロックチェーンのMatic Networkがサイドチェーンとして動いてるらしい。よくわかってない部分や勘違いしてる部分が結構ありそう。

Polygonメモ

実際にスマートコントラクトのHello Worldをやってみる。

smart contract作成メモ

リソース

やること

  • ユーザーとblockchain間で読み書きを行うSmart Contractを作成する

使うもの

  • react
  • metamask
    • wallet
  • ether.js
    • Ethereum用のクライアントライブラリ
  • hardhat
    • Ethereumの開発環境
  • ABI

流れ

ローカルネット

  • HardhatでローカルBlockchain Nodeを立ち上げる
  • ローカルテスト用のAccount&private keyが20個ほど作成される
  • hardhatでSolidityで書いたスマートコントラクトをコンパイルする
    • ABIが生成される
  • スマートコントラクトをデプロイスクリプトに追加する
  • スマートコントラクトをHardhatにデプロイする
  • Walletにローカルテスト用のAccountをimportする
  • React AppとそのAccountをWallet経由で接続し、ether.jsでHardhat側のスマートコントラクトとインタラクションを行う

テストネット

  • テストネットを選択する: Ropstenとか
  • ガス代を払うためにTest Etherを取得する
    • RopstenはどのTest Ether供給サイトもスパムにやられてるのか機能してないっぽい
      • 1日経過したら追加された!
  • Infura or Alchemy(テストネットへ接続するためのSaaSみたいなやつ)に登録。プロジェクトを作成してproxy linkを生成。自分のwalletアドレスの接続だけを受け付けるようにwhitelistに設定もしておく。
  • 取得したproxy linkとAccountの秘密鍵(walletからのエクスポート版)をhardhat.config.jsに設定する
  • hardhatコマンドのdeployコマンドでtestnetを指定してデプロイする

Hardhatにある公式のTutorialもやってみる。内容的には先のやつとほぼ変わらんが解説が少し丁寧。

Hardhat tutorialメモ

リソース

やること

  • hardhatを使った開発の流れを学ぶ
  • hardhatの仕組み等をempty projectから学ぶ

メモ

  • hardhatはtaskとpluginで構成されている
    • task
      • hardhat compileとか
    • plugin
      • ether.jsとかwaffleとか好きに入れ替えて使える
  • このtutorialだとHardhatをinitした時にempty projectから始めるのでbasic projectで初回から用意されているファイルなど構成の意味が深掘りされてわかるようになる。
  • Waffleとchaiを使ったテストについての方法も解説されてるのはよかった。
  • dAppのHackathon用に作られたawesome projectのリポジトリサンプル

simple nft exampleというnftアプリ開発のスマートコントラクトとフロントエンドが一緒になったtemplateがあった。

simple nft exampleメモ

リソース

やること

  • NFTのHello Worldより発展的なサンプルを読んでみる

メモ

simple-nft-exampleのコードリーディング

  • NFTをAccount同士で送り合えるスマートコントラクトとReactで作られたフロントエンドアプリ
  • react,hooks,graphql,ether.js,web3-modal,solidity,hardhat,infuraが使われてる
    • web3-modal
    • 自分はReact16.8以前までしか使ってないからhooksをよくわかってないっぽい
      • フック API リファレンス – React
        • ルール
          • フックは関数のトップレベルのみで呼び出す
          • Reactコンポーネント内部でのみ呼び出す
        • useState
          • stateとsetStateを返す
          • setStateでstateを更新するとstateをlistenしてるコンポーネントが更新される
          • FlutterでいうとStateNotifierみたいなやつ
        • useEffect
          • マウント時と更新時に実行される副作用(何かロードしたり色々)
          • 第二引数にstateを指定するとそのstateの更新時のみ実行されるようになる。空配列を指定すると全ての更新時に実行されなくなる。
          • callbackをreturnするとdispose時に実行してくれる。unsubscribeとかする時に便利。
          • FlutterでいうところのライフサイクルのinitStateやdidChangedDependenciesやdiposeの代わりになるやつ
        • useContext
          • そのコンポーネントのcontextを取得する。contextが変更されると更新される。
          • FlutterでいうところのriverpodのConsumerとその配下の更新の関係に似てる?
        • useReducer
          • reduxのreducer+useStateみたいなやつ。
          • Flutterに該当するやつはなさそう。
        • useMemo
          • メモ化された値を返す。
          • FlutterでいうところのGetterみたいなもんか?
        • useCallback
          • メモ化されたコールバックを返す。useMemoの関数版。使い所がよくわからん。
          • Flutterに該当するやつはなさそう。
        • useRef
          • 書き換え可能な値を保持するやつ。DOMへのアクセスを保持するのによく使う。inputのrefとか。
          • Flutterに該当するやつはない
        • useImperativeHandle
          • なんか色々カオスなことができそうなやつ。使うことはなさそう。
          • Flutterに該当するやつはない
        • useLayoutEffect
          • ほぼuseEffectと同じ。DOMの変更後に同期的に副作用が呼び出される点がuseEffectと異なる。
          • Flutterに該当するやつはない
        • useDebugValue
          • devtoolにラベルを表示できるやつ。
          • Flutterに該当するやつはない
        • カスタムフック
          • use始まりの関数。ロジックの共通化に便利。カスタムフック内のstateはそのフック内に閉じている。
    • Solidity
      • Solidity by Example | 0.8.3
        • Solidityの文法から特有の記法、主な攻撃コード例までコードサンプルがまとまってて良い。上から順に見ていくだけでプログラミングに親しんでる人には大体読める。
        • 気になったところ
    • ERC20
    • ERC721
    • 実際nft-scaffoldについて学ぶのが中心になってしまう。もうちょっとゼロから書いて理解したい。

最小限のコードでIPFSにアップロードされたファイルをERC721に則ってNFTとして鋳造しRopstenへのデプロイ、Walletでの確認を行うTutorial。これを元に拡張して行くことができそう。

ERC720に則ったコントラクトのチュートリアル

リソース

やること

  • ERC720に則ったスマートコントラクトのropstenへのデプロイ

メモ

Tutorial1 - testnetへコントラクトのデプロイまで

  • Alchemyの登録とAppの作成
  • metamaskのWallet作成
  • testnetへのデプロイ用にfaucetからテストETHを取得
  • プロジェクト作成
mkdir my-nft
cd my-nft
npm init
hardhatで開発環境構築(Create an empty hardhat.config.jsを選択)
npm install --save-dev hardhat
npx hardhat
mkdir contracts
mkdir scripts
Contractを書く
npm install @openzeppelin/contracts
code contracts/MyNFT.sol
//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract MyNFT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    constructor() public ERC721("MyNFT", "NFT") {}
    function mintNFT(address recipient, string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        _tokenIds.increment();
 
        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);
 
        return newItemId;
    }
}
WalletのPRIVATE_KEYとAlchemyのAPI_KEYを秘匿情報として.envに追加
npm install dotenv --save
code .env
API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"
hardhatでtestnetにデプロイする準備1
npm install --save-dev @nomiclabs/hardhat-ethers 'ethers@^5.0.0'\n
code hardhat.config.js
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require('dotenv').config();
require("@nomiclabs/hardhat-ethers");
const { API_URL, PRIVATE_KEY } = process.env;
module.exports = {
   solidity: "0.8.0",
   defaultNetwork: "ropsten",
   networks: {
      hardhat: {},
      ropsten: {
         url: API_URL,
         accounts: [`0x${PRIVATE_KEY}`]
      }
   },
}
hardhatでtestnetにデプロイする準備2
code scripts/deploy.js
async function main() {
  const MyNFT = await ethers.getContractFactory("MyNFT")
  const myNFT = await MyNFT.deploy()
  console.log("Contract deployed to address:", myNFT.address)
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error)
    process.exit(1)
  })
  • デプロイ
npx hardhat compile
npx hardhat run scripts/deploy.js --network ropsten

Tutorial2 - 電子データをIPFSにアップロードしてそのファイルをNFTとしてコントラクトにmintする

  • Pinataにアカウント作成
  • NFT化したいファイルをアップロードする
  • web3のインストール
npm install @alch/alchemy-web3
自分のアカウントアドレスを.envに追記
code .env
API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"
PUBLIC_KEY = "your-metamask-public-address"
nftの作成をするコードの準備
code scripts/mint-nft.js
require("dotenv").config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
 
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
 
// 先ほどデプロイ済みのコントラクトのAddress
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "<your contract address>";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
 
async function mintNFT(tokenURI) {
  const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest");
  const tx = {
    from: PUBLIC_KEY,
    to: contractAddress,
    nonce: nonce,
    gas: 500000,
    data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(),
  };
 
  const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
  signPromise
    .then((signedTx) => {
      web3.eth.sendSignedTransaction(
        signedTx.rawTransaction,
        function (err, hash) {
          if (!err) {
            console.log(
              "The hash of your transaction is: ",
              hash,
              "\nCheck Alchemy's Mempool to view the status of your transaction!"
            );
          } else {
            console.log(
              "Something went wrong when submitting your transaction:",
              err
            );
          }
        }
      );
    })
    .catch((err) => {
      console.log(" Promise failed:", err);
    });
}
 
mintNFT(
  "https://gateway.pinata.cloud/ipfs/<ipfs-hash>"
);
mintする
node scripts/mint-nft.js
成功したか確認

Tutorial3 - mintしたNFTをwalletで閲覧する

  • MetaMaskのmobile版をDLする
  • コントラクトをデプロイしたアカウントでログインする
  • Ropsten Networkに切り替える
  • NFTタブのNFTを追加へ先ほどデプロイしたスマートコントラクトのAddressとNFTのTokenIDを入力する

MetaMaskでログインさせる実装はWeb3に接続するどのサイトにも必要だと思うのでやってみる。ちょうどいいサンプルがあったからやってみた。結論からいうとWeb3とReactのエコシステムが発展しまくっており楽に実装できすぎて最高だった。Chakra UIも初めて触ったがコンポーネントが全て揃っててUI組むのがFlutterっぽくてすぐ手に馴染みそうだなと感じる。

MetaMaskでログインするフロントエンドのメモ

リソース

やること

  • MetaMaskでログインするフロントエンドをReact+Chakra UIで作る

メモ

コントラクトのセキュリティの知識も入れたかったので調べた。外部コントラクトを呼び出すことに起因する脆弱性が多い印象。

スマートコントラクトのセキュリティメモ

リソース

やること

  • スマートコントラクトのセキュリティ関連のキャッチアップ

メモ

  • スマートコントラクト におけるセキュリティは適切な設計と開発プロセスの時点で始めないといけない
    • ここでいう開発プロセスとは?
      • gitによるバージョン管理
      • PRベースの変更
      • 1人以上のコードレビュアーを設置
      • コンパイル・デプロイ・テストを一つのコマンドで実行できるようにする
      • MythrilSlitherのようなSolidityやEVMのバイトコードの静的解析ツールを使用する
      • SolidityのWarningは消す
      • ドキュメントを書く
    • さらに航空産業で長年培われた品質管理プロセスもソフトウェアのレビューに活かせるだろうとのこと

代表的な攻撃手法

  • Re-entrancy(リエントラント)
    • スマートコントラクトが外部のスマートコントラクトを呼び出してるいる時はメモリの状態とプロセスを一時停止させてしまう仕様(再代入可能)とdefault fallback functionの仕組みを突いて、脆弱性のあるスマートコントラクトから攻撃者のガス代がなくなるまでETHを無限に引き出し続けることができてしまう攻撃
      • 被害者に攻撃者の口座に預金命令 -> 攻撃者から同額を引き出し命令 -> 被害者からの同額を送金 -> その送金をフックに攻撃者のdefalt fallback functionが起動 -> 再び攻撃者からの引き出し命令が実行 -> 被害者からの送金 ....
    • 対策
      • 攻撃者への送金前に攻撃者の預金を0にしてから送金する
      • msg.sender.call.valueを使わないでsendtransferを使う
      • そもそもuntrustedなコントラクトの実行やそうしたコントラクトへETHの送信をしないように設計すれば防げる
    • 再エントラシー攻撃とは何ですか?
  • Front-Running(フロントランニング)
  • Timestamp Dependence
  • Integer Overflow and Underflow
  • DoS with Failed Call
    • 外部呼び出しのコントラクトを意図的に失敗させて無限ループを起こしDoSを引き起こす攻撃
    • 対策
      • 複数のコントラクトの呼び出しを控える
      • 外部コントラクトの呼び出し失敗時の処理を書く
  • DoS with Block Gas Limit
    • ガス代limitを超えた場合にtransactionが失敗することを利用した攻撃
  • Insufficient gas griefing
    • contract内でのsub contract(or function)の実行に必要なガス代が足りずにtransaction全体を失敗もしくは再実行させてしてしまう嫌がらせ
    • 対策
      • sub contractの方でガス代の検証を行う
      • 信頼したsub contractのみ実行するようにする
  • Forcibly Sending Ether to a Contract

とりあえず手を動かすのは一旦気が済んだ。以降しばらくは腰を据えてレポートや本などを読んでいくことにする。

ブロックチェーンと暗号資産に関するセキュリティについてのレポートのメモ

リソース

やること

  • ブロックチェーンに関して交わされている議論を追う

メモ

  • 暗号資産関連のインシデントは暗号資産取引所から不正にユーザーの署名鍵が流出して発生することが一番多い。署名鍵をユーザーが個別で管理できていれば良いが現実的には無理があり、取引所が代わりに預かり管理していたりする。このため取引所がクラックされる可能性(=単一障害点)になってしまっている。
    • やはり被害額ではcoincheckが群を抜いている...
    • Image
  • ソフトウェアの脆弱性が存在すると発覚した時に「正しく行動するとあらかじめわかっている、全体のハッシュパワーの 51%以上のハッシュパワーをもつマイナー」を事前に特定することは難しい。強制的にチェーンの分岐を行う際のガバナンスは課題。
  • 攻撃
    • 51%攻撃
    • Finney Attack
    • Brute Force Attack
    • Selfish Mining Attack
    • Sybil Attack
  • Blockchainに基づく暗号資産システム全体におけるセキュリティ目標を達成するための 6 つのレイヤー
    • 暗号技術レイヤー
      • ハッシュ関数・電子署名
      • 米国連邦政府標準を定めるNIST が、量子計算機が存在しても安全な暗号アルゴリズムに関する標準化に向けたコンペティションを行っている。

    • バックボーンプロトコル
      • ブロックチェーンの分散合意アルゴリズムなどの安全性証明
      • 51%攻撃を防ぐインセンティブ設計=ゲーム理論サイドからの研究中である課題
      • P2Pネットワーク自体の脆弱性を突いて全体の参加者の50%を操作して二重支払いを可能にする問題
    • アプリケーションプロトコル
      • トランザクション処理やスケーラビリティに関わる問題
      • Layer2技術を使ったライトニングネットワークがSPOFになる可能性
        • トラフィックの偏りの問題があり、ビットコインと同等の信頼モデルを実現できない
      • トランザクションとそこで使われた暗号資産のプライバシ保護。お金に色なし vs マネーロンダリング対策
    • アプリケーションロジック
      • 決済・その他ビジネスロジック(=帳簿の書き換え)の設計
      • 主にSolidityによるスマートコントラクトのバグ(Reentracy Attackなど)が主なインシデントの原因
      • 形式検証で事前に攻撃可能な状態遷移を検知する
    • 実装レイヤー
    • 運用レイヤー
      • システム全体を人含めてどう運用・監視するか
      • 非中央集権的なコミュニティでポリシー等を規定するのが難しい問題がある
      • 現実には、暗号資産取引所のシステムは、それぞれの取引所が独自の実装をしており、共通のアーキテクチャや安全性が確認された実装が存在しないというのが現状である

  • トレードオフ
    • スケーラビリティとセキュリティのトレードオフ
      • スケーラビリティを向上させるためには、単純にはブロックサイズを増やせばよいが、ブロックサイズを増やすと各ノードで記録すべき情報量が増えるために、マイニングを行うフルノードの運営コストが増加する。その結果全体のハッシュパワーが減少し、パーミッションレス・ブロックチェーンにおける 51%攻撃の成功確率が上がる

  • 暗号技術レイヤーやバックボーンレイヤーはだいぶ研究が進んでいるが、スケーラビリティを得るためのLayer 2 技術やより広い応用のためのスマートコントラクトのセキュリティについてはまだ今後の研究が必要

ニューノーマル時代における人間の社会活動を支える情報基盤の在り方とデジタルアイデンティティの位置づけのメモ

リソース

やること

  • blockchainと現実社会をつなげる構想についてのレポートを読む

メモ

  • 人間文化を尊重したデジタルアイデンティティの確立
  • トラスト
    • 信頼。維持コストと失う時のリスクが存在する。
  • オンライン中心になると物理空間でうまいことリスクとコスト調整してたコミュニケーションがしづらくなる。
    • フェイクニュースの例を挙げていたが物理空間でも同じでは。
    • 出会い系サイトでの個人と実際に会った時の差みたいなものか?
  • オンラインでも物理空間同様のトラストを形成を構築できる方法を模索したい。
  • オンラインでのコミュニケーションにおいて法的責任を果たすためには
    • 1)コミュニケーションのゴールが、参加者において共通の理解として確認されていること。
    • 2)コミュニケーションに関わるエンティティ(人間、及び機械)が認証されていること
      • 既存のインターネットの認証機構
    • 3)エンティティ間で送受信されるデータが改竄されておらず、作成者が認証されていること
      • 既存のインターネットの認証機構
    • 4)コミュニケーションに関わる、データ送信、エンティティによるデータ入出力、及びデータ加工などのプロセスの前後関係が、特定可能であり、また検証可能な形で記録されること。
      • パーミッションレスブロックチェーン
    • 5)コミュニケーションにおいて、人間が介在する全ての段階で、その時点の状態に対して、全ての人が同じ理解をしていることを逐次確認できること。その結果として、コミュニケーションの最終結果は、同じ理解のもとになされた結果であることを事後で確認できること。
  • 現状のデジタルアイデンティティの課題
    • 現在はサービス内に閉じたデジタルアイデンティティしかない(ex. TwitterでのユーザーやFacebookでのユーザーは別物として管理されてる)
  • ブロックチェーンを利用してGIDと個人を紐付けサービス問わずシームレスなデジタルアイデンティティを確立する
  • 具体的にはデジタルウォレットを中心に既存の認証やブロックチェーンの世界、物理空間の公的証明やマイナンバー的なものもふくめて統合していく構想がある。

官公庁のレポート色々を読んでのメモ

リソース

やること

  • 官公庁や民間のリサーチ会社のレポートをざっと目を通す

メモ

  • デジタル市場競争に係る中期展望レポート/デジタル市場競争会議(内閣官房)
    • 変化の速いデジタル市場(=デジタルと関わる既存産業含めほぼ全て)における競争の促進を将来への展望を見据えて考える
    • インターネットはネットワーク効果が働きメガプラットフォームが生まれやすい = 独占・寡占に陥りやすい
      • 同時にプライバシーの懸念/リスクが生まれた
    • デジタル上でのデータの質や信頼性の確保に課題
    • Society5.0
      • なにこれ....
    • トラストをベースとしたデジタル市場
    • 後半はDXが〜とかIoTが〜みたいな話が続いて疲れてくる
    • メガプラットフォームによる中央集権的なデータの集中を避け個人や法人がそのコントロール及びマネージを担い、Web3.0(分散型のデータガバナンスのレイヤー)の構築を目指す
      • ここにBlockchainが出てくる
      • => Trusted Web
  • 通貨と銀行の将来を考える研究会(中間報告)/野村総合研究所
    • CBDC(=中央銀行デジタル通貨)
    • 銀行券利用の減少
    • 日本の家計の支払い手段、クレカ30%現金38%電子マネー4%その他預金という感じか
    • 暗号資産を通じた国間での送金サービス
    • bitcoinは価値変動がデカすぎるのでstable coinは利用可能性が存在する。またBlockchainは支払いの面で速度の限界も存在する。
    • 中央銀行マネーの要件
      • 偽造や不正の抑制
      • 決済の即時完了性
    • 誰でも使える
    • いつでも使える
      • CBDCと仮想通貨の共存時のセキュリティや認証方法の確保
    • CBDCは日銀と市中銀行との関係のように中央銀行と仲介者が間に挟まって利用者に払込みや受け渡しがされる仕組みを想定
      • 誰が仲介者をやるか?民間企業にはコストがデカすぎて担うインセンティブがなさそう
        • 利用者の認証と取引の確認、支払い・決済登録の業務を分離すればインセンティブがやや緩和されるか
      • そのまま市中銀行が担えば良いのでは派が日銀内でも意見が割れている
    • CBDCに現金代替の機能を持たせるか否か。再考の余地あり。
  • 未経験から1年でキャッチアップしたブロックチェーンの学習法をまとめてみた - Gaudiy Tech Blogには他にもいくつか書いてあったけどどれも今の自分にはあまり興味を持てないものだったのであとは軽く目を通すにとどめた。

ブロックチェーンの活用例が知りたくてちょうど良さそうな本を読んだ。

ブロックチェーンがひらく「あたらしい経済」のメモ

リソース

やること

  • 実際の応用例や構想されてることを知る

メモ

  • 活用の順序としては、トレーサビリティ->コスト削減->金融->あらゆるもののトークン化->人の関係性の可視化
  • 応用例(~2020年前半あたり時点)
    • 地域コイン
    • コミュニティコイン
    • 電力の産地価値証明
    • サプライチェーンのトレーサビリティ
    • 二酸化炭素排出量の可視化とカーボンオフセット
    • 不動産の権利管理
    • 学歴や資格の記録
    • 記事の価値可視化
      • ALIS
    • IoT機器のプログラム保証
    • 人に紐づく履歴管理
    • 契約の自動化、契約書の共有・保管・電子契約
    • 行政サービス利用手続きのペーパーレス化
    • 人と人との関係性の可視化
  • 無形の物なら出来そうだけど有形な物だと時間と共に劣化を避けられないので所有権が移った時点で相手に受け渡せないなら対等な交換となりうるのか疑問。
  • 証券→現金→支払いとトークン→支払いで手数は減ってるように見えるがどちらも決済手数料はかかるし、トークンでの支払いが可能なように小売店等を整備するコストを考えると証券→現金のほうが汎用性あってよくない?となる
  • 不動産の所有のトークン化は技術的に所有の移転を簡易化できるとしても既存の不動産会社を排除することが一番大変そう。不動産業界のイノベーションは技術よりも既存の慣習とか人の方にあるのでは。
  • STO(Security Token Offering)
    • 金融機関から見るSTO(Security Token Offering)の現状と課題
      • ほふりとは
      • 従来のほふりをブロックチェーンに変えるメリットは?
        • ブロックチェーンを利用することにより、今まで困難であった投資家の把握が随時可能となったことが挙げられる。従来のほふりの管理は階層構造であるが故にほふりが最終投資家を把握していないことや、投資家の匿名性の尊重に由来し、発行体の求めに応じ投資家の状況を還元することは不可能であったが、STOの場合、社債券の不発行との枠組みを用いることで、社債原簿(後述)を介し発行体への随時還元が可能となった。発行体の中には、投資家とのコミュニケーションの活性化や金銭以外のリターンの提供、長期投資家の優遇といった点で意義を感じる向きも多いようだ。

  • 便利さは麻薬
  • 将来の展望についての話(主に3章)は「OOOがブロックチェーンで可能になるかもしれません」という言葉がよく出てくるがどう可能になるのかがイマイチ根拠がなくて過大評価しすぎじゃん..となる
  • トレーサビリティについても結局チェーンに記録するところ(=オラクル)の信頼性をどうやって担保するんですか?という点が付き纏う例が多い気がする。
  • コミュニティコインやトークン
    • サウナコインの例。ゲーマーやアイドルコミュニティへの応用もできるか。
  • ブロックチェーンに記述された情報から販売元などの製造過程を閲覧して購入する。消費者独自でフェアトレード。
  • ブロックチェーンの導入は公平公正見える化がなされるゆえに部分最適->全体最適圧が強い。部分最適で利益を取っていた人々にとっては抵抗が強い。よってブロックチェーンの流行は妨げられやすい。
  • GBEC
  • 中国のCBDCが実証実験開始
  • あたらしい経済 NEW ECONOMY|幻冬舎のブロックチェーン・仮想通貨(暗号資産)情報サイト

EthereumのWhitePaperのメモ

リソース

やること

  • EthereumのWhitepaperに目を通す

メモ

  • 最初のIntroductionでBitcoinの問題点(チューリング完全性の欠如・値が定まらない問題・状態の欠如・Blockchain が見えない問題)を挙げてるところとMiscellanea And Concerns以外はEthereumメモで書いたように公式のEthereumドキュメントを読むのと変わらなかった。

EIP1559のメモ

リソース

やること

  • EIP1559とは

メモ

  • ETHのガス代の決め方についての変更
  • 従来はオークション形式で決められていた
    • ネットワークが混雑しているときはすぐ高騰する。
    • ブラインドオークション形式ではガス代が予測しづらい
  • 今後はシステム的に自動で決められるbase fee + priority feeになる
    • ネットワークの混雑度によって自動的に決まるbase fee
      • 前のblockのサイズによってbase feeは決まる
        • ネットワークの混雑度に応じてブロックサイズは可変。EIP1559以前は固定だった。
      • base feeは+-12.5%で自動的に調整される
    • 加えてユーザーが好きに決められるpriority fee
      • これを自分のトランザクションの優先度に合わせて好きに設定できる
    • base feeは従来方式と違い1分単位で急激に変わったりしないので予測が立てやすくなるし、望みの価格になるまで待ちやすくもなる
    • base feeはマイナーに分配されず焼却される
      • そうでなければ意図的にネットワークを混雑させてbase feeを高止まりさせることができるので
  • 何が嬉しい?
    • ETHのガス代をより予測しやすくなる
  • ガス代は下がるの?
    • 下がらない。ガス代はネットワークの混雑が存在する(=トランザクションの需要が多い)限り下がりはしない。

ポルカドット

リソース

やること

  • ポルカドットが何か調べる

メモ

  • ドキュメントだけだと???って感じになるが↑の動画を見るとイメージ湧きやすい
  • いくつものBlockchainネットワークをRelay Chain(=ポルカドットのコアとなるチェーン)で接続するBlockchain Hubみたいなもの
    • Parachain
      • Relay Chainに接続するBlockChain
  • Relay ChainにはSmart Contractをデプロイできない
  • SubstrateでBlockchainを作成してPolkadotにParachainとして接続して、そのBlockchain上でSmart Contractを実行したりすることは可能
    • そうするとBitcoinをRelay Chainを介して自分の作ったParachain上で使えたりするようになる、というのが嬉しいポイントなのかな
    • ちなみにSubstrateはBlockchainを作成するための開発フレームワーク。Polkadotが公式に採用してる。

Layer2

リソース

やること

  • Layer2 is 何

メモ

  • EthereumのMain Chainのスケーリング問題に対応するために考えられた方法
  • Mainnetと別にLayer2のchainを利用して計算とデータの保持を分けるなどしてスケーリング問題の解決を図る

Rollups

リソース

やること

メモ

  • Ethereumの最新のLayer2技術
  • ChannelとSide Chainのハイブリッド
    • Channel
    • SideChain
      • EVMを持つ。Layer1のほぼコピーみたいな感じ。
      • 独自のコンセンサスアルゴリズムも持つ
      • Polygonはこれ
  • 計算はLayer2で行うがトランザクションデータはLayer1に保持する
  • コンセンサスはLayer1を使うのでセキュア
  • 計算はLayer2になるのでトランザクションスピードも上がりスケーリングしやすくなる
  • Layer2で行われたトランザクションはバッチでまとめて圧縮されLayer1へ送られる
  • 送られたバッチはどのようにしてLayer1側でvalidと検証されるのか?
    • Optimistic rollups
      • flaud proofs
        • Layer2からLayer1へ送られたデータはvalidであると仮定する。
        • Layer1にsubmitするグループはあらかじめ預託金(ETH)をdepostしておかないといけない
        • Layer1側のsmart contractではトランザクションデータのバッチを監視する機能がある
        • もし不正なトランザクションデータを送ってその監視で引っかかった場合、それを送ったグループの預託金は全て没収される。
      • submitされたデータがLayer1に追加されるまでに最悪1週間くらいかかる可能性がある
    • ZK rollups
      • validity proofs
        • ゼロ知識証明(Zero Knowledge Proof)を利用する
        • トランザクションデータのバッチの中にZK Snarkと呼ばれる暗号文を仕込む
        • Layer1側のsmart contractで検証をする
  • 総じてSideChainよりもgas feeは高い。Layer1のChainにバッチをブロックとして追加しないといけないので..。
  • なぜLayer2ではgas feeを抑えることができるのか
    • PlasmaとChannelsは、データと計算の両方をオフチェーンに移動させようとするという点で、「完全な」レイヤー2方式です。しかし、データの利用可能性に関する基本的なゲーム理論上の問題から、すべてのアプリケーションでこれを安全に行うことは不可能です。PlasmaとChannelは、所有者という明示的な概念に頼ることでこの問題を回避していますが、これが完全な一般性を阻んでいます。一方、ロールアップは、「ハイブリッド」なレイヤー2方式です。Rollupsは、計算(および状態の保存)をオフチェーンにしますが、トランザクションごとのデータをオンチェーンにします。効率を上げるために、可能な限りデータを計算に置き換えるために、様々な圧縮技術を使用しています。その結果、スケーラビリティはブロックチェーンのデータ帯域幅によって制限されますが、非常に有利な比率となっています。EthereumのベースレイヤーのERC20トークンの転送には45,000ガスのコストがかかりますが、ロールアップのERC20トークンの転送はオンチェーンで16バイトのスペースを使用し、コストは300ガス以下です。

    • データがオンチェーンであることが重要です(注:「IPFS上」にデータを置いても機能しません。IPFSではデータが利用可能かどうかのコンセンサスが得られないため、データはブロックチェーン上に置く必要があります)。ブロックチェーン上にデータを置き、その事実についてコンセンサスを得ることで、誰もが望めば、ロールアップされたすべてのオペレーションをローカルに処理することができ、不正行為を検知したり、引き出しを開始したり、個人的にトランザクションバッチの生成を開始したりすることが可能になります。データの利用可能性の問題がないということは、悪意のあるオペレーターやオフラインのオペレーターによる被害がさらに少なくなり(例えば、1週間の遅延を引き起こすことはできません)、誰がバッチを発行する権利を持っているかについて、より広い設計空間を開くことができ、ロールアップの推論が非常に容易になります。そして最も重要なことは、データ利用可能性の問題がないということは、アセットをオーナーにマッピングする必要がないということであり、Ethereumコミュニティがこれまでのレイヤー2スケーリングの形態よりもロールアップに大きな期待を寄せている主な理由につながります。

LINE BlockchainにおいてサービスA(Aさんのサービス)で購入したNFT1とサービスB(Bさんのサービス)で購入したNFT2を同時に取得する方法はあるか?

リソース

やること

  • LINE BlockchainのNFTの取得一覧の制約について調べる

メモ

  • 異なるownerが作成しているサービス(=別のネットワーク)間での横断検索的な話
  • 全体のnon-fungibleアイテムトークンの残高を取得するのAPIではcontractIDが必須になっている。contractIDはサービスごとに一つのID。つまりAPI的にはNFT取得にはネットワーク毎という縛りがある。よってネットワーク横断でNFTを一括取得する方法はない。
  • ユーザーがNFTを保持している複数サービス(別々のownerの作ったやつ)のcontractIDを全て取得できればいいがそれは出来なそう

Immutable X

リソース

やること

  • Immutable X とは何か

メモ

  • Immutable XはEthereumのLayer2ソリューションを提供する会社
  • 特徴
    • トランザクションに署名するゼロ知識証明を使ってブロックの改竄を不可能にする仕組みを使っている
      • EthereumのZK rollupsと大体同じっぽい。トランザクションに暗号文を含んでブロックにしてEthereumネットワークでvalidateしてもらう感じか。
    • ガス代がゼロ
      • この証明にはガス代がかかりますし、Immutable Xを運営していくためには最低でも1年間で200万から500万ドルの不変的なコストがかかります。その不変的なコストを私たちはユーザーに代わって支払うことで、ガス代がゼロになっているのです。

    • 9000TPSはLayer2ならではのパフォーマンス
    • NFT取引から手数料を取るモデル
  • ガス代をImmutable Xが支払ってるのはめちゃくちゃ力技だし結局一社に依存していると言う意味では中央集権的
  • サイドチェーンに比べてセキュリティが高いといってるがそれはそう
    • ネットワークの参加者が圧倒的に多いEthereumのコンセンサスアルゴリズムをそのまま使えるので
  • ポジショントークだと思うけどサイドチェーンはクソと言っている
  • 手数料が取られるとはいえ、ガス代がいらないのはかなり出品者・購入者にも魅力だしTikTokとか大手にも採用しやすいのでそれなりに使われていきそうな雰囲気

追及権

リソース

やること

  • 追及権とは

メモ

  • 追及権とは美術品が転売されるたびに取引額の一定割合を制作者であるアーティストが(ロイヤリティとして)受け取ることを可能にする権利
  • NFTの文脈ではすでにOpenseaなどで導入されている
  • 二次流通時にアーティストへロイヤリティが支払われるようになることは一見アーティストにとってプラスなのでは?と考えがちだが理論研究の大半は市場を歪めるという理由から政策として追及権を導入することに反対とされている
    • 確かに購入した作品を転売する際に売り上げから一部が引かれてしまうのであれば、追及権の付いていない作品を買おうとする行動原理も理解できる
    • と同時にそれを避けるためにアーティストは事前に売値を下げようする圧力がかかってしまうのも理解できる

Hokusai API

リソース

やること

  • Hokusai APIとは何か

メモ

  • NFTサービス開発用のAPIを提供しているサービス。NFT版Stripeみたいなイメージでwebsiteに統合できる。
  • mint, transfer, burn, see dataなどNFTのCRUDに必要なAPIを用意
  • ガス代とネットワーク代をHokusai側が支払ってくれる
    • が、API使用料はかかる。クリエータ側の負担がなくなるのは良さそう。
  • NFTデータはIPFSNFT Storage - Free decentralized storage and bandwidth for NFTs on IPFS and Filecoin.に置かれるのはデータのSPOF化を防いでくれるので評価高い。Openseaはこの辺怖いし。
    • IPFSにアップロードして得られたmetadata(JSON)を返すURL(もしくはIPFS URL)がtokenUrlとなる。これをAPIにPOSTすればmintになる。

OpenSea

リソース

やること

  • Openseaとは何か

メモ

  • NFTなどを販売するマーケットプレースを開設できるプラットフォーム
  • Loot(ガチャみたいなもの)やバンドルアイテムの販売もできる
  • smart contractを使ってOpenseaと統合できる
  • 販売したNFTに二次流通feeを設定できる。OpenSea側のプラットフォーム代は2.5%。
    • 例えばownerが2.5%の二次流通Feeを設定した場合、そのNFTを購入した誰かがまた別の人へ販売するときは
      • 「その販売者の売値 + Ownerの設定した2.5%のfee + OpenSea側の2.5%のfee」になる。
  • 月1で設定したアドレスに報酬を受け取れる

Oracle

リソース

やること

  • Blockchainの文脈で出てくるOracleとは

メモ

  • DBじゃない
  • meaning的には神からの神託を伝える者
  • Ethereum文脈ではスマートコントラクトをする時に必要な外部情報を保持伝達するサーバー
  • スマートコントラクトといえど外部の一次情報が間違っていれば元も子もない
  • 例えば現在の為替価格を知りたいときに叩く外部APIとか。

ERC721のスマートコントラクトをOpenSeaに統合する

リソース

やること

  • ERC721のスマートコントラクトとOpenseaの統合に関するTutorialをやる
  • どうしてERC721に沿ったトークンをコントラクトでデプロイするとOpenSeaで販売できるのか?を知る

メモ

  • proxy userとは
    • Opensea内での取引を行う内部アカウント
  • creature(NFT)はhttps://opensea-creatures-api.herokuapp.com/api/creature/{token_id}の形式で用意されている
    • スマートコントラクト内のNFTのtoken_idとAPIが1対1で対応するようにする
  • off-chainとは
    • on-chain(Blockchain)の外の世界。つまり外部API。off-chainのデータを提供してるものをOracleという。ChainLinkなんかが有名。
  • OpenSeaでは自分のスマートコントラクトのトークンを個別で見るにはhttps://[testnets].opensea.io/assets/<asset_contract_address>/<token_id>で確認できる
    • キャッシュされるのでキャッシュを更新したい場合はhttps://[testnets-]api.opensea.io/api/v1/asset/<your_contract_address>/<token_id>/?force_update=trueを叩けば良い
  • 売るにはOpenSeaの自分のアイテムでSellをポチッとやれば終わり。オークションもOpenSeaからポチッとやるだけ。
  • Get Listed | OpenSeaにスマートコントラクトのアドレスを入力するとListされる
    • スマートコントラクトのOwnerとしてMetamaskでログインすれば編集などそのCollectiveのページのカスタマイズができる
      • なのでNFTのスマートコントラクトをデプロイするときはOwenableの実装が必須。これがないとコントラクトの所有権が誰かわからず編集できない。
    • 「どうしてERC721に沿ったトークンをコントラクトでデプロイするとOpenSeaで販売できるのか?」の答え
      • デプロイしたスマートコントラクトをデプロイしたOwnerがOpenSeaに登録するから
      • networkにデプロイすると自動でOpenSeaが検知してマーケットに並べるのかと思ってたが違うっぽい

Arweave

リソース

やること

  • Arweaveとは

メモ

  • Arweaveは分散型ストレージでIPFSの一つ
  • Blockchainに似た仕組み(厳密にはグラフ構造に近い)でBlockweaveというチェーンを持っている
  • BlockweaveではProof of Accessというコンセンサスアルゴリズムを使いminerはこのアルゴリズムに従うことで報酬が得られる
    • Proof of Accessでは前のブロックとランダムに選ばれたブロックを使う。このランダムに選ばれたブロックを使うことからできるだけ多くのdisk spaceを提供しておいて保持しておいた方が得となる。よってdisk spaceをたくさん提供するインセンティブが発生する。
    • 雑な理解なので間違ってるかも。
    • 新しくSPOAというアルゴリズムになったみたいな話があった
      • データの転送速度にもフォーカスするのが目的っぽい

OpenZeppelin

リソース

やること

  • OpenZeppelinとは何か

メモ

  • Ethereumに関する開発を便利にするフレームワークを提供してくれるライブラリ
  • ERC20,ERC721,ERC777,ERC1155のスマートコントラクトに必要なメソッドをあらかじめ実装した抽象クラスの提供が一番よく使うと思う
  • Ownableとは
    • OpenZeppelinに実装されてるスマートコントラクトにアクセス管理の機能を提供するために必要なメソッドを実装したmodule
    • ここでいうOwner = 所有者(コントラクトをデプロイした人)
  • 他にもたくさんmoduleがあるがどれもソースコードが読みやすいしドキュメントがしっかり書かれてるので良い。まずはよく使うcontracts/のコードから読むといいかも。

Polygonへコントラクトのデプロイ

リソース

やること

  • Polygonへコントラクトをデプロイする

メモ

  • コードはコレクタブルNFTを作ってみる①事前にオフチェーンで画像を用意するパターン | ぽんた / Kenta Suharaを参考にしたが結構色々な部分が端折られているので最初はつまづく気がする。
  • 基本の流れ
  • ここまでやったがデプロイがうまくいかないので原因を探す
    • Polygon Basic IntegrationによるとPolygonのネットワークのスマートコントラクトはOpenSeaからガス代をかけずにいじれるような設定をしないといけない
      • OpenZeppelinERC721 moduleを継承していれば問題なさそう
    • 単純にtransaction feeが足りないだけでは?
      • mumbaiのfaucetでは0.1MATICを貰えた。polygonscanをみたところtransaction feeは今0.006とかだった。mainnetで自分の持っているmaticは0.003だったのでこれが原因っぽい...
      • mumbaiへのtransaction feeは0.025738752 MATICだった。
  • mumbaiにデプロイ後OpenSea, the largest NFT marketplaceへアクセス。metamaskでmumbaiに接続後ログイン。
    • Submit contract addressへアクセス。デプロイしたコントラクトを登録。We couldn't find this contract. Please ensure that this is a valid ERC721 or ERC1155 contract deployed on Mumbai and that you have already minted items on the contractというエラーが出るが、これは既知の問題らしい。24時間くらい待つと登録できるようになるっぽい。

SmartContractの開発環境を作る

リソース

やること

メモ

  • contract,frontendが必要になるのでmonorepo形式が良さそう
  • 気軽に試せるように簡易なフロントエンドがあると作成したcontractをtry & errorができる
  • 秘匿情報とか設定が必要なファイルは.hoge.jsみたいな感じでおいておいて、適宜コピーして使えるようにする
  • とりあえずlocalhostのみへのデプロイ。ethereumのmainnetやtestnetの設定は場合によるのでhardhat.config.jsにあとで追加すれば良い。
  • rootディレクトリでyarn devすれば全て立ち上がるようにする
    • 内部的には
      • hardhatでnodeを立てる
      • hardhatでcontractのデプロイ
      • デプロイしたcontractのABIをフロントエンド側に出力(フロントエンド側からcontractをシームレスに使えるようにするためにこれが大事。)
      • フロントエンドのreact serverの起動&watch
    • ローカルアカウントにfaucetを追加するには?
      • コントラクトから自分のローカルwalletのアドレスにtransferするだけ
      • yarn faucetというコマンド追加した
  • 成果物

コントラクトの再デプロイ

リソース

やること

  • コントラクトの状態を維持したまま再デプロイする方法について

メモ

  • 通常はコントラクトはimmutableなのでデプロイ後に変更ができない
  • しかしながらバグの修正など変更できた方が当たり前ながら便利
  • やることとしては
    • 新たなコントラクトのデプロイ
    • 全ての状態を古いコントラクトから新しいコントラクトにマイグレートする
    • 古いコントラクトとやりとりしているコントラクトのアドレスを新しいコントラクトのものに変更
    • 既存の利用者に新しいコントラクトを利用するよう調整する
  • これらの作業は煩雑で大変だがOpenZeppelinのプラグインでこれらを一括で行えるようになる
  • 必要なもの
    • yarn add @openzeppelin/contracts-upgradeable
    • yarn add -D @openzeppelin/hardhat-upgrades
  • 仕組み
    • 実装したコントラクト、ProxyAdminコントラクト、Proxyコントラクトの3つをデプロイする
    • Proxyコントラクトを表に出すインターフェースにする。状態を保持する。Proxyコントラクトは実装したコントラクトの機能を全て継承する。
    • コードのアップデート時はProxyAdminコントラクトがProxyコントラクトのコード部分のみ新しく実装したコントラクト(のアドレス)に差し替える。
    • こうすることで状態は維持されたまま実装だけ差し替え可能になる。
  • 制限

成果物

実際のコード例
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.3;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract MyNFT is
    Initializable,
    ContextUpgradeable,
    ERC721Upgradeable,
    ERC721EnumerableUpgradeable,
    OwnableUpgradeable
{
    using SafeMath for uint256;

    string private _baseTokenURI;
    uint256 public constant MAX_ELEMENTS = 3;
    uint256 public constant price = 1000000000000000000; //1 ETH / 1 MATIC

    function initialize(
        string memory name,
        string memory symbol,
        string memory baseTokenURI
    ) public initializer {
        __Context_init();
        __Ownable_init();
        __ERC721_init(name, symbol);
        __ERC721Enumerable_init();
        _baseTokenURI = baseTokenURI;
    }

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() initializer {}

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    )
        internal
        virtual
        override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function buy() public payable {
        require(
            totalSupply() < MAX_ELEMENTS,
            "Purchase would exceed max supply of NFTs"
        );
        require(price <= msg.value, "Ether value sent is not correct");
        uint256 mintIndex = totalSupply();
        _safeMint(msg.sender, mintIndex);
    }

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

    function ownedTokens() public view returns (uint256[] memory) {
        uint256 balance = balanceOf(msg.sender);
        if (balance == 0) {
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](balance);
            uint256 mintedCount = totalSupply();
            uint256 resultIndex = 0;
            uint256 tokenId = 0;
            while (tokenId < mintedCount) {
                if (ownerOf(tokenId) == msg.sender) {
                    result[resultIndex] = tokenId;
                    resultIndex = resultIndex.add(1);
                }
                tokenId = tokenId.add(1);
            }
            return result;
        }
    }

    function nyan() public view virtual returns (string memory) {
        return "nyao nyao nyan";
    }
}

コントラクトはどこに保持されるか

リソース

やること

  • コントラクトはどこに保持されるかソースコードレベルで知りたい

メモ

  • go-ethereum/core at master · ethereum/go-ethereumを読むとstateに関するコードが見える。とはいえでかいコードなのでちゃんと全部読むのは厳しい...
  • ソースコード(bytecote)もコントラクトのstate(storage)も全てEthereumのdbに保持されてる
    • levelDBとmemoryDB(map[string][]byteのstruct)を使ってる
  • storage
    • blockchainで永続化されるkey-valueのmap形式(levelDBか?)
    • 自分自身のコントラクトから自分自身のStorageにしかアクセスできない
  • memory
    • 呼び出し(コントラクトへのメッセージコール)ごとに初期化されるメモリ領域
    • 使用領域が大きいとgas代が多くかかる
  • stack
    • EVMはスタックマシーン。計算結果とかを積む。一番上のstack要素しかアクセスできない。

RinkebyのfaucetでETHをもらう

リソース

やること

  • EthreumのtestnetであるRinkbyからfaucetでテストETHをもらう

メモ

  • 手順
    • 自分のアドレスをTwitterに投稿する
    • Rinkeby: Authenticated Faucetへアクセス
    • 先ほどしたツイートのURLをフォームに入力する
    • 1日ほど待つ
      • だいぶ時間がかかるので気長に待つ
  • Openseaのtestnetがrinkebyかmunbaiしかないのでrinkebyを使うことが多くなりそう。多めにETHをもらっておくと良い。

OpenSeaでcollective NFTを作るSmart Contractを書いてみる

やったこと

collective NFTをOpenSeaで販売したい時用のSmart Contractのコードを書いた。機能としてはopenzeppelinのERC721に実装されてる標準機能に加えて、

  • ownerのみmintできる
  • ownerのみ保持してるtoken一覧を見られる

機能を追加。ローカルでの確認が結構だるいのでローカル/testnet/mainnetで開発できるように.envでの秘匿情報を環境変数で切り替えられるようにしている。

https://github.com/YuheiNakasaka/simple-collective-nft

これを使って実際にOpenSeaのtestnetにも公開したのが下記。polygonのtestnetのmumbaiを利用した(rinkyebyは十分なfaucetが得られなかったので...)。

https://testnets.opensea.io/collection/teigashitsuartnft

Web3.0

リソース

やること

  • Web3.0とは

メモ

  • Web3.0とは
    • 分散型Web。主にBlockchainを中心に構築されるWebの世界。
    • Web2.0
      • 中央集権的で双方向なWebの世界。巨大な企業がWeb上に構築したサービスやインフラを利用することが多い。
    • Web1.0
      • インターネット黎明期。テキスト中心のWebの世界。
  • Web3.0がWeb2.0を飲み込むわけではない。選択肢が増えただけ。
  • Web3.0とWeb2.0の比較(ソース)
    • Blockchains = Web3 servers + DBs
    • Smart contracts = Web3 apps
    • Public keys = Web3 usernames
    • Private keys = Web3 passwords
    • Tokens = Web3 platform assets
    • Tokenomics = Web3 business models
    • x2E = Web3 user acquisition models
    • NFTs = Web3 media assets
    • DAOs = Web3 social networks
  • 各種サービス
    • web3.0の世界を形作るための仕様やサービスがいくつも現れてきている。それらは別途深掘りする予定。

ENS

リソース

やること

  • ENSとは何か

メモ

  • ENS
    • Ethereum Name Service、つまりDNSのようなシステム。
      • 10.0.0.89400:f40:2001:0:e23e:1f33:1e81:d48bのようなIPアドレスにexample.comといったドメイン名を割り当てるのと同じことをEthreumネットワーク空間でも行う
    • ENSによって0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266のようなWalletアドレス・暗号通貨アドレス・コンテンツハッシュ・メタデータなんかにalice.ethみたいな可読性の高い名前を割り当てられるようになる。また、逆引きできるようにもなってる。
    • ここで購入できる
    • 価格
      • ETHで支払う
      • 5文字以上で$5/year in ETH、4文字で$160/year in IETH、 3文字だと$640/year in ETH
  • ENSをちょっと深掘り
    • ENSのregistrarとresolverはsmart contractでEthreumのmainnetとtestnetに存在する。ensの保持者は自分でサブドメインを管理設定可能。
    • ensjsを使うことでENSからアドレスを引いてきたりできる。
    • 名前解決の流れ
      • Image
      • ENS Registryに知りたいENSを投げるとResolverのアドレスを返してくれる
      • 返されたアドレスに再度ENSを投げるとそのENSのアドレスを返却してくれる
    • ルートノードはmulttisigでethereum communityの人たちで管理してる
    • registryのアドレス
  • ENSをdAppに適用する

ライトニングネットワーク

リソース

やること

  • ライトニングネットワークとは

メモ

  • ブロックチェーンの外で取引を行うオフチェーン取引によってビットコインの送金速度の向上や少額決済(マイクロペイメント)に対応した安価な送金手数料を実現するために考案された送金方法
  • ペイメントチャンネル
    • 二者間のみのチャンネルを作成。作成したチャンネルでは送金受取を何度も行える。送金受取は互いの秘密鍵での署名が必須。最終的にチャンネルを閉じた時の互いの残高のみがブロックチェーン上に記録される。
    • 送信受取は何度もできるが記録は1度だけなので手数料が少なくて済む
  • ライトニングネットワーク
    • ペイメントチャンネルを複数人の間で行えるようにしたもの。
    • A,Cが送金受取を行いたいがチャンネルがない場合、すでにチャンネルを持っているBとそれぞれ接続することでやりとりができるようになる。
    • BはA,Cの暗号資産を一時的に保有するので持ち逃げリスクがありそうだがそこを暗号アルゴリズムで出来ないようにしている
作成者以外のコメントは許可されていません