bitcoin.confの構成

mainnet=1               # mainnetを利用する
txindex=1               # indexを作成して全てのトランザクションIDを参照可能にする
server=1                # JSON RPCサーバとしてコマンドを受け付ける
rest=1                  # REST インターフェースを有効にする
rpcuser= "Username"     # JSON RPCのためのユーザ名
rpcpassword= "Password" # JSON RPCのためのパスワード
rpcport=8332            # JSON RPC用ポート番号

gen-bitcoin-conf.sh

export LC_ALL=C
TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)}
BUILDDIR=${BUILDDIR:-$TOPDIR}
BINDIR=${BINDIR:-$BUILDDIR/src}
BITCOIND=${BITCOIND:-$BINDIR/bitcoind}
SHARE_EXAMPLES_DIR=${SHARE_EXAMPLES_DIR:-$TOPDIR/share/examples}
EXAMPLE_CONF_FILE=${EXAMPLE_CONF_FILE:-$SHARE_EXAMPLES_DIR/bitcoin.conf}

[ ! -x "$BITCOIND" ] && echo "$BITCOIND not found or not executable." && exit 1

DIRTY=""
VERSION_OUTPUT=$($BITCOIND --version)
if [[ $VERSION_OUTPUT == *"dirty"* ]]; then
  DIRTY="${DIRTY}${BITCOIND}\n"
fi

if [ -n "$DIRTY" ]
then
  echo -e "WARNING: $BITCOIND was built from a dirty tree.\n"
  echo -e "To safely generate a bitcoin.conf file, please commit your changes to $BITCOIND, rebuild, then run this script again.\n"
fi

echo 'Generating example bitcoin.conf file in share/examples/'

# create the directory, if it doesn't exist
mkdir -p "${SHARE_EXAMPLES_DIR}"

# create the header text
cat > "${EXAMPLE_CONF_FILE}" << 'EOF'
##
## bitcoin.conf configuration file.
## Generated by contrib/devtools/gen-bitcoin-conf.sh.
##
## Lines beginning with # are comments.
## All possible configuration options are provided. To use, copy this file
## to your data directory (default or specified by -datadir), uncomment
## options you would like to change, and save the file.
##


### Options
EOF

# parse the output from bitcoind --help
# adding newlines is a bit funky to ensure portability for BSD
# see here for more details: https://stackoverflow.com/a/24575385
${BITCOIND} --help \
    | sed '1,/Print this help message and exit/d' \
    | sed -E 's/^[[:space:]]{2}\-/#/' \
    | sed -E 's/^[[:space:]]{7}/# /' \
    | sed -E '/[=[:space:]]/!s/#.*$/&=1/' \
    | awk '/^#[a-z]/{x=$0;next}{if (NF==0) print x"\n",x="";else print}' \
    | sed 's,\(^[[:upper:]].*\)\:$,\
### \1,' \
    | sed 's/[[:space:]]*$//' >> "${EXAMPLE_CONF_FILE}"

# create the footer text
cat >> "${EXAMPLE_CONF_FILE}" << 'EOF'

# [Sections]
# Most options will apply to all networks. To confine an option to a specific
# network, add it under the relevant section below.
#
# Note: If not specified under a network section, the options addnode, connect,
# port, bind, rpcport, rpcbind, and wallet will only apply to mainnet.

# Options for mainnet
[main]

# Options for testnet
[test]

# Options for signet
[signet]

# Options for regtest
[regtest]
EOF

sedの使い方

1.rakus rakus
2.rakus

$ sed -e ‘s/rakus/RAKUS/g’ sample.tx
1.RAKUS RAKUS
2.RAKUS

-Eのところは複数指定にしている?

${BITCOIND} --help \
    | sed '1,/Print this help message and exit/d' \
    | sed -E 's/^[[:space:]]{2}\-/#/' \
    | sed -E 's/^[[:space:]]{7}/# /' \
    | sed -E '/[=[:space:]]/!s/#.*$/&=1/' \
    | awk '/^#[a-z]/{x=$0;next}{if (NF==0) print x"\n",x="";else print}' \
    | sed 's,\(^[[:upper:]].*\)\:$,\
### \1,' \
    | sed 's/[[:space:]]*$//' >> "${EXAMPLE_CONF_FILE}"

[shell] manページを作ろう

シェルで引数を

#!/bin/bash

if [ $# -ne 2 ]; then
    echo "指定された引数は$#個です。" 1>&2
    echo "実行するには2個の引数が必要です。" 1>&2
    exit 1
fi

cat <<__EOT__
指定された引数は、
    $1
    $2
の$#個です。
__EOT__

exit 0

上記をベースに、引数で表示するmanを変えていきます。caseで引数に応じて出し分ける。

#!/bin/bash

output() {
    case $1 in
        "daemon")
                cat <<__EOT__
            NAME daemon
            SYNOPOIS A program that resides in main memory and provides specific functions
__EOT__
    exit 0 ;;
            
        "cli")
                cat <<__EOT__
            NAME cli
            SYNOPOIS A text-based interface that allows you to enter commands to interact with your computer's operating system
__EOT__
    exit 0 ;;   
    esac
}

if [ $1 == "man" ]; then
    if [ $# -ne 2 ]; then
        echo "Please enter arguments after man." 1>&2
        exit 1
    else
        output $2
    fi
fi

$ ./test.sh man daemon
NAME daemon
SYNOPOIS A program that resides in main memory and provides specific functions
$ ./test.sh man cli
NAME cli
SYNOPOIS A text-based interface that allows you to enter commands to interact with your computer’s operating system

gen-manpage.pyだと、コンテンツは別ページに持っているようです。topディレクトリにディレクトリ名を繋げて参照しています。
os.getenvは環境変数を持ってくる

import os
import subprocess
import sys
import tempfile

BINARIES = [
'src/bitcoind',
'src/bitcoin-cli',
'src/bitcoin-tx',
'src/bitcoin-wallet',
'src/bitcoin-util',
'src/qt/bitcoin-qt',
]

# Paths to external utilities.
git = os.getenv('GIT', 'git')
help2man = os.getenv('HELP2MAN', 'help2man')

# If not otherwise specified, get top directory from git.
topdir = os.getenv('TOPDIR')
if not topdir:
    r = subprocess.run([git, 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE, check=True, text=True)
    topdir = r.stdout.rstrip()

# Get input and output directories.
builddir = os.getenv('BUILDDIR', topdir)
mandir = os.getenv('MANDIR', os.path.join(topdir, 'doc/man'))

# Verify that all the required binaries are usable, and extract copyright
# message in a first pass.
versions = []
for relpath in BINARIES:
    abspath = os.path.join(builddir, relpath)
    try:
        r = subprocess.run([abspath, "--version"], stdout=subprocess.PIPE, check=True, text=True)
    except IOError:
        print(f'{abspath} not found or not an executable', file=sys.stderr)
        sys.exit(1)
    # take first line (which must contain version)
    verstr = r.stdout.splitlines()[0]
    # last word of line is the actual version e.g. v22.99.0-5c6b3d5b3508
    verstr = verstr.split()[-1]
    assert verstr.startswith('v')
    # remaining lines are copyright
    copyright = r.stdout.split('\n')[1:]
    assert copyright[0].startswith('Copyright (C)')

    versions.append((abspath, verstr, copyright))

if any(verstr.endswith('-dirty') for (_, verstr, _) in versions):
    print("WARNING: Binaries were built from a dirty tree.")
    print('man pages generated from dirty binaries should NOT be committed.')
    print('To properly generate man pages, please commit your changes (or discard them), rebuild, then run this script again.')
    print()

with tempfile.NamedTemporaryFile('w', suffix='.h2m') as footer:
    # Create copyright footer, and write it to a temporary include file.
    # Copyright is the same for all binaries, so just use the first.
    footer.write('[COPYRIGHT]\n')
    footer.write('\n'.join(versions[0][2]).strip())
    footer.flush()

    # Call the binaries through help2man to produce a manual page for each of them.
    for (abspath, verstr, _) in versions:
        outname = os.path.join(mandir, os.path.basename(abspath) + '.1')
        print(f'Generating {outname}…')
        subprocess.run([help2man, '-N', '--version-string=' + verstr, '--include=' + footer.name, '-o', outname, abspath], check=True)

manページの作り方

マニュアルはUbuntuだと $ man ${command} で表示される。
上部にdescriptionがあり、オプションの説明、その後はauthor, bug, report, copyrightなどだ。

以下はcatの例
CAT(1) User Commands CAT(1)

NAME
cat – concatenate files and print on the standard output

SYNOPSIS
cat [OPTION]… [FILE]…

DESCRIPTION
Concatenate FILE(s) to standard output.

With no FILE, or when FILE is -, read standard input.

-A, –show-all
EXAMPLES
cat f – g
Output f’s contents, then standard input, then g’s contents.

cat Copy standard input to standard output.

AUTHOR
Written by Torbjorn Granlund and Richard M. Stallman.

REPORTING BUGS
GNU coreutils online help:
Report any translation bugs to
COPYRIGHT
Copyright © 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL
version 3 or later .
This is free software: you are free to change and redistribute it. There is
NO WARRANTY, to the extent permitted by law.

SEE ALSO
tac(1)

Full documentation
or available locally via: info ‘(coreutils) cat invocation’

GNU coreutils 8.32 February 2022 CAT(1)

シェルで受けっとって表示か?

PythonでCopyrightを自動挿入

まず対象となるファイルを適当に作成します。
script.cpp

#include "headers.h"

bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
file_name = './script.cpp'

with open(file_name) as f:
    data = f.readlines()

data.insert(1, '// Copyright (c) 2009 Satoshi Nakamoto\n')

with open(file_name, mode='w') as f:
    f.writelines(data)

これだと、全部書き変わってしまう…

copyright_header.pyだと、作者をファイルごとに定義しています。

COPYRIGHT_WITH_C = r'Copyright \(c\)'
COPYRIGHT_WITHOUT_C = 'Copyright'
ANY_COPYRIGHT_STYLE = '(%s|%s)' % (COPYRIGHT_WITH_C, COPYRIGHT_WITHOUT_C)

YEAR = "20[0-9][0-9]"
YEAR_RANGE = '(%s)(-%s)?' % (YEAR, YEAR)
YEAR_LIST = '(%s)(, %s)+' % (YEAR, YEAR)
ANY_YEAR_STYLE = '(%s|%s)' % (YEAR_RANGE, YEAR_LIST)
ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE = ("%s %s" % (ANY_COPYRIGHT_STYLE,
                                                ANY_YEAR_STYLE))

ANY_COPYRIGHT_COMPILED = re.compile(ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE)

def compile_copyright_regex(copyright_style, year_style, name):
    return re.compile(r'%s %s,? %s( +\*)?\n' % (copyright_style, year_style, name))

EXPECTED_HOLDER_NAMES = [
    r"Satoshi Nakamoto",
    r"The Bitcoin Core developers",
    r"BitPay Inc\.",
    r"University of Illinois at Urbana-Champaign\.",
    r"Pieter Wuille",
    r"Wladimir J\. van der Laan",
    r"Jeff Garzik",
    r"Jan-Klaas Kollhof",
    r"ArtForz -- public domain half-a-node",
    r"Intel Corporation ?",
    r"The Zcash developers",
    r"Jeremy Rubin",
]

f.writelines()ではなく、f.write(”.join())で関数と引数にして書いてます。

################################################################################
# read and write to file
################################################################################

def read_file_lines(filename):
    with open(filename, 'r', encoding="utf8") as f:
        file_lines = f.readlines()
    return file_lines

def write_file_lines(filename, file_lines):
    with open(filename, 'w', encoding="utf8") as f:
        f.write(''.join(file_lines))

なるほど、Pythonならギリまだいけるな..

LLVMとは?

様々なプログラミング言語に対し、様々なコンピュータやOSに対応させることができる共通基盤
中間言語に変換し、中間コードは、特定の機種やOSで直に動作するネイティブコードに変換して実行される
実行時に変換しながら実行するのは仮想マシン
RustやSwiftはLLVMの利用を前提としている

$ sudo apt install clang -y
$ clang -S -emit-llvm -O3 helloworld.c
$ cat helloworld.ll

; ModuleID = 'helloworld.c'
source_filename = "helloworld.c"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"

@str = private unnamed_addr constant [14 x i8] c"Hello, world!\00", align 1

; Function Attrs: nofree nounwind uwtable
define dso_local i32 @main(i32 noundef %0, i8** nocapture noundef readnone %1) local_unnamed_addr #0 {
  %3 = tail call i32 @puts(i8* nonnull dereferenceable(1) getelementptr inbounds ([14 x i8], [14 x i8]* @str, i64 0, i64 0))
  ret i32 0
}

; Function Attrs: nofree nounwind
declare noundef i32 @puts(i8* nocapture noundef readonly) local_unnamed_addr #1

attributes #0 = { nofree nounwind uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon,+outline-atomics,+v8a" }
attributes #1 = { nofree nounwind }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8}
!llvm.ident = !{!9}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, !"branch-target-enforcement", i32 0}
!2 = !{i32 1, !"sign-return-address", i32 0}
!3 = !{i32 1, !"sign-return-address-all", i32 0}
!4 = !{i32 1, !"sign-return-address-with-bkey", i32 0}
!5 = !{i32 7, !"PIC Level", i32 2}
!6 = !{i32 7, !"PIE Level", i32 2}
!7 = !{i32 7, !"uwtable", i32 1}
!8 = !{i32 7, !"frame-pointer", i32 1}
!9 = !{!"Ubuntu clang version 14.0.0-1ubuntu1.1"}

[C++] 循環参照

クラスの相互参照における問題
A.h

#ifndef _A_H_
#define _A_H_

#include "B.h"

class A {
    B* m_bB;
    ...
}

#endif // _A_H_

ifndef, define, endifは重複回避

B.h

#ifndef _B_H_
#define _B_H_

#include "B.h"

class B {
    A* m_bA;
    ...
}

#endif // _B_H_

お互いに参照していると、いつまで経っても参照が終わらなくなってしまう

#ifndef _A_H_
#define _A_H_

class B;

class A {
private:
    B* m_bB;
public:
    A();
    void foo();
    void bar();
    ...
}

#endif // _A_H_
#ifndef _B_H_
#define _B_H_

class A;

class B {
private: 
    A* m_bA;
public:
    B(A* pA);
    void hoge();
}

#endif // _B_H_

includeはしていないのだが、うーん、なんかよくわからんな…

circular-dependencies : 循環参照

あるファイルを require したときにその結果が空のオブジェクトとして返される問題
module.exports first, lazy require, dependency injectionなどの解決方法がある

import sys
import re

MAPPING = {
    'core_read.cpp': 'core_io.cpp',
    'core_write.cpp': 'core_io.cpp',
}

# Directories with header-based modules, where the assumption that .cpp files
# define functions and variables declared in corresponding .h files is
# incorrect.
HEADER_MODULE_PATHS = [
    'interfaces/'
]

def module_name(path):
    if path in MAPPING:
        path = MAPPING[path]
    if any(path.startswith(dirpath) for dirpath in HEADER_MODULE_PATHS):
        return path
    if path.endswith(".h"):
        return path[:-2]
    if path.endswith(".c"):
        return path[:-2]
    if path.endswith(".cpp"):
        return path[:-4]
    return None

files = dict()
deps: dict[str, set[str]] = dict()

RE = re.compile("^#include <(.*)>")

# Iterate over files, and create list of modules
for arg in sys.argv[1:]:
    module = module_name(arg)
    if module is None:
        print("Ignoring file %s (does not constitute module)\n" % arg)
    else:
        files[arg] = module
        deps[module] = set()

# Iterate again, and build list of direct dependencies for each module
# TODO: implement support for multiple include directories
for arg in sorted(files.keys()):
    module = files[arg]
    with open(arg, 'r', encoding="utf8") as f:
        for line in f:
            match = RE.match(line)
            if match:
                include = match.group(1)
                included_module = module_name(include)
                if included_module is not None and included_module in deps and included_module != module:
                    deps[module].add(included_module)

# Loop to find the shortest (remaining) circular dependency
have_cycle: bool = False
while True:
    shortest_cycle = None
    for module in sorted(deps.keys()):
        # Build the transitive closure of dependencies of module
        closure: dict[str, list[str]] = dict()
        for dep in deps[module]:
            closure[dep] = []
        while True:
            old_size = len(closure)
            old_closure_keys = sorted(closure.keys())
            for src in old_closure_keys:
                for dep in deps[src]:
                    if dep not in closure:
                        closure[dep] = closure[src] + [src]
            if len(closure) == old_size:
                break
        # If module is in its own transitive closure, it's a circular dependency; check if it is the shortest
        if module in closure and (shortest_cycle is None or len(closure[module]) + 1 < len(shortest_cycle)):
            shortest_cycle = [module] + closure[module]
    if shortest_cycle is None:
        break
    # We have the shortest circular dependency; report it
    module = shortest_cycle[0]
    print("Circular dependency: %s" % (" -> ".join(shortest_cycle + [module])))
    # And then break the dependency to avoid repeating in other cycles
    deps[shortest_cycle[-1]] = deps[shortest_cycle[-1]] - set([module])
    have_cycle = True

sys.exit(1 if have_cycle else 0)

[shell] btc daemonが起動していなかったら起動する

VPSでbtc daemonを運用としていると、何故か勝手に落ちることがあるので、バッチでnodeが動いているか確認して、動いていなかったら起動するコマンドをシェルスクリプトで実行する。

ログファイルに書き込む際に、command=”bitcoin-core.cli –getinfo –testnet | sudo tee -a ${log_FILE}”などとやってしまうと、retval=$?の返り値が、teeの方で判定されてしまうので、判定した後に書き込むとしないと行けない。

#!/bin/sh

today=$(date "+%Y%m%d")
time=$(date "+%Y%m%d %H:%M:%S")
log_FILE="./log/log_${today}.log"

if [ ! -e $log_FILE ]; then
    sudo touch ${log_FILE}
fi

command="bitcoin-core.cli --getinfo --testnet"
eval $command

retval=$?
if [ $retval -eq 0 ]
then
    echo "btc daemon is running. " $time | sudo tee -a ${log_FILE}
else
    echo "btc daemon is stopped. " $time | sudo tee -a ${log_FILE}
    # command="bitcoin-core.daemon -testnet -prune=1000"
    command="echo try again!"
    eval $command
fi

あとはこのシェルをcronで設定すれば良いだけですね。

### cronの設定
とりあえずバッチの実行時間は2時にしました。

$ dnf search crontabs
Failed to set locale, defaulting to C.UTF-8
AlmaLinux 8 – BaseOS 4.8 MB/s | 6.3 MB 00:01
AlmaLinux 8 – AppStream 7.1 MB/s | 12 MB 00:01
AlmaLinux 8 – Extras 36 kB/s | 20 kB 00:00
Extra Packages for Enterprise Linux 8 – x86_64 8.9 MB/s | 16 MB 00:01
nginx stable repo 26 kB/s | 55 kB 00:02
nginx mainline repo 77 kB/s | 135 kB 00:01
======================== Name Exactly Matched: crontabs ========================
crontabs.noarch : Root crontab files used to schedule the execution of programs

$ crontab -e

0 2 * * * /home/alma/command/btc_daemon.sh

$ systemctl restart crond
$ systemctl enable crond
$ systemctl status crond
● crond.service – Command Scheduler
Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor prese>
Active: active (running) since Sat 2024-02-24 21:38:44 JST; 43s ago
Main PID: 900566 (crond)
Tasks: 1 (limit: 5868)
Memory: 1.1M
CGroup: /system.slice/crond.service
└─900566 /usr/sbin/crond -n

あとは、明日ログを見て、実行されているようであれば、bitcoin-core.daemon のコメントアウトを外します。
※シェルを使うようになったのかと思うと感慨深い…

[shell] コマンドを実行する

シェルでのコマンドの実行はevalで実行する

command="echo hoge"
eval $command

$ ./test.sh
hoge

終了コードで処理を変える

command="echo hoge"
eval $command

retval=$?

if [ $retval -eq 0 ]
then
    echo "成功しました!"
else
    echo "失敗しました"
fi

これをechoではなく、bitcoin-core.cli –getinfoにします。

command="bitcoin-core.cli --getinfo"
eval $command

retval=$?

if [ $retval -eq 0 ]
then
    echo "成功しました!"
else
    echo "失敗しました"
fi

値が1の時に起動するように書けばよさそうですね。

fish shell入門

ubuntu22.04にインストールします。

$ sudo apt-add-repository ppa:fish-shell/release-3
$ sudo apt update
$ sudo apt install fish
$ fish –version
fish, version 3.7.0

~/.config/ に対象フォルダがないぞ…