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/ に対象フォルダがないぞ…

[shell] awkを使いたい

hoge.txt

1 2 3
4 5 6
awk '{print $1}' hoge.txt

$ bash cli.bash
1
4

awk '{print $0}' hoge.txt

$ bash cli.bash
1 2 3
4 5 6

awk '$1 ~ /^[0-9]/ { print $1; }' hoge.txt

$ bash cli.bash
1
4

sub(前, 後、入力文字列)

awk '$1 ~ /^[0-9]/ {sub("1", "10", $0); print $1; }' hoge.txt

$ bash cli.bash
10
4

置換、正規表現で検出しているのがわかります。

helpopts=$($bitcoin_cli -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' )
//
commands=$(_bitcoin_rpc help 2>/dev/null | awk '$1 ~ /^[a-z]/ { print $1; }')

なるほど、ユーザのCLIの処理を実装するのはシェルスクリプトで書いていくのね。

[shell] localで複数宣言

複数宣言する際に、配列も交えて宣言ができる

myfunc(){
    local next words=() prev
    next="taiwan"
    prev="korea"
    words=("a" "b" "c")
    echo $prev
    echo ${words[@]}
}
myfunc

$ bash cli.bash
korea
a b c

これで、以下のlocalの宣言の意味もわかりますね。

_bitcoin_cli() {
    local cur prev words=() cword
    local bitcoin_cli

    # save and use original argument to invoke bitcoin-cli for -help, help and RPC
    # as bitcoin-cli might not be in $PATH
    bitcoin_cli="$1"

    COMPREPLY=()
    // 省略
}
    if ((cword > 5)); then
        case ${words[cword-5]} in
            sendtoaddress)
                COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
                return 0
                ;;
        esac
    fi

この COMPREPLY=( $( compgen -W “true false” — “$cur” ) ) は何をやっているかというと、COMPREPLYの中にcompgen … のコマンドを入れていますね。

COMMAND=()
COMMAND=( $(date -d tomorrow) )

echo ${COMMAND[@]}

つまりbitcoin_cliで打った時のコマンド処理を書き換えています。

[shell] caseとesac

while :
do
    read key
    case "$key" in
        "a" ) echo "aが入力されました";;
        "b" ) echo "bが入力されました";;
        "c" ) echo "cが入力されました";;
        "q" ) echo "終了します"
            break;;
    esac
done

exit 0

$ bash cli.bash
a
aが入力されました
b
bが入力されました
q
終了します

num=(2 3 4 5 6 7 8 9)
odds=(1 3 5 7 9)
odd=()
for i in ${num}; do
    case $i in $odds)
        odd+=($i)
        ;;
    esac
done
echo ${odd[@]}

なんか違うな〜