LoginSignup
20
15

More than 5 years have passed since last update.

[Ethereum] ウォレット作成

Last updated at Posted at 2018-03-13

概要

geth使ってパブリック・ネットワークのウォレットを作成しようと色々調べていたら、本番と同期した上でpersonalのapi開放しないと無理だったので、もっと敷居の低いwalletを作成する。

構成

──index.html

やること

  1. アドレス + private_keyで送金できるようにする
  2. 環境を選択できるようにする
  3. keystoreからprivate_keyを複合できるようにする
  4. 本番と同期はしない(同期まで2日くらいかかるし数十GBの容量食うし爆音だしでいいことない)

接続先

https://infura.io/
必要な情報を送ると、パプリック・ropstenなどのエンドポイントをメールで送ってくれる。
personalのAPIが開放されていないため、送金などは証明書付きで行う必要がある。

作成

1.js
必要なファイルをcdnにする
https://qiita.com/takanorig/items/89db46120d2ec171e3d8
- keythereum
- ethereumjs-tx
- web3(ver 1.0)

<script src="https://cdn.rawgit.com/ethereumjs/keythereum/622b3a4b/dist/keythereum.min.js"></script>
<script src="https://cdn.rawgit.com/ethereum/web3.js/1.0/dist/web3.min.js"></script>
<script src="https://cdn.rawgit.com/ethereumjs/browser-builds/2fb69a714afe092b06645286f14b94f41e5c062c/dist/ethereumjs-tx.js"></script>

※web3はver1.0(開発中)を利用するため、https://github.com/ethereum/web3.js 
のブランチを『1.0』に切り替えて使用している

機能

  • アカウントを作成 + keystore作成 + ローカル保存

        async function generate_account() {
            ret = await web3.eth.accounts.create();
            s = web3.eth.accounts.encrypt(ret.privateKey, $("#apw").val());
            var blob = new Blob([JSON.stringify(s)], { "type" : "application/octet-stream" });
            url = URL.createObjectURL(blob);
            window.open(url);
            update_addr();
        }
  • keystoreからprivateKeyの復元

            $('#check').click(function(e) {
                var key = prompt('paste keystore(json_string)');
                var keyObj = JSON.parse(key);
                var password = prompt('password?');
                var privateKey=keythereum.recover(password, keyObj);

                console.log(privateKey.toString('hex'));

                $("#address").val(keyObj.address);
                $("#private_key").val(privateKey.toString('hex'));
            });
  • 証明書付き送金

    async function main() {
        // 送信元のアドレス (Walletの作成手順にあるwallet.getAddress()のメソッドによって得られたaddress)     
        const fromAddress = $("#address").val();
        // 送信先のアドレス
        const toAddress = $("#address_to").val();
        // 送信先のアドレス
        const gass_price = "0x" + Number($("#gass_price").val()).toString(16);
        // 送信先のアドレス
        const gass_limit = "0x" + Number($("#gass_limit").val()).toString(16);

        // 著名付きトランザクションのnonce値として必要なため、送信元アドレスのTransaction countを取得する
        const count = await web3.eth.getTransactionCount("0x" +fromAddress);
        const countHex = `0x` + count.toString(16);
        const money = "0x" + Number(web3.utils.toWei($('#money').val(), 'ether')).toString(16);
        //console.log(money);  
        // 上記のWallet作成手順にある、wallet.getPrivateKeyString()によって得られた、Private keyの値を設定する
        const privateKeyStr = $("#private_key").val();
        let privateKey = new EthJS.Buffer.Buffer(privateKeyStr, 'hex');

        // transactionのパラメータの設定
        const txParams = {
            nonce: countHex,
            // 適切なgasPriceとgasLimitを設定する
//    gasPrice: '0x09184e72a000',
//    gasLimit: '0x30000',
            gasPrice: gass_price,
            gasLimit: gass_limit,
            to: toAddress,
            // 送金する金額
            value: money
        };
        console.log(txParams);
        web3.eth.estimateGas(txParams,function(error, result){
            console.log("gasLimit:");
            console.log(result.toString(16));
        });
        // Transactionオブジェクトの生成
        const tx = new EthJS.Tx(txParams);
        // 秘密鍵での署名 
        tx.sign(privateKey);
        // Transactionオブジェクトをシリアライズして、16進数で表現
        const serializedTx = tx.serialize();
        const rawTx = '0x' + serializedTx.toString('hex');
        // 署名付きトランザクションの送信
        web3.eth.sendSignedTransaction(rawTx)
        .on('transactionHash', function(hash){
          console.log('transactionHash');
          web3.eth.getTransaction(hash).then(alertJSON);
        })
        .on('receipt', function(receipt){
          console.log('receipt');
        })
        .on('confirmation', function(confirmationNumber, receipt){
          console.log('confirmation');
        })
        .on('error', function(error){
          if (error.indexOf("Transaction was not mined within 50 blocks")) {
            return;
          }
          alert(error);
          console.log(error);
        }); 
    }

※web3のバグで50秒後に『Transaction was not mined within 50 blocks』のエラーが飛んでくるため、無視する。

全体


<html>
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" type="text/css" media="all" />
</head>
<body>

<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdn.rawgit.com/ethereumjs/keythereum/622b3a4b/dist/keythereum.min.js"></script>
<script src="https://cdn.rawgit.com/ethereum/web3.js/1.0/dist/web3.min.js"></script>
<script src="https://cdn.rawgit.com/ethereumjs/browser-builds/2fb69a714afe092b06645286f14b94f41e5c062c/dist/ethereumjs-tx.js"></script>
<script type="text/javascript">
    var price = null;
    const web3 = new Web3();
    web3.setProvider(new web3.providers.HttpProvider('http://localhos:8545'));
    function setGasPrice(){
        web3.eth.getGasPrice(function(error, result){
            console.log(result.toString());
            $("#gass_price").val(result.toString());
        });
    }

    async function main() {
        // 送信元のアドレス (Walletの作成手順にあるwallet.getAddress()のメソッドによって得られたaddress)     
        const fromAddress = $("#address").val();
        // 送信先のアドレス
        const toAddress = $("#address_to").val();
        // 送信先のアドレス
        const gass_price = "0x" + Number($("#gass_price").val()).toString(16);
        // 送信先のアドレス
        const gass_limit = "0x" + Number($("#gass_limit").val()).toString(16);

        // 著名付きトランザクションのnonce値として必要なため、送信元アドレスのTransaction countを取得する
        const count = await web3.eth.getTransactionCount("0x" +fromAddress);
        const countHex = `0x` + count.toString(16);
        const money = "0x" + Number(web3.utils.toWei($('#money').val(), 'ether')).toString(16);
        //console.log(money);  
        // 上記のWallet作成手順にある、wallet.getPrivateKeyString()によって得られた、Private keyの値を設定する
        const privateKeyStr = $("#private_key").val();
        let privateKey = new EthJS.Buffer.Buffer(privateKeyStr, 'hex');

        // transactionのパラメータの設定
        const txParams = {
            nonce: countHex,
            // 適切なgasPriceとgasLimitを設定する
//    gasPrice: '0x09184e72a000',
//    gasLimit: '0x30000',
            gasPrice: gass_price,
            gasLimit: gass_limit,
            to: toAddress,
            // 送金する金額
            value: money
        };
        console.log(txParams);
        web3.eth.estimateGas(txParams,function(error, result){
            console.log("gasLimit:");
            console.log(result.toString(16));
        });
        // Transactionオブジェクトの生成
        const tx = new EthJS.Tx(txParams);
        // 秘密鍵での署名 
        tx.sign(privateKey);
        // Transactionオブジェクトをシリアライズして、16進数で表現
        const serializedTx = tx.serialize();
        const rawTx = '0x' + serializedTx.toString('hex');
        // 署名付きトランザクションの送信
        web3.eth.sendSignedTransaction(rawTx)
        .on('transactionHash', function(hash){
          console.log('transactionHash');
          web3.eth.getTransaction(hash).then(alertJSON);
        })
        .on('receipt', function(receipt){
          console.log('receipt');
        })
        .on('confirmation', function(confirmationNumber, receipt){
          console.log('confirmation');
        })
        .on('error', function(error){
          if (error.indexOf("Transaction was not mined within 50 blocks")) {
            return;
          }
          alert(error);
          console.log(error);
        }); 
    }
</script>
<div class="container">
    <h2>ローカル ウォレット</h2>
    <div class="panel panel-default">
        <div class="panel-body">
            <div class="row">
                <div class="col-sm-4"><label>送り主 / private_key</label></div>
                <div class="col-sm-8"><input type="text" id="address"/><input type="password" id="private_key"/></div>
            </div>
            <div class="row">
                <div class="col-sm-4"><label>送り先 / 金額(eth)</label></div>
                <div class="col-sm-8"><input type="text" id="address_to"/><input type="text" id="money" value="1"/></div>
            </div>
            <div class="row">
                <div class="col-sm-4"><label>gass price(wei) / gass limit</label></div>
                <div class="col-sm-8"><input type="text" id="gass_price" value="10000000000000"/><input type="text" id="gass_limit" value="196608"/></div>
            </div>
            <div class="row">
                <div class="col-sm-4"></div>
                <div class="col-sm-8"><input type="button" class="btn btn-primary active" id="go" value="送金"/></div>
            </div>
            <hr/>
            <div class="row">
                <div class="col-sm-4"><label>キーストア読み込み</label></div>
                <div class="col-sm-8"><input type="button" class="btn btn-primary active" id="check" value="keystore + password -> private_key"/></div>
            </div>
            <hr/>
            <div class="row">
                <div class="col-sm-4"><label>アカウント作成(パスワード入力)</label></div>
                <div class="col-sm-8">
                    <input type="password" id="apw" value=""/>
                    <input type="button" class="btn btn-primary active" id="create" value="登録"/>
                </div>
            </div>

            <div class="row">
                <div class="col-sm-4" id="keystore"></div>
            </div>

            <hr/>
<!-- 接続先。infura.ioに登録してエンドポイントを取得し、ここに貼り付ける。geth立ち上げて指定するのもあり -->
            <div class="row">
                <div class="col-sm-12">
                    <input type="radio" name=host value="https://mainnet.infura.io/xxxxxxxxxxxxxxx"> Ethereum<br/>
                    <input type="radio" name=host value="http://localhost:8545" checked="checked"> private_net(local)<br/>
                    <input type="radio" name=host value="https://ropsten.infura.io/xxxxxxxxxx"> test_net(Ropsten)<br/>
                </div>
            </div>
            <hr/>
            <div class="row">
                <div class="col-sm-12">mining : <label id="mining" /></label></div>
            </div>
        </div>
    </div>
    <hr/>
    <label> addr_list</label>
    <ul id="demo">
    </ul>

    <script type="text/javascript">
        function alertJSON(json) {
            alert(JSON.stringify(json));
        }
        function logDemoElements(elements) {
            ul = $("#demo");
            elements.forEach(function(val){
                ul.append('<li class="addr">' + val + "</li>");
            });
        }
        function update_addr() {
            $("#demo li").remove();
            web3.eth.getAccounts().then(logDemoElements);
        }
        function update_wallet() {
            $("#demo li").remove();
            web3.eth.getAccounts().then(logDemoElements);
        }

        async function generate_account() {
            ret = await web3.eth.accounts.create();
            s = web3.eth.accounts.encrypt(ret.privateKey, $("#apw").val());
            var blob = new Blob([JSON.stringify(s)], { "type" : "application/octet-stream" });
            url = URL.createObjectURL(blob);
            window.open(url);
            update_addr();
        }

        $(function() {
            $( 'input[name="host"]:radio' ).change( function() {
                console.log( $( this ).val()); // valueを表示  
                web3.setProvider(new web3.providers.HttpProvider($( this ).val()));
                update_addr();
                setGasPrice();
            });

            $('#go').click(function(e) {
                main();
            });
            $('#create').click(function(e) {
                generate_account();
            });

            $('#demo').on('click','.addr', function(){
                $("#address_to").val(this.innerText);
            });

            // アカウント作成
            $('#check').click(function(e) {
                var key = prompt('paste keystore(json_string)');
                var keyObj = JSON.parse(key);
                var password = prompt('password?');
                var privateKey=keythereum.recover(password, keyObj);

                console.log(privateKey.toString('hex'));

                $("#address").val(keyObj.address);
                $("#private_key").val(privateKey.toString('hex'));
            });
            $(document).ready(function(){
                update_addr();
                setGasPrice();
            });
        });
    </script>
</body>
</html>

スクリーンショット 2018-03-12 19.14.34.png

  • 送信元 / privateKey / 送信先 / 金額を入力して送金ができる
  • キーストア読み込みボタンを押すと入力boxが出るので、keystore内のjsonをテキストに貼り付ける
    • ok押下後パスワードを聞かれるのでパスワード入力
    • 少し待つと送信元とprivateKeyのテキストボックスに値が入力される
  • アカウント作成を行うと、keystoreファイルがダウンロードされる
  • ラジオボタン選択で接続先を切り替える。接続先はinfura.ioに登録して取得なりGeth使って自分で立ち上げるなりする
  • 自前のgethでノードを立ち上げた場合、そこで作成したアカウントはadd_listに表示される。選択すると送金先のテキストにアドレスをコピー

参考

geth consoleを使わずJavaScriptのみでブロックチェーン(Ethereum)を操作する方法
https://qiita.com/fukumame55/items/e631119877c4cd77d0a5

20
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
15