Node.js環境では確認できたが、それをブラウザ上で実行できるコードに変換することができなかった。

情報源

モナレッジの作者らいうさんの記事でした。感謝。

ライブラリ

これを使って実現できるらしい。だが、Node.js用ライブラリ。これはブラウザ上では動作しない。

まずNode.js上で動作確認してみるか。

要点

ひとまず要点だけを抜き出してみる。

const verify = bitcoinMessage.verify(message, address, signature, messagePrefix)

このverifyで、指定したアドレスの本人確認ができると思われる。

戻り値verifyはBoolean。trueならそのアドレスは本人である。falseならそのアドレスは本人でない。

引数 概要
message mpurse.signMessageに渡した署名メッセージ。
address 本人確認したいアドレス
signature mpurse.signMessage(message)の戻り値
messagePrefix \x19Monacoin Signed Message:\n固定(モナコイン用)

つまり以下のようになる。

const address = await window.mpurse.getAddress()
const message = "Please sign this message :" + new Date.getTime();
const signature = await window.mpurse.signMessage(message);
const messagePrefix = "\x19Monacoin Signed Message:\n"
const verify = bitcoinMessage.verify(message, address, signature, messagePrefix)

私の目的は、SNSのプロフィールにあるアドレス文字列が本人のものかを確認すること。なので上記を実行してverifytrueであり、かつSNSのプロフィールにあるアドレス文字列がawait window.mpurse.getAddress()の戻り値と同じだったら、本人確認できたものと判断できるはず。

ようするに本人が本人のアドレスを、本人のプロフィール内に書いたと判断できる。このときだけ、このアドレスとプロフィール情報を、どうにかしてどこかのサーバに保存したり更新したり取得したりできるようにしたい。

そのためには、まずアドレスの本人確認メソッドが動作するかを確認するのが先決。

動作確認

シグネチャの確認

mpurseを動作させるにはHTTPSサーバでないとダメ。でも私のローカル環境ではその準備がない。なのでGitHub Pagesを借りる。専用ページを用意してpurse APIを使う。

これで、指定したアドレスで、指定したメッセージを渡したときの、mpurse.signMessag()の戻り値を確認できる。

そのときのアドレス、メッセージ、戻り値のシグネチャの3点をメモっておく。おそらくシグネチャは他人に知られるとマズイので、ここでは伏せておく。

verifyメソッドの確認

以下のコードで上記3点を入力し、verifytrueであることを確認する。

var bitcoinMessage = require('bitcoinjs-message')

//var address = await window.mpurse.getAddress()
const address = '上記で指定したアドレス'
//var message = "Please sign this message :" + new Date.getTime();
const message = "上記で指定したメッセージ";
//const signature = await window.mpurse.signMessage(message);
const signature = '上記で返ってきたシグネチャ'
const messagePrefix = "\x19Monacoin Signed Message:\n"
const verify = bitcoinMessage.verify(message, address, signature, messagePrefix)
console.log(`verify:`, verify);

このコードはNode.jsで実行する必要がある。参考情報は以下。

Node.jsをインストールする

私のマシンはラズパイ4なので、そいつにNode.jsとnpmをインストールする。以下コマンドで。

sudo apt install -y nodejs npm
sudo npm cache clean
sudo npm install -g n
sudo n stable
sudo apt-get purge -y nodejs npm

新しい端末を起動して以下を確認する。

node -v
npm -v

新しいプロジェクトを作る。

mkdir verify-test
npm init

ハローワールド。

vim index.js
console.log('Hello World!');

実行する。

node index.js

verifyの動作確認

bitcoinjs-messageを入れる。

npm i bitcoinjs-message

index.jsに先述のコードを貼り付ける。

var bitcoinMessage = require('bitcoinjs-message')

//var address = await window.mpurse.getAddress()
const address = '上記で指定したアドレス'
//var message = "Please sign this message :" + new Date.getTime();
const message = "上記で指定したメッセージ";
//const signature = await window.mpurse.signMessage(message);
const signature = '上記で返ってきたシグネチャ'
const messagePrefix = "\x19Monacoin Signed Message:\n"
const verify = bitcoinMessage.verify(message, address, signature, messagePrefix)
console.log(`verify:`, verify);

実行する。

node index.js
verify: true

trueが返ってきた。ちゃんとアドレスが本人であることを確認できた!

ブラウザ上で動作するようにしたい

あとはこのverifyメソッドがブラウザで動作してくれたらいい。いつも使うライブラリはCDNが用意されているので、それを使うだけなのだが、今回はそれがなかった。

仕方ないので、元のコードからどうにか自力で、ブラウザで動作するコードを出力するしかない。webpackを使って吐き出させようとした。

結論からいうと、できなかった。途中までの成果物は以下。

  1. 元のコードからフォークする
  2. webpackをインストールする
  3. 設定ファイルを適当にググってそれっぽくした
  4. エラーになった

参考情報は以下。

package.json

var bitcoinMessage = require('bitcoinjs-message')
export class MonaMessage {
    verify() {
        const messagePrefix = "\x19Monacoin Signed Message:\n"
        return bitcoinMessage.verify(message, address, signature, messagePrefix)
    }
}

package.json

元のコードの"scripts":に以下を追記した。

    "dev": "webpack --mode development",
    "build": "webpack --mode production",
    "watch": "webpack --mode development --watch"

最終的には以下。

{
  "name": "bitcoinjs-message",
  "version": "2.2.0",
  "description": "bitcoinjs-message",
  "keywords": [
    "bitcoinjs-message",
    "bitcoin"
  ],
  "homepage": "https://github.com/bitcoinjs/bitcoinjs-message",
  "bugs": {
    "url": "https://github.com/bitcoinjs/bitcoinjs-message/issues"
  },
  "license": "MIT",
  "files": [
    "index.js",
    "index.d.ts"
  ],
  "main": "./index.js",
  "types": "./index.d.ts",
  "repository": {
    "type": "git",
    "url": "https://github.com/bitcoinjs/bitcoinjs-message.git"
  },
  "scripts": {
    "coverage": "nyc tape test/*.js",
    "prepublish": "npm run test",
    "standard": "standard",
    "test": "npm run standard && npm run unit",
    "unit": "tape test/*.js",
    "dev": "webpack --mode development",
    "build": "webpack --mode production",
    "watch": "webpack --mode development --watch"
  },
  "dependencies": {
    "babel-loader": "^8.2.5",
    "bech32": "^1.1.3",
    "bitcoinjs-message": "^2.2.0",
    "bs58check": "^2.1.2",
    "buffer-equals": "^1.0.3",
    "create-hash": "^1.1.2",
    "secp256k1": "^3.0.1",
    "varuint-bitcoin": "^1.0.1"
  },
  "devDependencies": {
    "bigi": "^1.4.1",
    "bitcoinjs-lib": "^3.2.0",
    "nyc": "^14.1.1",
    "standard": "^12.0.1",
    "tape": "^4.5.1",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  },
  "engines": {
    "node": ">=0.10"
  }
}

webpack.config.js

const path = require('path')
module.exports = {
    entry: {
        'mona-message': [`./mona-message.js`],
    },
    output: {
        path: path.join(__dirname, '/'),
        publicPath: '/',
        filename: `[name].min.js`,
        library: 'monacoinjs-message',
        libraryExport: 'default',
        libraryTarget: 'umd',
        globalObject: 'this',
    },
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            use: [{loader: 'babel-loader'}]
        }]
    },
    devtool: 'inline-source-map',
};

エラー内容

$ npm run dev

> bitcoinjs-message@2.2.0 dev
> webpack --mode development

asset mona-message.min.js 1.17 MiB [emitted] (name: mona-message)
runtime modules 1010 bytes 5 modules
modules by path ./node_modules/ 435 KiB
  javascript modules 432 KiB 79 modules
  json modules 3.31 KiB
    ./node_modules/secp256k1/lib/messages.json 2.2 KiB [built] [code generated]
    ./node_modules/elliptic/package.json 1.11 KiB [built] [code generated]
optional modules 30 bytes [optional]
  buffer (ignored) 15 bytes [optional] [built] [code generated]
  crypto (ignored) 15 bytes [optional] [built] [code generated]
./mona-message.js 236 bytes [built] [code generated]
util (ignored) 15 bytes [built] [code generated]
util (ignored) 15 bytes [built] [code generated]

ERROR in ./node_modules/cipher-base/index.js 2:16-43
Module not found: Error: Can't resolve 'stream' in '/tmp/work/bitcoinjs-message-webpack-20220614/node_modules/cipher-base'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
	- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "stream": false }
 @ ./node_modules/create-hash/browser.js 6:11-33
 @ ./node_modules/bitcoinjs-message/index.js 4:19-41
 @ ./mona-message.js 1:21-49

ERROR in ./node_modules/readable-stream/lib/_stream_readable.js 46:13-37
Module not found: Error: Can't resolve 'buffer' in '/tmp/work/bitcoinjs-message-webpack-20220614/node_modules/readable-stream/lib'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
	- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "buffer": false }
 @ ./node_modules/readable-stream/readable-browser.js 1:10-63
 @ ./node_modules/hash-base/index.js 3:16-52
 @ ./node_modules/md5.js/index.js 3:15-35
 @ ./node_modules/create-hash/browser.js 3:10-27
 @ ./node_modules/bitcoinjs-message/index.js 4:19-41
 @ ./mona-message.js 1:21-49

ERROR in ./node_modules/readable-stream/lib/_stream_writable.js 70:13-37
Module not found: Error: Can't resolve 'buffer' in '/tmp/work/bitcoinjs-message-webpack-20220614/node_modules/readable-stream/lib'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
	- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "buffer": false }
 @ ./node_modules/readable-stream/readable-browser.js 4:0-55
 @ ./node_modules/hash-base/index.js 3:16-52
 @ ./node_modules/md5.js/index.js 3:15-35
 @ ./node_modules/create-hash/browser.js 3:10-27
 @ ./node_modules/bitcoinjs-message/index.js 4:19-41
 @ ./mona-message.js 1:21-49

ERROR in ./node_modules/readable-stream/lib/internal/streams/buffer_list.js 15:15-32
Module not found: Error: Can't resolve 'buffer' in '/tmp/work/bitcoinjs-message-webpack-20220614/node_modules/readable-stream/lib/internal/streams'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
	- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "buffer": false }
 @ ./node_modules/readable-stream/lib/_stream_readable.js 72:17-58
 @ ./node_modules/readable-stream/readable-browser.js 1:10-63
 @ ./node_modules/hash-base/index.js 3:16-52
 @ ./node_modules/md5.js/index.js 3:15-35
 @ ./node_modules/create-hash/browser.js 3:10-27
 @ ./node_modules/bitcoinjs-message/index.js 4:19-41
 @ ./mona-message.js 1:21-49

ERROR in ./node_modules/ripemd160/index.js 2:13-37
Module not found: Error: Can't resolve 'buffer' in '/tmp/work/bitcoinjs-message-webpack-20220614/node_modules/ripemd160'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
	- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "buffer": false }
 @ ./node_modules/create-hash/browser.js 4:16-36
 @ ./node_modules/bitcoinjs-message/index.js 4:19-41
 @ ./mona-message.js 1:21-49

ERROR in ./node_modules/safe-buffer/index.js 3:13-30
Module not found: Error: Can't resolve 'buffer' in '/tmp/work/bitcoinjs-message-webpack-20220614/node_modules/safe-buffer'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
	- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "buffer": false }
 @ ./node_modules/bs58check/base.js 4:13-42
 @ ./node_modules/bs58check/index.js 4:20-37
 @ ./node_modules/bitcoinjs-message/index.js 1:18-38
 @ ./mona-message.js 1:21-49

6 errors have detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.

webpack 5.73.0 compiled with 6 errors in 3599 ms

予備知識がなさすぎて、どうにもできない。たぶんライブラリを読み込めてないっぽい。Module not foundと言っているので。

これを読み解いてどうこうしようという気が今の私にはない。遠回りになりすぎる。

私にとってNode.jsやwebpackを触るのは難易度が高すぎる。わずかなバージョン差で設定ファイルを変えたり、インストールするパッケージの名前やバージョンの互換性がなくなったりする。そういうのに振り回されたくない。これをやる前に、もっとやりたいことがある。

結論

今の私の技術力では、ブラウザ上でアドレスの本人確認ができない。とりあえず渋々、諦める。