[Ethereum] Ubuntu20.04にGethをインストールして起動

GethはGo Ethereumの略, GNU LGPL v3ライセンス
Ethereumクライアントで、マイニングや送金を実行することができる。
送金の際はトランザクションを生成し、任意のコードも載せることもできる。

### Ubuntu20.04にGethをインストールする
$ sudo apt-get update
$ sudo apt-get upgrade

$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install ethereum
$ geth –help
$ geth –help
NAME:
geth – the go-ethereum command line interface

Copyright 2013-2021 The go-ethereum Authors

USAGE:
geth [options] [command] [command options] [arguments…]

VERSION:
1.10.13-stable-7a0c19f8

### Gethの起動
1. データディレクトリ: 送受信するブロックやアカウント情報を保存
2. genesis.json: 設定ファイル、ローカルプライベートテストネットを使う場合に必要

$ vi genesis.json

{
  "config": {
        "chainId": 10,
        "homesteadBlock": 0,
        "eip150Block": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}

$ tree
.
├── genesis.json
├── geth
│   ├── LOCK
│   ├── chaindata
│   │   ├── 000001.log
│   │   ├── CURRENT
│   │   ├── LOCK
│   │   ├── LOG
│   │   └── MANIFEST-000000
│   ├── lightchaindata
│   │   ├── 000001.log
│   │   ├── CURRENT
│   │   ├── LOCK
│   │   ├── LOG
│   │   └── MANIFEST-000000
│   └── nodekey
└── keystore

$ geth –networkid “15” –nodiscover –port 30304 –datadir ./ console 2>> ./geth_err.log
Welcome to the Geth JavaScript console!

instance: Geth/v1.10.13-stable-7a0c19f8/linux-amd64/go1.17.2
at block: 0 (Thu Jan 01 1970 00:00:00 GMT+0000 (UTC))
datadir: /home/vagrant/dev/ethereum/eth_testnet
modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit

OK

[NEM] sdkでapostilleを実装する

var nem = require("nem-sdk").default;

const password = '';
const privateKey = '';
const common = nem.model.objects.create('common')(password, privateKey);
const fileName = "sample.txt";
const content = "Apostille is awesome !";
const tag = "Test Apostille";
var fileContent = nem.crypto.js.enc.Utf8.parse(content);

var endpoint = nem.model.objects.create("endpoint")(nem.model.nodes.defaultMainnet, nem.model.nodes.defaultPort);

var apostille = nem.model.apostille.create(common, "Test.txt", fileContent, tag, nem.model.apostille.hashing["SHA256"], false, {}, true, nem.model.network.data.mainnet.id);



async function main() {
	nem.model.transactions.send(common, apostille.transaction, endpoint).then(function(res){
		// If code >= 2, it's an error
		if (res.code >= 2) {
			console.error(res.message);
		} else {
			console.log("\nTransaction: " + res.message);
			console.log("\nCreate a file with the fileContent text and name it:\n" + apostille.data.file.name.replace(/\.[^/.]+$/, "") + " -- Apostille TX " + res.transactionHash.data + " -- Date DD/MM/YYYY" + "." + apostille.data.file.name.split('.').pop());
			console.log("When transaction is confirmed the file should audit successfully in Nano");
			console.log("\nYou can also take the following hash: " + res.transactionHash.data + " and put it into the audit.js example");
		}
	}, function(err) {
		console.error(err);
	});
}

main();

$ node apostille.js

Transaction: SUCCESS

Create a file with the fileContent text and name it:
Test — Apostille TX 22817314fc9e51742ce6a5836e1ad7bb622cf32289d546831d1ab99224618c2f — Date DD/MM/YYYY.txt
When transaction is confirmed the file should audit successfully in Nano

You can also take the following hash: 22817314fc9e51742ce6a5836e1ad7bb622cf32289d546831d1ab99224618c2f and put it into the audit.js example

おおおおおお、完全に理解した。

### apostilleのcheck

var nem = require("nem-sdk").default;

const content = "Apostille is awesome !";
var fileContent = nem.crypto.js.enc.Utf8.parse(content);

var endpoint = nem.model.objects.create("endpoint")(nem.model.nodes.defaultMainnet, nem.model.nodes.defaultPort);

var txHash = "22817314fc9e51742ce6a5836e1ad7bb622cf32289d546831d1ab99224618c2f"


async function main() {
	nem.com.requests.transaction.byHash(endpoint, txHash).then(function(res) {
	  // Verify
	  if (nem.model.apostille.verify(fileContent, res.transaction)) {
	    console.log("Apostille is valid");
	  } else {
	    console.log("Apostille is invalid");
	  }
	}, function(err) {
	  console.log("Apostille is invalid");
	  console.log(err);
	});
}

main();

$ node confirm.js
Apostille is valid

すげええええええええ
これを実装する

よっしゃあああああああああああああああああああああああ
あとはNEMの価格を取得して表示するのみ

[JavaScript] ファイル名とファイルの中身を取得する

ブロックチェーン上に上げるために、ファイル名とファイルの中身を取得したい。

html

				<div class="control">
					<input id="file" name="file" type="file" />
				</div>
				// 省略
		<p>Please press the button.</p>
		<input type="submit" class="button is-link" id="btn" value="authorize" onclick="OnButtonClick();">

view: buttonを押すと処理を実行するように書いている(onChangeではない)

reader.readAsTextとした後に、reader.onload(ファイルの読み込み完了) 後に処理をする。

		let reader = new FileReader();
		let input = ""

		function OnButtonClick() {
			input = document.getElementById("file").files[0]
			reader.readAsText(input, 'UTF-8')
			reader.onload = ()=> {
	            main();	
	        };
						
		}
		async function main() {
			console.log(input.name)
			console.log(reader.result);
		}

なるほどー、割と簡単にできたなー

[NEM] Apostilleで実装したい

public apostilleとprivate apostilleがある

## open apostille
https://www.openapostille.net/detail/797ae3b488f7808bf3b7ad46c7f487f2b02645175f09256ca532eb74b274d319

Txhash: 797ae3b488f7808bf3b7ad46c7f487f2b02645175f09256ca532eb74b274d319
ファイル名: ジャパンカップ予想 — Apostille TX 797ae3b488f7808bf3b7ad46c7f487f2b02645175f09256ca532eb74b274d319 — Date 2017-11-25.docx

## transfer transaction:
http://chain.nem.ninja/#/transfer/797ae3b488f7808bf3b7ad46c7f487f2b02645175f09256ca532eb74b274d319

Message: 4e545903560fc41a6f8c9495ee7e7e18322973c50e9e2f4fbb91fa892343412586d9a808

### public apostilleの詳細
fe4e545903560fc41a6f8c9495ee7e7e18322973c50e9e2f4fbb91fa892343412586d9a808

fe 16進数
4e5459 apostilleを示す
0 public(privateは9)
3 hash algorithm sha256(1:md5, 2:sha1, 3:sha256, 8:sha3-256, 9:sha3-512などがある)
560fc41a6f8c9495ee7e7e18322973c50e9e2f4fbb91fa892343412586d9a808 hash

$ sha256sum “ジャパンカップ予想 — Apostille TX 797ae3b488f7808bf3b7ad46c7f487f2b02645175f09256ca532eb74b274d319 — Date 2017-11-25.docx”
560fc41a6f8c9495ee7e7e18322973c50e9e2f4fbb91fa892343412586d9a808 ジャパンカップ予想 — Apostille TX 797ae3b488f7808bf3b7ad46c7f487f2b02645175f09256ca532eb74b274d319 — Date 2017-11-25.docx

一致する
sha256sumコマンドはSHA-256を用いて、256ビット(64桁の16進数)を出力

NanoWalletではファイル名の記載されたトランザクションハッシュからハッシュ値が記録されたメッセージを取得し、
メッセージから取り出したハッシュ値とファイルのハッシュ値を付き合わせて完全性を証明している
存在証明はそのトランザクションのタイムスタンプを利用する
-> 上記の2つで、ある時点でこのファイルが存在していたと証明できる
アポスティーユはトランザクションの応用

#### NanoWalletで作成してみる

{"data":[
{"filename":"test.txt",
 "tags":"test",
  "fileHash":"fe4e545983efc827e551fcd0c8429a468a7c6c64f5f231b1221c3ecffdbfff00dacdd9a95aed46a4d30e88cf83dfdf9a49beb4a7e748bcde7465ac15e202a50ad28f44f606",
  "owner":"NDXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "fromMultisig":"NDIWUXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "dedicatedAccount":"NCFYBGQXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "dedicatedPrivateKey":"79e4a484XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "txHash":"bea36f12fe14a046ab849f8df097fbe238aae457e45a36854d800d8f45c52b33",
  "txMultisigHash":"",
  "timeStamp":"Sat, 18 Dec 2021 14:35:34 GMT"
 }]}

filename, tag, filehash, owner, fromMultisig, dedicatedAccount, dedicatedPrivtateKey, txHash, txMultisigHash, timeStamp

MessageHEX: 4e545903 5526798746c3bdabb663b681ed78c2f8ac0b13b275dc4f95d4941fc58d7258e0

$ sha256sum “sample — Apostille TX 52d76444c713f3f84e5c44107cce511d974a250b91c5c49a1323bfe838da0f5a — Date 2021-12-19.txt”
5526798746c3bdabb663b681ed78c2f8ac0b13b275dc4f95d4941fc58d7258e0 sample — Apostille TX 52d76444c713f3f84e5c44107cce511d974a250b91c5c49a1323bfe838da0f5a — Date 2021-12-19.txt

ダウンロードしたファイルのsha256でhash計算した値が、MessageのHash値と一致していることで、このファイルが、この時間にあったことを証明している。

ンンン、なるほどー

[NEM] mosaicで商品購入ページを作りたい

基本的にはsendと同じだが、商品を選択して、商品数を入力できるようにすれば良い

		<div class="card column">
			<center><input type="radio" name="art" value="1" checked></center>
		</div>
		<div class="card column">
			<center><input type="radio" name="art" value="2"></center>
		</div>
		<div class="card column">
			<center><input type="radio" name="art" value="3"></center>
		</div>

		<div class="field" width="90%" style="margin:20px">
			<label class="label">Amount</label>
			<div class="control ">
				<input class="input" size="3" type="number" id="amount" style="width: 33%;" placeholder="" value="1">
			</div>
		</div>

js: 商品情報を連想配列で持って、radioボタンが押された時に、配列の何番目かを取得すれば良い

		var products = [
			{name:"Rime ethics", price:"20"},
			{name:"Midnight Asia", price:"30"},
			{name:"Dreams & Struggle", price:"90"},
		]

		// 省略

		function OnButtonClick() {
			let checkValue = "";
			let elements = document.getElementsByName('art');
			for (let i = 0; i < elements.length; i++){
			    if (elements.item(i).checked){
			        checkValue = i;
			    } 
			}
			sendAmountNem = parseInt(products[checkValue]['price']) * amount.value;
			name = products[checkValue]['name'];
			if (!walletpassword.value || !privatekey.value) {
			    resArea.innerHTML = "Wallet Password、privatekeyを入力してください";
			} else {
			    main();
			}		
			
		}

割とやりたいことはできたか…

[NEM] Wallet情報の取得

var nem = require("nem-sdk").default;

const endpoint = nem.model.objects.create('endpoint')(nem.model.nodes.defaultMainnet, nem.model.nodes.defaultPort);

const mainnetAddress = '****'

async function main() {
	await nem.com.requests.account.data(endpoint, mainnetAddress).then(res => {
		console.log('balance:', res.account);
	}).catch(err => {
		console.log(err);
	});
}

main();

$ node wallet.js
balance: { address: ‘****’,
harvestedBlocks: 0,
balance: 00000000,
importance: 0,
vestedBalance: 00000000,
publicKey:
‘****’,
label: null,
multisigInfo: {} }

なるほどね、アカウントにいくら入ってるかってのは誰でも見れちゃうんだな

[JavaScript] inputに入力があれbuttonを押せるようにする

btn.disabled = true; でbuttonを制御します。
input.addEventListener(“change”, func); で btn.disabledの状態を変更する

let btn = document.getElementById('btn');
		let wallet = document.getElementById('wallet');
		let message = document.getElementById('message');
		let amount = document.getElementById('amount');
		let resArea = document.getElementById("resArea");
		btn.disabled = true; 
		wallet.addEventListener("change", stateHandle);
		amount.addEventListener("change", stateHandle);
		message.addEventListener("change", stateHandle);
		function stateHandle() {
		  if (wallet.value == "" || amount.value == "") {
		    btn.disabled = true; 
		  } else {
		    btn.disabled = false;
		  }
		}

		function OnButtonClick() {
			if (!wallet.value || !message.value || !amount.value) {
			    resArea.innerHTML = "Wallet Address、amount、messageを入力してください";
			} else {
			    main();
			}		
			
		}
		async function main() {
			console.log("送金完了しました")
			console.log(wallet.value)
			console.log(message.value)
			console.log(amount.value)
			resArea.innerHTML = "送金完了しました";
		}

最初

入力後

OK
これで実際に送金できるかテストする

しゃあああああああああああああああああああ
次はxem amountの取得かな

[NEM] node.jsでmosaicを送信

nem-sdkをインストールします
$ npm install nem-sdk

mainnetで送信します。

var nem = require("nem-sdk").default;

const endpoint = nem.model.objects.create('endpoint')(nem.model.nodes.defaultMainnet, nem.model.nodes.defaultPort);

async function main() {

    const toAddress = '';
    const sendAmount = 1;
    const sendMsg = 'Hello World!';
    const password = '';
    const privateKey = '';
    const common = nem.model.objects.create('common')(password, privateKey);
    const yourMosaicNamespace = 'capitalcoin';
    const yourMosaicName = 'hpscript';

    let transferTransaction = nem.model.objects.create('transferTransaction')(toAddress, sendAmount, sendMsg);

    const xemMozaic = nem.model.objects.create('mosaicAttachment')('nem', 'xem', 0);
    transferTransaction.mosaics.push(xemMozaic);

    const yourMosaic = nem.model.objects.create('mosaicAttachment')(yourMosaicNamespace, yourMosaicName, 10);
    transferTransaction.mosaics.push(yourMosaic);

    let mosaicDefinitionMetaDataPair = nem.model.objects.get('mosaicDefinitionMetaDataPair');
    nem.com.requests.namespace.mosaicDefinitions(endpoint, yourMosaic.mosaicId.namespaceId).then(res => {

        const neededDefinition = nem.utils.helpers.searchMosaicDefinitionArray(res.data, [yourMosaicName]);

        const fullMosaicName  = nem.utils.format.mosaicIdToName(yourMosaic.mosaicId);

        if (undefined === neededDefinition[fullMosaicName]) {
            return console.log('Mosaic not found !');
        }

        mosaicDefinitionMetaDataPair[fullMosaicName] = {};
        mosaicDefinitionMetaDataPair[fullMosaicName].mosaicDefinition = neededDefinition[fullMosaicName];

        nem.com.requests.mosaic.supply(endpoint, fullMosaicName).then(supplyRes => {

            mosaicDefinitionMetaDataPair['nem:xem'].supply = 8999999999;
            mosaicDefinitionMetaDataPair[fullMosaicName].supply = supplyRes.supply;

            const transactionEntity = nem.model.transactions.prepare('mosaicTransferTransaction')(common, transferTransaction, mosaicDefinitionMetaDataPair, nem.model.network.data.mainnet.id);

            nem.model.transactions.send(common, transactionEntity, endpoint).then(sendRes => {
                console.log('sendRes:', sendRes);
            }).catch(sendErr => {
                console.log('sendError:', sendErr);
            });
        }).catch(supplyErr => {
            console.log('supplyErr:', supplyErr);
        });
    }).catch(err => {
        console.log('mosaicDefinitionsError:', err);
    });
}

main();

$ node test.js
sendRes: { innerTransactionHash: {},
code: 1,
type: 1,
message: ‘SUCCESS’,
transactionHash:
{ data:
‘f08066a73d37f523482d6dc61a60e53db4cbd5097262f1ee419cae7ae4ad9a7a’ } }

送信結果

おおお、これをUIをつけてやりたい

<body>
	<div class="container">
		<h1 class="title">NEM Project</h1>
		<p>送金ボタンを押してください</p>
		<input type="button" value="送金" onclick="OnButtonClick();">
	</div>
	<script src="nem-sdk.js"></script>
	<script>
		var nem = require("nem-sdk").default;
		
		function OnButtonClick() {
			main();
			console.log("送金完了しました")
		}
		const endpoint = nem.model.objects.create('endpoint')(nem.model.nodes.defaultMainnet, nem.model.nodes.defaultPort);
		async function main() {
		    // 省略
		}
	</script>

あ、出来た^^

受け取るwalletのアドレスとメッセージを入力フォームにして、そこに送信できるようにしたい。

[NEM] NEM Walletから独自通貨(mosaic)を送金

Nem用のwalletであるRaccoon Walletをスマホにインストールし、ウォレットのアドレスを取得します。

RaccoonWallet
https://apps.apple.com/jp/app/raccoonwallet/id1437034062

NEM WalletからRaccoon Walletのアドレスへ、mosaicを送信します

Raccoon Wallet側で受け取ったトランザクションを確認

おおおお、なんか凄い
これをプログラムでやりたい。出来ればUI付きで

[Ethereum] UIをコントラクトに接続

$ mkdir greeter-dapp
$ truffle unbox react
$ truffle develop

truffle(develop)> compile
truffle(develop)> migrate

$ cp ../greeter/contracts/Greeter.sol contracts/

2_deploy_greeter.js

var GreeterContract = artifacts.require("./Greeter.sol");

module.exports = function(deployer) {
  deployer.deploy(GreeterContract);
};

client/src/App.js

import React, { Component } from "react";
import GreeterContract from "./contracts/Greeter.json";
import getWeb3 from "./getWeb3";

import "./App.css";

class App extends Component {
  state = { greeting: '', web3: null, accounts: null, contract: null };

  componentDidMount = async () => {
    try {
      // Get network provider and web3 instance.
      const web3 = await getWeb3();

      // Use web3 to get the user's accounts.
      const accounts = await web3.eth.getAccounts();

      // Get the contract instance.
      const networkId = await web3.eth.net.getId();
      const deployedNetwork = GreeterContract.networks[networkId];
      const instance = new web3.eth.Contract(
        GreeterContract.abi,
        deployedNetwork && deployedNetwork.address,
      );

      // Set web3, accounts, and contract to the state, and then proceed with an
      // example of interacting with the contract's methods.
      this.setState({ web3, accounts, contract: instance }, this.runExample);
    } catch (error) {
      // Catch any errors for any of the above operations.
      alert(
        `Failed to load web3, accounts, or contract. Check console for details.`,
      );
      console.error(error);
    }
  };

  runExample = async () => {
    const { accounts, contract } = this.state;

    const response = await contract.methods.greet().call();
    this.setState({greeting: response})
  };

  render() {
    if (!this.state.web3) {
      return <div>Loading Web3, accounts, and contract...</div>;
    }
    return (
      <div className="App">
        <h1>Greeter</h1>

        {this.state.greeting}

        <form>
          <label>
            New Greeting:
            <input type="text" value={this.state.greeting} onChange={this.handleGreetingChange}>
          </label>
        </form>
        <button onClick={this.formSubmitHandler}> Submit </button>
      </div>
    );
  }
}

export default App;

うーむ、ようわからん