イントロダクション
この投稿は、最初の原則からのDAppのテストという以前のブログ投稿の更新版です。前回と同様に、Ethereumのプライベートネット上でSolidity Remix IDEの「寄付」チュートリアルを立ち上げるために必要なステップを説明します。この投稿の基にするチュートリアルはRemix IDEのReadTheDocsで入手可能ですが、yann300から必要な2つのファイルをここに公開する許可を得ています。この投稿には、DAppが適切に機能するために現在必要な追加のステップが含まれています。
このチュートリアルでは、前回と同じツールセットを使用します。Ethereumノードとしてgethを選択し、DAppブラウザとしてMistを選び、静的ファイルのウェブサーバーとしてPythonのSimpleHTTPServerを使用します。Gethはブロックチェーン上にデータを保存し、読み込むことができます。ウェブサーバーは、DAppで使用する静的HTML、CSS、およびJavaScriptファイルを迅速に提供することを可能にします。Mistブラウザは、これら2つの情報技術のパラダイム間の橋渡しを行い、ウェブサーバーからの静的ファイルをロードし、提供されたweb3.jsオブジェクトを介してEthereumノードと通信できるようにします。
私たちの以前の経験から、devモードでgethノードを実行することが、DAppの開発とテストに最適な方法であると結論付けました。Ganacheなどの他の開発プラットフォームも存在しますが、gethのdevモードを使用して開発することによって、メインネット上でのDAppの動作をシミュレーションすることに関しては、これ以上の方法は見つかりませんでした。
この更新されたチュートリアルでは、新たにリリースされたRemix IDEのスタンドアロン版を使用します。npmまたはyarnを使ってインストールしてください。
$ yarn global add remix-ide
このチュートリアルは、gethバージョン1.8.6-stable、Mistバージョン0.10.0を使用して記述されました。
gethの初期化
まず、ブロックチェーンデータを保存するためのフォルダーを作成しましょう。
次に、新しく作成したデータディレクトリを指してgethノードを起動しましょう。DAppはシングルノードプライベートネットにデプロイされ、メインネット上でのDAppの構築とテストの金銭的負担を避けることができるように、gethをdevモードで実行します。
$ geth --dev --datadir test-chain-dir console
gethのターミナルウィンドウには、以下のような出力が表示されるはずです。
INFO [05-17|14:32:51] Maximum peer count ETH=25 LES=0 total=25INFO [05-17|14:32:52] Using developer account address=0xf92EcEa910C31e302699e58B866bD2604527dD1dINFO [05-17|14:32:52] Starting peer-to-peer node instance=Geth/v1.8.6-stable/darwin-amd64/go1.10.1INFO [05-17|14:32:52] Allocated cache and file handles database=/Users/danielbriskin/tut/test-chain-dir/geth/chaindata cache=768 handles=1024INFO [05-17|14:32:52] Writing custom genesis blockINFO [05-17|14:32:52] Persisted trie from memory database nodes=12 size=2.26kB time=54.111µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00BINFO [05-17|14:32:52] Initialised chain configuration config="{ChainID: 1337 Homestead: 0 DAO: DAOSupport: false EIP150: 0 EIP155: 0 EIP158: 0 Byzantium: 0 Constantinople: Engine: clique}"INFO [05-17|14:32:52] Initialising Ethereum protocol versions="[63 62]" network=1INFO [05-17|14:32:52] Loaded most recent local header number=0 hash=4374f1…a3fed1 td=1INFO [05-17|14:32:52] Loaded most recent local full block number=0 hash=4374f1…a3fed1 td=1INFO [05-17|14:32:52] Loaded most recent local fast block number=0 hash=4374f1…a3fed1 td=1INFO [05-17|14:32:52] Regenerated local transaction journal transactions=0 accounts=0INFO [05-17|14:32:52] Starting P2P networkingINFO [05-17|14:32:52] started whisper v.6.0INFO [05-17|14:32:52] RLPx listener up self="enode://38fb7579853407637b21437c1595a47995a63fd39cb7c0a192d891c68728c0d4ed7d0402517f9b915d924594b0e91441fbc6f7963a91f780f74119ab4db5291b@[::]:49775?discport=0"INFO [05-17|14:32:52] IPC endpoint opened url=/Users/danielbriskin/tut/test-chain-dir/geth.ipcINFO [05-17|14:32:52] Transaction pool price threshold updated price=18000000000INFO [05-17|14:32:52] Etherbase automatically configured address=0xf92EcEa910C31e302699e58B866bD2604527dD1dINFO [05-17|14:32:52] Starting mining operationINFO [05-17|14:32:52] Commit new mining work number=1 txs=0 uncles=0 elapsed=118.067µsWARN [05-17|14:32:52] Block sealing failed err="waiting for transactions"出力の二番目の最後の行は、ノードがブロックをマイニングし始めたことを示しています。ただし、出力の最後の行は、ブロックに含めるトランザクションがないため、マイニングが一時停止していることを示しています。これは、以前のチュートリアルで表示された出力とは異なります。元の投稿の執筆時点で、geth --devはプルーフオブワークからプルーフオブオーソリティマイニングアルゴリズムに移行しました。これにより、プライベートネットDAppの開発が簡素化され、マイナーを手動で開始および停止する必要がなくなりました。マイナーは含めるトランザクションがある場合のみブロックをマイニングします。
IPCエンドポイントが開かれたことを示す行に注意してください。これにより、Mistを起動してプライベートネットに接続できるようになります。そうしましょう。
Linux:
$ mist --rpc test-chain-dir/geth.ipc
Mac:
$ /Applications/Mist.app/Contents/MacOS/Mist --rpc test-chain-dir/geth.ipc
Mistターミナルウィンドウは出力の行を生成し始め、まもなくスプラッシュスクリーンが開かれるのが見えるはずです。アプリケーションウィンドウを開くには、Launch Applicationボタンを押してください。以下のウィンドウが表示されるはずです。

異常に大きな開始残高に注意してください。Etherを生成するためにブロックをマイニングする必要はなくなりました。Etherbaseから個人のアカウントに単純に送金するか、Etherbaseウォレットを直接使用できます。
その点を考慮して、gethターミナルウィンドウに戻り、個人アカウントを作成しましょう。
> personal.newAccount()Passphrase:Repeat passphrase:"0xb1c7a4b22525f920ccb4c43904bb86ca487f07fe"
Mistアプリケーションウィンドウに戻ると、Ethereum Walletタブに以前の出力の行に一致するアドレスを持つ新しいアカウントが表示されます。開始Ether残高はゼロです。また、gethターミナルウィンドウからその残高も確認できます。
> web3.fromWei(eth.getBalance(personal.listAccounts[1]), "ether")0
配列インデックスの値を1から0に切り替えて、コマンドラインからEtherbaseの残高を確認することもできます。
> web3.fromWei(eth.getBalance(personal.listAccounts[0]), "ether")1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+59
寄付DApp
このチュートリアルでは、2つのファイルが必要です:
Solidityスマートコントラクトファイル
contract Donation { address owner; event fundMoved(address _to, uint _amount); modifier onlyowner { if (msg.sender == owner) _; } address[] _giver; uint[] _values; function Donation() { owner = msg.sender; } function donate() payable { addGiver(msg.value); } function moveFund(address _to, uint _amount) onlyowner { uint balance = this.balance; uint amount = _amount; if (_amount <= this.balance) { if (_to.send(this.balance)) { fundMoved(_to, _amount); } else { throw; } } else { throw; } } function addGiver(uint _amount) internal { _giver.push(msg.sender); _values.push(_amount); }}
このファイルをDonation.solとして保存してください
<pre><code class="html">
<div>
<div>Donation Contract</div>
<br/>
<input id='contractaddress' placeholder='contract address'></intput>
<br/>
<div>
<br/>
<input id='fromGive' placeholder='from' ></intput><input placeholder='amount' id='valueGive'></intput><button id="fallbackbtn" onclick="donate()">give</button>
<br/>
<br/>
<input id='fromMoveFund' placeholder='from' ></intput><input id='moveFundTo' placeholder='move to' ></intput><input id='amountToMove' placeholder='amount' ></intput><button id="movefundbtn" onclick="movefund()">moveFund</button>
<br/>
<br/>
<div id='wait' ></div>
</div>
<br/>
<br/>
<div id='log'>
</div>
</div>
<script type="text/javascript">
function donate () {
var donation = contractspec.at(document.getElementById('contractaddress').value)
donation.donate({
from: document.getElementById('fromGive').value,
value: document.getElementById('valueGive').value
}, function (error, txHash) {
tryTillResponse(txHash, function (error, receipt) {
alert('done ' + txHash)
})
})
}
function movefund () {
var donation = contractspec.at(document.getElementById('contractaddress').value)
donation.moveFund(
document.getElementById('moveFundTo').value,
document.getElementById('amountToMove').value,
function (error, txHash) {
tryTillResponse(txHash, function (error, receipt) {
alert('done ' + txHash)
})
})
}
var contractspec = web3.eth.contract([{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"moveFund","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"donate","outputs":[],"payable":true,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"fundMoved","type":"event"}]);
function tryTillResponse (txhash, done) {
document.getElementById('wait').innerHTML = 'waiting for the transaction to be mined ...'
web3.eth.getTransactionReceipt(txhash, function (err, result) {
if (!err && !result) {
setTimeout(function () { tryTillResponse(txhash, done) }, 500)
} else {
document.getElementById('wait').innerHTML = ''
var log = document.createElement("div")
log.innerHTML = JSON.stringify(result)
document.getElementById('log').appendChild(log)
done(err,result)
}
})
}
</script>
</code></pre>このファイルをindex.htmlとして保存してください
webserverという新しいフォルダーを作成し、index.htmlをそこに移動してください。ターミナルウィンドウでフォルダーを開き、次のコマンドを実行します。
$ python -m SimpleHTTPServer
今度はデフォルトのブラウザを開いてhttp://localhost:8000をロードします。デベロッパーコンソールを開いて、出力を確認します。
次の画面が表示されます。

コンソールのエラーに注意してください。予想通り、Chromeはweb3をサポートしていません。これが、Mistを使用する理由の一部です。
次に、Mistでhttp://localhost:8000を開きます。ページを右クリックしてデベロッパーコンソールを開いてください。同じエラーが表示されるはずです。

web3がまだ定義されていないようです。index.htmlにJavaScriptファイルを含める必要があります。
行20の閉じた</div>の下に、次の行を追加してください。
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script>
Mistのページをリフレッシュしてください。同じエラーが表示され続けるはずです。なぜでしょうか?
実際、gethノードを提供してweb3を初期化する必要があります。これにより、web3.jsプラグインがgethに接続できるようになり、JavaScript APIを使用してブロックチェーンにデータを読み書きできるようになります。
寄付関数をすべて含む<script>タグの内部に次のコードを追加してください。
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}Mistのページをリフレッシュしてください。コンソールにはもはやエラーは表示されないはずです。
誰もが質問するかもしれませんが、HTMLにweb3.jsを含め、gethノード接続で初期化するためにすべてこの手間をかける必要があるなら、Mistを使用する意味は何でしょうか?これらすべてがChromeでも動作するのではありませんか?
短い答えは、MistがEthereumに対する他の統合の形を提供することです。たとえば、Mistの右上隅には小さなアイコンが表示されます。それをクリックすると、Etherウォレットが含まれたポップアップが表示されます。このモーダルを使用すると、DAppsが支払いを行ったり受け取ったりするために使用できるアドレスを制御できます。従来のウェブブラウザはこのタイプの統合をサポートしていません。
Donation.solをブロックチェーンにデプロイする
フロントエンドがセットアップされ、プライベートネットブロックチェーンにDonationスマートコントラクトがデプロイされるようにする必要があります。
まず、ターミナルウィンドウでRemix IDEを実行します。スタンドアロンIDEが利用可能になった今、Mistに統合されたものよりもこれを使用することを強くお勧めします。
Mistブラウザを開いてhttp://localhost:8080をロードします。次の画面が表示されるはずです。

デフォルトのballot.solタブを閉じて、左パネルの左上隅にあるフォルダアイコンを押してDonation.solを開きます。右のパネルで、Compileタブを選択し、「コンパイルを開始」ボタンを押します。右パネルに警告のリストが表示されるべきですが、このチュートリアルの目的のためには、これを気にする必要はありません。
右のパネルでRunタブに切り替えます。パネルの一番上で、Environmentのドロップダウンに注意してください。JavaScript VM、Injected Web3、Web3 Providerの3つのオプションがあります。このドロップダウンでは、スマートコントラクトがデプロイされる場所を選択できます。
JavaScript VM - これは、ブラウザを使用して孤立したEthereumノードを実行します。スマートコントラクトを迅速にテストするのに便利ですが、結果として得られるブロックチェーンは持続しないため、ブラウザを閉じると消えます。
Injected Web3 - これは、ブラウザが現在接続しているWeb3プロバイダーを使用します。このチュートリアルの目的にとって、私たちがMistインスタンスを実行中のgeth devノードに接続したため、これが望ましいオプションです。
Web3 Provider - これにより、IDEを手動でWeb3プロバイダーにポイントできます。たとえば、別のマシンでgethノードを実行している場合、私たちのローカルRemix IDEインスタンスをリモートEthereumノードに接続できます。
Environmentドロップダウンの下に、アカウントドロップダウンがあります。ここでは、スマートコントラクトのデプロイを資金調達するために使用するEtherウォレットを選択できます。デフォルトでは、アカウントドロップダウンが空であるはずです。これは、まだRemix IDEに接続するために認可されたEtherウォレットがないからです。Mistの右上隅で、画素化された高齢者アイコンをクリックします。次のポップアップウィンドウが表示されます。

承認するアカウントを選択してください。私たちは今のところ私たちの資金があるEtherbaseアカウントを使用します。承認をクリックした後、ブラウザが更新されます。Runタブに戻ると、ドロップダウンにアカウントが利用可能であるはずです。
今、プライベートネットにスマートコントラクトをデプロイする時が来ました。Runタブで、パネルの二番目のセクションで、ドロップダウンにDonationが選択されていることを確認し、それを下にあるサーモン色のDeployボタンをクリックします。次のウィンドウがポップアップします。

Etherウォレットのパスワードを入力してください。個人アカウントを使用している場合、これはチュートリアルの最初にアカウントを作成したときに入力したのと同じパスワードです。Etherbaseウォレットのデフォルトパスワードは空白です。Send Transactionボタンをクリックしてください。
gethターミナルウィンドウに戻りましょう。次の出力が表示されます。
INFO [05-18|11:07:11] Submitted contract creation fullhash=0xc6e89db6d500cb8cfa8f16ff590f4f866de0cc7fe31d60d84e6f72f43db39327 contract=0xE6D27720E581b5133EeB9011006A47C750AEa426
INFO [05-18|11:07:11] Commit new mining work number=1 txs=1 uncles=0 elapsed=2.536ms
INFO [05-18|11:07:11] Successfully sealed new block number=1 hash=c551ee…91cd18
INFO [05-18|11:07:11] 🔨 mined potential block number=1 hash=c551ee…91cd18
INFO [05-18|11:07:11] Commit new mining work number=2 txs=0 uncles=0 elapsed=1.153ms
WARN [05-18|11:07:11] Block sealing failed err="waiting for transactions"
成功しました!私たちのスマートコントラクトがブロックチェーンにデプロイされました。出力の最初の行にある契約アドレスに注意してください。このアドレスは、Remix IDEウィンドウのRunタブの下部からもコピーできます。これは、私たちのスマートコントラクトがブロックチェーン上に存在するアドレスです。
寄付DAppの使用
今、私たちのフロントエンドは実行中であり、web3経由でgethに接続でき、スマートコントラクトはプライベートネットのブロックチェーンにデプロイされています。すべてをまとめましょう。
Python SimpleHTTPServerが実行中の状態で、Mistブラウザでhttp://localhost:8000をロードします。最初の契約アドレスフィールドに、前のステップでgethターミナルウィンドウまたはRemixからコピーした契約アドレスを貼り付けます。次の行のフィールドに、"from"フィールドにあなたのEtherウォレットアドレスを貼り付け、"amount"フィールドに1000000000000000000を入力します。この金額は1 EtherをWeiで表しています。次に「寄付する」ボタンを押すと、トランザクションウィンドウがポップアップします。

Etherウォレットのパスワードを入力してトランザクションを送信します。数秒後に、DAppウィンドウは次のようになります。

何が起こったのかを確認するためにgethターミナルウィンドウを見てみましょう。
INFO [05-18|11:24:26] Submitted transaction fullhash=0x69c5346dbaf3b5a5415134c181ba16bcda76966bd60b9a42cf646651fc4a517a recipient=0xE6D27720E581b5133EeB9011006A47C750AEa426
INFO [05-18|11:24:26] Commit new mining work number=2 txs=1 uncles=0 elapsed=801.974µs
INFO [05-18|11:24:26] Successfully sealed new block number=2 hash=f88586…e73ee1
INFO [05-18|11:24:26] 🔨 mined potential block number=2 hash=f88586…e73ee1
INFO [05-18|11:24:26] Commit new mining work number=3 txs=0 uncles=0 elapsed=514.943µs
WARN [05-18|11:24:26] Block sealing failed err="waiting for transactions"
トランザクションは成功し、Etherはスマートコントラクトのアドレスに送信されました。トランザクションのfullhashをコピーし、何が起こったのかを見てみましょう。
トランザクションのデバッグ
Remix IDEウィンドウの右パネルのデバッガータブに切り替え、"Transaction index or hash"フィールドにfullhashを貼り付けることで、トランザクションによって実行されたコードをステップスルーできます。再生ボタンを押してハッシュからトランザクションをロードします。スライダーや前後ボタンを使用して、スマートコントラクトの関数の実行をナビゲートできます。
結論
このチュートリアルがDAppの基本的なアーキテクチャを理解するのに役立ったことを願っています。geth devモードは、テストEthereumブロックチェーンで迅速に動き出すための素晴らしい方法であり、gethとMist、そしてシンプルなウェブサーバーの組み合わせは、基本的なDApp開発の完全なツールチェーンです。