Waves CUSTOMノードを立てる

Wavesフルノードには三つの種類があって、設定ファイルで切り替えられる。

  1. Mainnet
  2. Testnet
  3. Cuscom

Mainnet、Testnetはすでに稼働中のブロックチェーンに参加する。
一方、Customは勝手に新たなWavesブロックチェーンを立ち上げることができる。

WavesのGitHubに設定ファイル例があるが、
独自にやるには、このうちgenesisブロックを埋めなければならない。
すなわち、

  1. アドレス(ウォレット)を生成
  2. GenesisブロックのSignatureを生成

を行う必要がある。

ウォレットは前回解決済みなので、 GenesisブロックSignatureの生成を行う

Genesisブロック

Waves GitHub Wikiにデータ構造が書いてあるが、

例によって正しく読み取れなかったので実装をあてにする。

このファイルdef genesis()が署名の検証をやっているっぽく、

  • 署名鍵のseedは空(なし)
  • reference (データ構造上はparent block signature)は ALL 0xFF
  • consensus data (データ構造上はgeneration signature)は ALL 0x00

またトランザクションデータ(TransactionsBlockFieldVersion1or2())は、

  • 先頭にトランザクション数(1バイト)
  • 各トランザクションの先頭に4バイトのデータ長が付く

と読み取れる。

Pythonで書いてみる

Python で Genesis Block Signature を生成

対象のgenesisブロック

custom {
    address-scheme-character: "Z"
    genesis {
        average-block-delay: 60s
        initial-base-target: 153722867
        timestamp = 1504915200000
        block-timestamp = 1504915200000
        signature: ""
        initial-balance: 10000000000000000
        transactions = [
            # seed : 5AX9YLXLNJxLHhzP1P2SvHXLx
            {recipient: "3QUKxuTp24VNwhHDqAkYTGWKYDNz9wUAQaA", amount: 10000000000000000},
        ]
    }
}

Python

#!/usr/bin/env python3
# coding: utf-8

import base58
import hashlib
import axolotl_curve25519 as curve
import struct
import os


def genesis_transaction(timestamp, recipient, amount):
    data = b'\01' + \
           struct.pack('>Q', timestamp) + \
           recipient + \
           struct.pack('>Q', amount)

    data = struct.pack('>L', len(data)) + data

    return data


def genesis_block_signature():
    """genesis block sig"""

    # 2017-09-09 00:00:00 GMT
    transaction_timestamp = 1504915200000
    # 2017-09-09 00:00:00 GMT
    block_timestamp = 1504915200000

    initial_balance = 10000000000000000
    initial_base_target = 153722867

    transactions = (
        ('3QUKxuTp24VNwhHDqAkYTGWKYDNz9wUAQaA', initial_balance),
    )

    transaction_data = struct.pack('B', len(transactions))
    for tx in transactions:
        transaction_data += genesis_transaction(transaction_timestamp, base58.b58decode(tx[0]), tx[1])

    # sign
    private_key = curve.generatePrivateKey(hashlib.sha256(b'').digest())
    public_key = curve.generatePublicKey(private_key)

    parent_block_sig = b'\xFF' * 64
    previous_block_gen_sig = b'\x00' * 32

    sign_data = b'\x01' + \
                struct.pack('>Q', block_timestamp) + \
                parent_block_sig + \
                struct.pack('>L', 40) + \
                struct.pack('>Q', initial_base_target) + \
                previous_block_gen_sig + \
                struct.pack('>L', len(transaction_data)) + \
                transaction_data + \
                public_key

    sigunature = curve.calculateSignature(os.urandom(64), private_key, sign_data)

    print('sigunature = {0}'.format(base58.b58encode(sigunature)))


def main():
    """waves custom"""
    genesis_block_signature()


if __name__ == '__main__':
    main()

出力されたsignatureを先ほどの設定ファイルに記入し、Wavesフルノードを実行すればCUSTOMで動き出す。
やったね。

最終的な設定ファイル含めGitHubへ

留意点

署名に使っているライブラリ(axolotl_curve25519)は、 メッセージ(署名対象データ)サイズに256バイト以下の制限があるので、 genesis_transactionは二つまで。

CUSTOMの良いところ

  • 誰にも迷惑かからない
  • ブロックチェーンがすぐ同期
  • 手数料のかかる処理を気兼ねなく試せる
  • 困ったら最初からやり直しできる

など。

クライアントとか別途必要だけどね。

追記

2017-11-18

Wavesの Github Wiki に解説が作成されていた。


See also