Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864825
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
169 KB
Subscribers
None
View Options
diff --git a/.arclint b/.arclint
index 6bdd1c63f..ac8dfb101 100644
--- a/.arclint
+++ b/.arclint
@@ -1,322 +1,322 @@
{
"linters": {
"generated": {
"type": "generated"
},
"clang-format": {
"type": "clang-format",
"version": ">=12.0",
"bin": [
"clang-format-12",
"clang-format"
],
"include": "(^(src|chronik)/.*\\.(h|c|cpp|mm)$)",
"exclude": [
"(^src/(secp256k1|univalue|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"black": {
"type": "black",
"version": ">=24.0.0",
"include": [
"(\\.py$)",
"(^electrum/electrum-abc$)"
],
"exclude": [
"(^contrib/apple-sdk-tools/)",
"(^electrum/electrumabc_gui/qt/icons.py)",
"(\\_pb2.py$)"
]
},
"flake8": {
"type": "flake8",
"version": ">=5.0",
"include": [
"(\\.py$)",
"(^electrum/electrum-abc$)"
],
"exclude": [
"(^contrib/apple-sdk-tools/)",
"(^electrum/electrumabc_gui/qt/icons.py)",
"(\\_pb2.py$)"
],
"flags": [
"--ignore=A003,E203,E303,E305,E501,E704,W503,W504",
"--require-plugins=flake8-comprehensions,flake8-builtins"
]
},
"lint-format-strings": {
"type": "lint-format-strings",
"include": "(^(src|chronik)/.*\\.(h|c|cpp)$)",
"exclude": [
- "(^src/(secp256k1|univalue|leveldb)/)",
+ "(^src/(secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)",
"(^src/test/fuzz/strprintf.cpp$)"
]
},
"check-doc": {
"type": "check-doc",
"include": "(^(src|chronik)/.*\\.(h|c|cpp)$)"
},
"lint-tests": {
"type": "lint-tests",
"include": "(^src/(seeder/|rpc/|wallet/)?test/.*\\.(cpp)$)"
},
"phpcs": {
"type": "phpcs",
"include": "(\\.php$)",
"exclude": [
"(^arcanist/__phutil_library_.+\\.php$)"
],
"phpcs.standard": "arcanist/phpcs.xml"
},
"lint-locale-dependence": {
"type": "lint-locale-dependence",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)",
"exclude": [
- "(^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/))",
+ "(^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h))",
"(^src/bench/nanobench.h$)"
]
},
"lint-cheader": {
"type": "lint-cheader",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)",
"exclude": [
- "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)",
+ "(^src/(crypto/ctaes|secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"spelling": {
"type": "spelling",
"exclude": [
"(^build-aux/m4/)",
"(^depends/)",
"(^doc/release-notes/)",
- "(^src/(qt/locale|secp256k1|univalue|leveldb)/)",
+ "(^src/(qt/locale|secp256k1|leveldb)/)",
"(^test/lint/dictionary/)",
"(^web/e.cash/public/animations/)",
"(package-lock.json)",
"(^electrum/electrumabc/wordlist/)"
],
"spelling.dictionaries": [
"test/lint/dictionary/english.json"
]
},
"lint-assert-with-side-effects": {
"type": "lint-assert-with-side-effects",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)",
"exclude": [
- "(^src/(secp256k1|univalue|leveldb)/)",
+ "(^src/(secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"lint-include-quotes": {
"type": "lint-include-quotes",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)",
"exclude": [
- "(^src/(secp256k1|univalue|leveldb)/)",
+ "(^src/(secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"lint-include-guard": {
"type": "lint-include-guard",
"include": "(^(src|chronik)/.*\\.h$)",
"exclude": [
- "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)",
+ "(^src/(crypto/ctaes|secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)",
"(^src/tinyformat.h$)"
]
},
"lint-include-source": {
"type": "lint-include-source",
"include": "(^(src|chronik)/.*\\.(h|c|cpp)$)",
"exclude": [
- "(^src/(secp256k1|univalue|leveldb)/)",
+ "(^src/(secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"lint-std-chrono": {
"type": "lint-std-chrono",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)"
},
"lint-stdint": {
"type": "lint-stdint",
"include": "(^(src|chronik)/.*\\.(h|c|cpp)$)",
"exclude": [
- "(^src/(secp256k1|univalue|leveldb)/)",
+ "(^src/(secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)",
"(^src/compat/assumptions.h$)"
]
},
"check-files": {
"type": "check-files"
},
"lint-boost-dependencies": {
"type": "lint-boost-dependencies",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)"
},
"lint-python-encoding": {
"type": "lint-python-encoding",
"include": "(\\.py$)",
"exclude": [
"(^contrib/apple-sdk-tools/)"
]
},
"shellcheck": {
"type": "shellcheck",
"version": ">=0.7.0",
"flags": [
"--external-sources",
"--source-path=SCRIPTDIR"
],
"include": "(\\.(sh|bash)$)",
"exclude": [
- "(^src/(secp256k1|univalue)/)",
+ "(^src/(secp256k1)/)",
"(^electrum/)"
]
},
"lint-shell-locale": {
"type": "lint-shell-locale",
"include": "(\\.(sh|bash)$)",
"exclude": [
- "(^src/(secp256k1|univalue)/)",
+ "(^src/(secp256k1)/)",
"(^cmake/utils/log-and-print-on-failure.sh)"
]
},
"lint-cpp-void-parameters": {
"type": "lint-cpp-void-parameters",
"include": "(^(src|chronik)/.*\\.(h|cpp)$)",
"exclude": [
- "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)",
+ "(^src/(crypto/ctaes|secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)",
"(^src/compat/glibc_compat.cpp$)"
]
},
"lint-logs": {
"type": "lint-logs",
"include": "(^(src|chronik)/.*\\.(h|cpp|rs)$)"
},
"lint-qt": {
"type": "lint-qt",
"include": "(^src/qt/.*\\.(h|cpp)$)",
"exclude": [
"(^src/qt/(locale|forms|res)/)"
]
},
"lint-doxygen": {
"type": "lint-doxygen",
"include": "(^(src|chronik)/.*\\.(h|c|cpp)$)",
"exclude": [
- "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)",
+ "(^src/(crypto/ctaes|secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"lint-whitespace": {
"type": "lint-whitespace",
"include": "(\\.(ac|am|cmake|conf|in|include|json|m4|md|openrc|php|pl|rs|sh|txt|yml)$)",
"exclude": [
- "(^src/(secp256k1|univalue|leveldb)/)",
+ "(^src/(secp256k1|leveldb)/)",
"(^src/bench/nanobench.h$)"
]
},
"yamllint": {
"type": "yamllint",
"include": "(\\.(yml|yaml)$)",
- "exclude": "(^src/(secp256k1|univalue|leveldb)/)"
+ "exclude": "(^src/(secp256k1|leveldb)/)"
},
"lint-check-nonfatal": {
"type": "lint-check-nonfatal",
"include": [
"(^src/rpc/.*\\.(h|c|cpp)$)",
"(^src/wallet/rpc*.*\\.(h|c|cpp)$)"
],
"exclude": "(^src/rpc/server.cpp)"
},
"lint-markdown": {
"type": "lint-markdown",
"include": [
"(\\.md$)"
],
"exclude": [
"(^modules/docs/chronik.e.cash/)"
]
},
"lint-python-mypy": {
"type": "lint-python-mypy",
"version": ">=0.910",
"include": "(\\.py$)",
"exclude": [
"(^contrib/apple-sdk-tools/)",
"(^contrib/macdeploy/)",
"(^electrum/)"
],
"flags": [
"--ignore-missing-imports",
"--install-types",
"--non-interactive"
]
},
"lint-python-mutable-default": {
"type": "lint-python-mutable-default",
"include": "(\\.py$)",
"exclude": [
"(^contrib/apple-sdk-tools/)"
]
},
"prettier": {
"type": "prettier",
"version": ">=2.6.0",
"include": [
"(^apps/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)",
"(^doc/standards/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)",
"(^cashtab/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)",
"(^modules/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)",
"(^web/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)"
],
"exclude": [
"(^web/.*/translations/.*\\.json$)",
"(^web/e.cash/public/animations/)"
]
},
"lint-python-isort": {
"type": "lint-python-isort",
"version": ">=5.6.4",
"include": [
"(\\.py$)",
"(^electrum/electrum-abc$)"
],
"exclude": [
"(^contrib/apple-sdk-tools/)",
"(^electrum/electrumabc_gui/qt/icons.py)",
"(\\_pb2.py$)"
]
},
"rustfmt": {
"type": "rustfmt",
"version": ">=1.5.1",
"include": "(\\.rs$)"
},
"eslint": {
"type": "eslint",
"version": ">=8.0.0",
"include": [
"(cashtab/.*\\.js$)",
"(apps/alias-server/.*\\.js$)",
"(modules/ecashaddrjs/.*\\.js$)",
"(apps/ecash-herald/.*\\.js$)",
"(modules/chronik-client/.*\\.(js|jsx|ts|tsx)$)",
"(^web/e.cash/.*\\.js$)"
]
},
"lint-python-flynt": {
"type": "lint-python-flynt",
"version": ">=0.78",
"include": "(\\.py$)",
"exclude": [
"(^contrib/apple-sdk-tools/)",
"(^electrum/)"
]
}
}
}
diff --git a/.gitignore b/.gitignore
index ac7f41ea6..71dd70005 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,101 +1,100 @@
abc-ci-builds
*.tar.gz
*.exe
src/bitcoin
src/bitcoind
src/bitcoin-cli
src/bitcoin-seeder
src/bitcoin-tx
src/bitcoin-chainstate
src/bitcoin-wallet
src/test/test_bitcoin
src/test/test_bitcoin_fuzzy
src/qt/test/test_bitcoin-qt
-src/univalue/gen
src/obj
src/qt/*.moc
src/qt/moc_*.cpp
src/qt/forms/ui_*.h
src/qt/test/moc*.cpp
src/qt/bitcoin-qt.config
src/qt/bitcoin-qt.creator
src/qt/bitcoin-qt.creator.user
src/qt/bitcoin-qt.files
src/qt/bitcoin-qt.includes
.deps
.libs
.*.swp
*.*~*
*.bak
*.rej
*.orig
*.pyc
*.o
*.o-*
*.a
*.pb.cc
*.pb.h
*.log
*.trs
*.dmg
*.json.h
*.raw.h
# Only ignore unexpected patches
*.patch
!depends/patches/**/*.patch
!contrib/guix/**/*.patch
# Compilation and Qt preprocessor part
*.qm
Makefile
bitcoin-qt
Bitcoin-Qt.app
# Resources cpp
qrc_*.cpp
# Mac specific
.DS_Store
build
#lcov
*.gcno
*.gcda
/*.info
#build tests
test/config.ini
test/cache/*
*.mypy_cache/
!src/leveldb*/Makefile
contrib/devtools/split-debug.sh
# Ignore node_modules at every level
node_modules/
# Ignore secrets.js for JS apps that use secret keys, e.g. databases or telegram bots
secrets.js
# Local Netlify folder
.netlify
# arc liberate cache file
.phutil_module_cache
# GUIX
/output/
/var/
/distsrc-*/
web/e.cash/public/rss.xml
diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py
index 2d43c955e..8527fe3d7 100755
--- a/contrib/devtools/copyright_header.py
+++ b/contrib/devtools/copyright_header.py
@@ -1,725 +1,724 @@
#!/usr/bin/env python3
# Copyright (c) 2016 The Bitcoin Core developers
# Copyright (c) 2017-2019 The Bitcoin developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import datetime
import fnmatch
import os
import re
import subprocess
import sys
##########################################################################
# file filtering
##########################################################################
EXCLUDE = [
# auto generated:
"src/qt/bitcoinstrings.cpp",
"src/chainparamsseeds.h",
# other external copyrights:
"src/reverse_iterator.h",
"src/test/fuzz/FuzzedDataProvider.h",
"src/tinyformat.h",
"src/bench/nanobench.h",
"test/functional/test_framework/bignum.py",
# python init:
"*__init__.py",
]
EXCLUDE_COMPILED = re.compile("|".join([fnmatch.translate(m) for m in EXCLUDE]))
EXCLUDE_DIRS = [
# git subtrees
"src/crypto/ctaes/",
"src/leveldb/",
"src/secp256k1/",
- "src/univalue/",
]
INCLUDE = ["*.h", "*.cpp", "*.cc", "*.c", "*.mm", "*.py", "*.sh", "*.bash-completion"]
INCLUDE_COMPILED = re.compile("|".join([fnmatch.translate(m) for m in INCLUDE]))
def applies_to_file(filename):
for excluded_dir in EXCLUDE_DIRS:
if filename.startswith(excluded_dir):
return False
return (EXCLUDE_COMPILED.match(filename) is None) and (
INCLUDE_COMPILED.match(filename) is not None
)
##########################################################################
# obtain list of files in repo according to INCLUDE and EXCLUDE
##########################################################################
GIT_LS_CMD = "git ls-files --full-name".split(" ")
GIT_TOPLEVEL_CMD = "git rev-parse --show-toplevel".split(" ")
def call_git_ls(base_directory):
out = subprocess.check_output([*GIT_LS_CMD, base_directory])
return [f for f in out.decode("utf-8").split("\n") if f != ""]
def call_git_toplevel():
"Returns the absolute path to the project root"
return subprocess.check_output(GIT_TOPLEVEL_CMD).strip().decode("utf-8")
def get_filenames_to_examine(base_directory):
"Returns an array of absolute paths to any project files in the base_directory that pass the include/exclude filters"
root = call_git_toplevel()
filenames = call_git_ls(base_directory)
return sorted(
[
os.path.join(root, filename)
for filename in filenames
if applies_to_file(filename)
]
)
##########################################################################
# define and compile regexes for the patterns we are looking for
##########################################################################
COPYRIGHT_WITH_C = r"Copyright \(c\)"
COPYRIGHT_WITHOUT_C = "Copyright"
ANY_COPYRIGHT_STYLE = f"({COPYRIGHT_WITH_C}|{COPYRIGHT_WITHOUT_C})"
YEAR = "20[0-9][0-9]"
YEAR_RANGE = f"({YEAR})(-{YEAR})?"
YEAR_LIST = f"({YEAR})(, {YEAR})+"
ANY_YEAR_STYLE = f"({YEAR_RANGE}|{YEAR_LIST})"
ANY_COPYRIGHT_STYLE_OR_YEAR_STYLE = f"{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(f"{copyright_style} {year_style},? {name}")
EXPECTED_HOLDER_NAMES = [
r"Satoshi Nakamoto\n",
r"The Bitcoin Core developers\n",
r"The Bitcoin developers\n",
r"The Bitcoin ABC developers\n",
r"BitPay Inc\.\n",
r"University of Illinois at Urbana-Champaign\.\n",
r"Pieter Wuille\n",
r"Pieter Wuille, Shammah Chancellor, Neil Booth",
r"Wladimir J. van der Laan\n",
r"Jeff Garzik\n",
r"Jan-Klaas Kollhof\n",
r"ArtForz -- public domain half-a-node\n",
r"Amaury SÉCHET\n",
r"Intel Corporation\n",
r"The Zcash developers\n",
r"Jeremy Rubin\n",
]
DOMINANT_STYLE_COMPILED = {}
YEAR_LIST_STYLE_COMPILED = {}
WITHOUT_C_STYLE_COMPILED = {}
for holder_name in EXPECTED_HOLDER_NAMES:
DOMINANT_STYLE_COMPILED[holder_name] = compile_copyright_regex(
COPYRIGHT_WITH_C, YEAR_RANGE, holder_name
)
YEAR_LIST_STYLE_COMPILED[holder_name] = compile_copyright_regex(
COPYRIGHT_WITH_C, YEAR_LIST, holder_name
)
WITHOUT_C_STYLE_COMPILED[holder_name] = compile_copyright_regex(
COPYRIGHT_WITHOUT_C, ANY_YEAR_STYLE, holder_name
)
##########################################################################
# search file contents for copyright message of particular category
##########################################################################
def get_count_of_copyrights_of_any_style_any_holder(contents):
return len(ANY_COPYRIGHT_COMPILED.findall(contents))
def file_has_dominant_style_copyright_for_holder(contents, holder_name):
match = DOMINANT_STYLE_COMPILED[holder_name].search(contents)
return match is not None
def file_has_year_list_style_copyright_for_holder(contents, holder_name):
match = YEAR_LIST_STYLE_COMPILED[holder_name].search(contents)
return match is not None
def file_has_without_c_style_copyright_for_holder(contents, holder_name):
match = WITHOUT_C_STYLE_COMPILED[holder_name].search(contents)
return match is not None
##########################################################################
# get file info
##########################################################################
def read_file(filename):
return open(filename, "r", encoding="utf8").read()
def gather_file_info(filename):
info = {}
info["filename"] = filename
c = read_file(filename)
info["contents"] = c
info["all_copyrights"] = get_count_of_copyrights_of_any_style_any_holder(c)
info["classified_copyrights"] = 0
info["dominant_style"] = {}
info["year_list_style"] = {}
info["without_c_style"] = {}
for holder_name in EXPECTED_HOLDER_NAMES:
has_dominant_style = file_has_dominant_style_copyright_for_holder(
c, holder_name
)
has_year_list_style = file_has_year_list_style_copyright_for_holder(
c, holder_name
)
has_without_c_style = file_has_without_c_style_copyright_for_holder(
c, holder_name
)
info["dominant_style"][holder_name] = has_dominant_style
info["year_list_style"][holder_name] = has_year_list_style
info["without_c_style"][holder_name] = has_without_c_style
if has_dominant_style or has_year_list_style or has_without_c_style:
info["classified_copyrights"] = info["classified_copyrights"] + 1
return info
##########################################################################
# report execution
##########################################################################
SEPARATOR = "-".join(["" for _ in range(80)])
def print_filenames(filenames, verbose):
if not verbose:
return
for filename in filenames:
print(f"\t{filename}")
def print_report(file_infos, verbose):
print(SEPARATOR)
examined = [i["filename"] for i in file_infos]
print(
f"{len(examined)} files examined according to INCLUDE and EXCLUDE fnmatch rules"
)
print_filenames(examined, verbose)
print(SEPARATOR)
print("")
zero_copyrights = [i["filename"] for i in file_infos if i["all_copyrights"] == 0]
print(f"{len(zero_copyrights):4d} with zero copyrights")
print_filenames(zero_copyrights, verbose)
one_copyright = [i["filename"] for i in file_infos if i["all_copyrights"] == 1]
print(f"{len(one_copyright):4d} with one copyright")
print_filenames(one_copyright, verbose)
two_copyrights = [i["filename"] for i in file_infos if i["all_copyrights"] == 2]
print(f"{len(two_copyrights):4d} with two copyrights")
print_filenames(two_copyrights, verbose)
three_copyrights = [i["filename"] for i in file_infos if i["all_copyrights"] == 3]
print(f"{len(three_copyrights):4d} with three copyrights")
print_filenames(three_copyrights, verbose)
four_or_more_copyrights = [
i["filename"] for i in file_infos if i["all_copyrights"] >= 4
]
print(f"{len(four_or_more_copyrights):4d} with four or more copyrights")
print_filenames(four_or_more_copyrights, verbose)
print("")
print(SEPARATOR)
print(
'Copyrights with dominant style:\ne.g. "Copyright (c)" and '
'"<year>" or "<startYear>-<endYear>":\n'
)
for holder_name in EXPECTED_HOLDER_NAMES:
dominant_style = [
i["filename"] for i in file_infos if i["dominant_style"][holder_name]
]
if len(dominant_style) > 0:
print(
"{:4d} with '{}'".format(
len(dominant_style), holder_name.replace("\n", "\\n")
)
)
print_filenames(dominant_style, verbose)
print("")
print(SEPARATOR)
print(
'Copyrights with year list style:\ne.g. "Copyright (c)" and '
'"<year1>, <year2>, ...":\n'
)
for holder_name in EXPECTED_HOLDER_NAMES:
year_list_style = [
i["filename"] for i in file_infos if i["year_list_style"][holder_name]
]
if len(year_list_style) > 0:
print(
"{:4d} with '{}'".format(
len(year_list_style), holder_name.replace("\n", "\\n")
)
)
print_filenames(year_list_style, verbose)
print("")
print(SEPARATOR)
print(
'Copyrights with no "(c)" style:\ne.g. "Copyright" and "<year>" or '
'"<startYear>-<endYear>":\n'
)
for holder_name in EXPECTED_HOLDER_NAMES:
without_c_style = [
i["filename"] for i in file_infos if i["without_c_style"][holder_name]
]
if len(without_c_style) > 0:
print(
"{:4d} with '{}'".format(
len(without_c_style), holder_name.replace("\n", "\\n")
)
)
print_filenames(without_c_style, verbose)
print("")
print(SEPARATOR)
unclassified_copyrights = [
i["filename"]
for i in file_infos
if i["classified_copyrights"] < i["all_copyrights"]
]
print(f"{len(unclassified_copyrights)} with unexpected copyright holder names")
print_filenames(unclassified_copyrights, verbose)
print(SEPARATOR)
def exec_report(base_directory, verbose):
filenames = get_filenames_to_examine(base_directory)
file_infos = [gather_file_info(f) for f in filenames]
print_report(file_infos, verbose)
##########################################################################
# report cmd
##########################################################################
REPORT_USAGE = """
Produces a report of all copyright header notices found inside the source files
of a repository.
Usage:
$ ./copyright_header.py report <base_directory> [verbose]
Arguments:
<base_directory> - The base directory of a bitcoin source code repository.
[verbose] - Includes a list of every file of each subcategory in the report.
"""
def report_cmd(argv):
if len(argv) == 2:
sys.exit(REPORT_USAGE)
base_directory = argv[2]
if not os.path.exists(base_directory):
sys.exit(f"*** bad <base_directory>: {base_directory}")
if len(argv) == 3:
verbose = False
elif argv[3] == "verbose":
verbose = True
else:
sys.exit(f"*** unknown argument: {argv[2]}")
exec_report(base_directory, verbose)
##########################################################################
# query git for year of last change
##########################################################################
GIT_LOG_CMD = "git log --pretty=format:%ai {}"
def call_git_log(filename):
out = subprocess.check_output((GIT_LOG_CMD.format(filename)).split(" "))
return out.decode("utf-8").split("\n")
def get_git_change_years(filename):
git_log_lines = call_git_log(filename)
if len(git_log_lines) == 0:
return [datetime.date.today().year]
# timestamp is in ISO 8601 format. e.g. "2016-09-05 14:25:32 -0600"
return [line.split(" ")[0].split("-")[0] for line in git_log_lines]
def get_most_recent_git_change_year(filename):
return max(get_git_change_years(filename))
##########################################################################
# read and write to file
##########################################################################
def read_file_lines(filename):
f = open(filename, "r", encoding="utf8")
file_lines = f.readlines()
f.close()
return file_lines
def write_file_lines(filename, file_lines):
f = open(filename, "w", encoding="utf8")
f.write("".join(file_lines))
f.close()
##########################################################################
# update header years execution
##########################################################################
COPYRIGHT = r"Copyright \(c\)"
YEAR = "20[0-9][0-9]"
YEAR_RANGE = f"({YEAR})(-{YEAR})?"
HOLDER = "The Bitcoin developers"
UPDATEABLE_LINE_COMPILED = re.compile(" ".join([COPYRIGHT, YEAR_RANGE, HOLDER]))
DISTRIBUTION_LINE = re.compile(
r"Distributed under the MIT software license, see the accompanying"
)
def get_updatable_copyright_line(file_lines):
index = 0
for line in file_lines:
if UPDATEABLE_LINE_COMPILED.search(line) is not None:
return index, line
index = index + 1
return None, None
def find_distribution_line_index(file_lines):
index = 0
for line in file_lines:
if DISTRIBUTION_LINE.search(line) is not None:
return index
index = index + 1
return None
def parse_year_range(year_range):
year_split = year_range.split("-")
start_year = year_split[0]
if len(year_split) == 1:
return start_year, start_year
return start_year, year_split[1]
def year_range_to_str(start_year, end_year):
if start_year == end_year:
return start_year
return f"{start_year}-{end_year}"
def create_updated_copyright_line(line, last_git_change_year):
copyright_splitter = "Copyright (c) "
copyright_split = line.split(copyright_splitter)
# Preserve characters on line that are ahead of the start of the copyright
# notice - they are part of the comment block and vary from file-to-file.
before_copyright = copyright_split[0]
after_copyright = copyright_split[1]
space_split = after_copyright.split(" ")
year_range = space_split[0]
start_year, end_year = parse_year_range(year_range)
if end_year == last_git_change_year:
return line
return (
before_copyright
+ copyright_splitter
+ year_range_to_str(start_year, last_git_change_year)
+ " "
+ " ".join(space_split[1:])
)
def update_updatable_copyright(filename):
file_lines = read_file_lines(filename)
index, line = get_updatable_copyright_line(file_lines)
if not line:
print_file_action_message(filename, "No updatable copyright.")
return
last_git_change_year = get_most_recent_git_change_year(filename)
new_line = create_updated_copyright_line(line, last_git_change_year)
if line == new_line:
print_file_action_message(filename, "Copyright up-to-date.")
return
file_lines[index] = new_line
write_file_lines(filename, file_lines)
print_file_action_message(filename, f"Copyright updated! -> {last_git_change_year}")
def exec_update_header_year(base_directory):
for filename in get_filenames_to_examine(base_directory):
update_updatable_copyright(filename)
##########################################################################
# update cmd
##########################################################################
UPDATE_USAGE = """
Updates all the copyright headers of "The Bitcoin developers" which were
changed in a year more recent than is listed. For example:
// Copyright (c) <firstYear>-<lastYear> The Bitcoin developers
will be updated to:
// Copyright (c) <firstYear>-<lastModifiedYear> The Bitcoin developers
where <lastModifiedYear> is obtained from the 'git log' history.
This subcommand also handles copyright headers that have only a single year. In those cases:
// Copyright (c) <year> The Bitcoin developers
will be updated to:
// Copyright (c) <year>-<lastModifiedYear> The Bitcoin developers
where the update is appropriate.
Usage:
$ ./copyright_header.py update <base_directory>
Arguments:
<base_directory> - The base directory of a bitcoin source code repository.
"""
def print_file_action_message(filename, action):
print(f"{filename:52s} {action}")
def update_cmd(argv):
if len(argv) != 3:
sys.exit(UPDATE_USAGE)
base_directory = argv[2]
if not os.path.exists(base_directory):
sys.exit(f"*** bad base_directory: {base_directory}")
exec_update_header_year(base_directory)
##########################################################################
# inserted copyright header format
##########################################################################
def get_header_lines(header, start_year, end_year):
lines = header.split("\n")[1:-1]
lines[0] = lines[0].format(year_range_to_str(start_year, end_year))
return [line + "\n" for line in lines]
CPP_HEADER = """
// Copyright (c) {} The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
def get_cpp_header_lines_to_insert(start_year, end_year):
return reversed(get_header_lines(CPP_HEADER, start_year, end_year))
SCRIPT_HEADER = """
# Copyright (c) {} The Bitcoin developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
def get_script_header_lines_to_insert(start_year, end_year):
return reversed(get_header_lines(SCRIPT_HEADER, start_year, end_year))
##########################################################################
# query git for year of last change
##########################################################################
def get_git_change_year_range(filename):
years = get_git_change_years(filename)
return min(years), max(years)
##########################################################################
# check for existing ABC copyright
##########################################################################
def file_already_has_bitcoin_copyright(file_lines):
index, _ = get_updatable_copyright_line(file_lines)
return index is not None
##########################################################################
# insert header execution
##########################################################################
def file_has_hashbang(file_lines):
if len(file_lines) < 1:
return False
if len(file_lines[0]) <= 2:
return False
return file_lines[0][:2] == "#!"
def insert_script_header(filename, file_lines, start_year, end_year):
header_lines = get_script_header_lines_to_insert(start_year, end_year)
insert_idx = find_distribution_line_index(file_lines)
if insert_idx is not None:
file_lines.insert(insert_idx, list(header_lines)[-1])
else:
if file_has_hashbang(file_lines):
insert_idx = 1
else:
insert_idx = 0
for line in header_lines:
file_lines.insert(insert_idx, line)
write_file_lines(filename, file_lines)
def insert_cpp_header(filename, file_lines, start_year, end_year):
file_lines.insert(0, "\n")
header_lines = get_cpp_header_lines_to_insert(start_year, end_year)
insert_idx = find_distribution_line_index(file_lines)
if insert_idx is not None:
file_lines.insert(insert_idx, list(header_lines)[-1])
else:
for line in header_lines:
file_lines.insert(0, line)
write_file_lines(filename, file_lines)
def exec_insert_header(filename, style):
file_lines = read_file_lines(filename)
if file_already_has_bitcoin_copyright(file_lines):
sys.exit(f"*** {filename} already has a copyright by The Bitcoin developers")
start_year, end_year = get_git_change_year_range(filename)
if style in ["python", "shell"]:
insert_script_header(filename, file_lines, start_year, end_year)
else:
insert_cpp_header(filename, file_lines, start_year, end_year)
##########################################################################
# insert cmd
##########################################################################
INSERT_USAGE = """
Inserts a copyright header for "The Bitcoin developers" at the top of the
file in either Python or C++ style as determined by the file extension. If the
file is a Python file and it has a '#!' starting the first line, the header is
inserted in the line below it.
The copyright dates will be set to be:
"<year_introduced>-<current_year>"
where <year_introduced> is according to the 'git log' history. If
<year_introduced> is equal to <current_year>, the date will be set to be:
"<current_year>"
If the file already has a copyright for "The Bitcoin developers", the
script will exit.
Usage:
$ ./copyright_header.py insert <file>
Arguments:
<file> - A source file in the bitcoin repository.
"""
def insert_cmd(argv):
if len(argv) != 3:
sys.exit(INSERT_USAGE)
filename = argv[2]
if not os.path.isfile(filename):
sys.exit(f"*** bad filename: {filename}")
_, extension = os.path.splitext(filename)
if extension not in [".h", ".cpp", ".cc", ".c", ".py", ".sh"]:
sys.exit(f"*** cannot insert for file extension {extension}")
if extension == ".py":
style = "python"
elif extension == ".sh":
style = "shell"
else:
style = "cpp"
exec_insert_header(filename, style)
##########################################################################
# UI
##########################################################################
USAGE = """
copyright_header.py - utilities for managing copyright headers of 'The Bitcoin
developers' in repository source files.
Usage:
$ ./copyright_header <subcommand>
Subcommands:
report
update
insert
To see subcommand usage, run them without arguments.
"""
SUBCOMMANDS = ["report", "update", "insert"]
if __name__ == "__main__":
if len(sys.argv) == 1:
sys.exit(USAGE)
subcommand = sys.argv[1]
if subcommand not in SUBCOMMANDS:
sys.exit(USAGE)
if subcommand == "report":
report_cmd(sys.argv)
elif subcommand == "update":
update_cmd(sys.argv)
elif subcommand == "insert":
insert_cmd(sys.argv)
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 3e2e7211e..1b077431f 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -1,1292 +1,1288 @@
Developer Notes
===============
<!-- markdown-toc start -->
**Table of Contents**
- [Developer Notes](#developer-notes)
- [Coding Style (General)](#coding-style-general)
- [Coding Style (C++)](#coding-style-c)
- [Doxygen comments](#doxygen-comments)
- [Coding Style (Python)](#coding-style-python)
- [Development tips and tricks](#development-tips-and-tricks)
- [Compiling for debugging](#compiling-for-debugging)
- [Compiling for gprof profiling](#compiling-for-gprof-profiling)
- [debug.log](#debuglog)
- [Writing tests](#writing-tests)
- [Writing script integration tests](#writing-script-integration-tests)
- [Testnet and Regtest modes](#testnet-and-regtest-modes)
- [DEBUG_LOCKORDER](#debug_lockorder)
- [DEBUG_LOCKCONTENTION](#debug_lockcontention)
- [Valgrind suppressions file](#valgrind-suppressions-file)
- [Compiling for test coverage](#compiling-for-test-coverage)
- [Performance profiling with perf](#performance-profiling-with-perf)
- [Sanitizers](#sanitizers)
- [Locking/mutex usage notes](#lockingmutex-usage-notes)
- [Threads](#threads)
- [Ignoring IDE/editor files](#ignoring-ideeditor-files)
- [Development guidelines](#development-guidelines)
- [Wallet](#wallet)
- [General C++](#general-c)
- [C++ data structures](#c-data-structures)
- [Strings and formatting](#strings-and-formatting)
- [Variable names](#variable-names)
- [Threads and synchronization](#threads-and-synchronization)
- [Scripts](#scripts)
- [Shebang](#shebang)
- [Source code organization](#source-code-organization)
- [GUI](#gui)
- [Unit tests](#unit-tests)
- [Third party libraries](#third-party-libraries)
- [Git and GitHub tips](#git-and-github-tips)
- [Release notes](#release-notes)
- [RPC interface guidelines](#rpc-interface-guidelines)
- [Internal interface guidelines](#internal-interface-guidelines)
<!-- markdown-toc end -->
Coding Style (General)
----------------------
Various coding styles have been used during the history of the codebase,
and the result is not very consistent. However, we're now trying to converge to
a single style, so please use it in new code. Old code will be converted
gradually and a handful of linters will help you to clean up your patches before
submitting them for review. These linters are run automatically when using
`arc diff` but can also be explicitly called with `arc lint`.
Coding Style (C++)
------------------
- Basic rules specified in [.clang-format](/.clang-format).
- Braces on new lines for namespaces, classes, functions, methods.
- Braces on the same line for everything else.
- 4 space indentation (no tabs) for every block except namespaces.
- No indentation for `public`/`protected`/`private` or for `namespace`.
- No extra spaces inside parenthesis; don't do ( this )
- No space after function names; one space after `if`, `for` and `while`.
- Always add braces for block statements (e.g. `if`, `for`, `while`).
- `++i` is preferred over `i++`.
- `static_assert` is preferred over `assert` where possible.
Generally; compile-time checking is preferred over run-time checking.
- Use CamelCase for functions/methods, and lowerCamelCase for variables.
- GLOBAL_CONSTANTS should use UPPER_SNAKE_CASE.
- namespaces should use lower_snake_case.
- Function names should generally start with an English command-form verb
(e.g. `ValidateTransaction`, `AddTransactionToMempool`, `ConnectBlock`)
- Variable names should generally be nouns or past/future tense verbs.
(e.g. `canDoThing`, `signatureOperations`, `didThing`)
- Avoid using globals, remove existing globals whenever possible.
- Class member variable names should be prepended with `m_`
- DO choose easily readable identifier names.
- DO favor readability over brevity.
- DO NOT use Hungarian notation.
- DO NOT use abbreviations or contractions within identifiers.
- WRONG: mempool
- RIGHT: MemoryPool
- WRONG: ChangeDir
- RIGHT: ChangeDirectory
- DO NOT use obscure acronyms, DO uppercase any acronyms.
- FINALLY, do not migrate existing code unless refactoring. It makes
forwarding-porting from Bitcoin Core more difficult.
The naming convention roughly mirrors [Microsoft Naming Conventions](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/general-naming-conventions)
C++ Coding Standards should strive to follow the [LLVM Coding Standards](https://llvm.org/docs/CodingStandards.html)
Code style example:
```c++
// namespaces should be lower_snake_case
namespace foo_bar_bob {
/**
* Class is used for doing classy things. All classes should
* have a doxygen comment describing their PURPOSE. That is to say,
* why they exist. Functional details can be determined from the code.
* @see PerformTask()
*/
class Class {
private:
//! memberVariable's name should be lowerCamelCase, and be a noun.
int m_memberVariable;
public:
/**
* The documentation before a function or class method should follow Doxygen
* spec. The name of the function should start with an english verb which
* indicates the intended purpose of this code.
*
* The function name should be should be CamelCase.
*
* @param[in] s A description
* @param[in] n Another argument description
* @pre Precondition for function...
*/
bool PerformTask(const std::string& s, int n) {
// Use lowerChamelCase for local variables.
bool didMore = false;
// Comment summarizing the intended purpose of this section of code
for (int i = 0; i < n; ++i) {
if (!DidSomethingFail()) {
return false;
}
...
if (IsSomethingElse()) {
DoMore();
didMore = true;
} else {
DoLess();
}
}
return didMore;
}
}
} // namespace foo
```
Doxygen comments
-----------------
To facilitate the generation of documentation, use doxygen-compatible comment blocks for functions, methods and fields.
For example, to describe a function use:
```c++
/**
* ... text ...
* @param[in] arg1 A description
* @param[in] arg2 Another argument description
* @pre Precondition for function...
*/
bool function(int arg1, const char *arg2)
```
A complete list of `@xxx` commands can be found at http://www.doxygen.nl/manual/commands.html.
As Doxygen recognizes the comments by the delimiters (`/**` and `*/` in this case), you don't
*need* to provide any commands for a comment to be valid; just a description text is fine.
To describe a class use the same construct above the class definition:
```c++
/**
* Alerts are for notifying old versions if they become too obsolete and
* need to upgrade. The message is displayed in the status bar.
* @see GetWarnings()
*/
class CAlert
{
```
To describe a member or variable use:
```c++
int var; //!< Detailed description after the member
```
or
```cpp
//! Description before the member
int var;
```
Also OK:
```c++
///
/// ... text ...
///
bool function2(int arg1, const char *arg2)
```
Not OK (used plenty in the current source, but not picked up):
```c++
//
// ... text ...
//
```
A full list of comment syntaxes picked up by doxygen can be found at http://www.doxygen.nl/manual/docblocks.html,
but if possible use one of the above styles.
To build doxygen locally to test changes to the Doxyfile or visualize your comments before landing changes:
```
ninja doc-doxygen
# output goes to doc/doxygen/html/
```
Coding Style (Python)
---------------------
Refer to [functional-tests.md#style-guidelines](functional-tests.md#style-guidelines).
Development tips and tricks
---------------------------
### Compiling for debugging
Run `cmake` with `-DCMAKE_BUILD_TYPE=Debug` to add additional compiler flags
that produce better debugging builds.
### Compiling for gprof profiling
```
cmake -GNinja .. -DENABLE_HARDENING=OFF -DENABLE_PROFIILING=gprof
```
### debug.log
If the code is behaving strangely, take a look in the debug.log file in the data directory;
error and debugging messages are written there.
The `-debug=...` command-line option controls debugging; running with just `-debug` or `-debug=1` will turn
on all categories (and give you a very large debug.log file).
The Qt code routes `qDebug()` output to debug.log under category "qt": run with `-debug=qt`
to see it.
### Writing tests
For details on unit tests, see [Compiling/running unit tests](unit-tests.md).
For details on functional tests, see [Functional tests](functional-tests.md).
### Writing script integration tests
Script integration tests are built using `src/test/script_tests.cpp`:
1. Uncomment the line with `#define UPDATE_JSON_TESTS`
2. Add a new TestBuilder to the `script_build` test to cover your test case.
3. `ninja check-bitcoin-script_tests`
4. Copy your newly generated test JSON from `<build-dir>/src/script_tests.json.gen` to `src/test/data/script_tests.json`.
Please commit your TestBuilder along with your generated test JSON and cleanup the uncommented #define before code review.
### Testnet and Regtest modes
Run with the `-testnet` option to run with "play bitcoins" on the test network, if you
are testing multi-machine code that needs to operate across the internet.
If you are testing something that can run on one machine, run with the `-regtest` option.
In regression test mode, blocks can be created on-demand; see [test/functional/](/test/functional) for tests
that run in `-regtest` mode.
### DEBUG_LOCKORDER
Bitcoin ABC is a multi-threaded application, and deadlocks or other
multi-threading bugs can be very difficult to track down.
The `-DCMAKE_BUILD_TYPE=Debug` cmake option adds `-DDEBUG_LOCKORDER` to the
compiler flags. This inserts run-time checks to keep track of which locks are
held, and adds warnings to the debug.log file if inconsistencies are detected.
### DEBUG_LOCKCONTENTION
Defining `DEBUG_LOCKCONTENTION` adds a "lock" logging category that, when enabled,
logs the location and duration of each lock contention to the `debug.log` file.
To enable it, run cmake with `-DDEBUG_LOCKCONTENTION` added to your CPPFLAGS,
e.g. `-DCMAKE_CXX_FLAGS="-DDEBUG_LOCKCONTENTION"`, then build and run bitcoind.
You can then use the `-debug=lock` configuration option at bitcoind startup or
`bitcoin-cli logging '["lock"]'` at runtime to turn on lock contention logging.
It can be toggled off again with `bitcoin-cli logging [] '["lock"]'`.
### Assertions and Checks
The util file `src/util/check.h` offers helpers to protect against coding and
internal logic bugs. They must never be used to validate user, network or any
other input.
* `assert` or `Assert` should be used to document assumptions when any
violation would mean that it is not safe to continue program execution. The
code is always compiled with assertions enabled.
- For example, a nullptr dereference or any other logic bug in validation
code means the program code is faulty and must terminate immediately.
* `CHECK_NONFATAL` should be used for recoverable internal logic bugs. On
failure, it will throw an exception, which can be caught to recover from the
error.
- For example, a nullptr dereference or any other logic bug in RPC code
means that the RPC code is faulty and can not be executed. However, the
logic bug can be shown to the user and the program can continue to run.
* `Assume` should be used to document assumptions when program execution can
safely continue even if the assumption is violated. In debug builds it
behaves like `Assert`/`assert` to notify developers and testers about
nonfatal errors. In production it doesn't warn or log anything, though the
expression is always evaluated.
- For example it can be assumed that a variable is only initialized once,
but a failed assumption does not result in a fatal bug. A failed
assumption may or may not result in a slightly degraded user experience,
but it is safe to continue program execution.
### Valgrind suppressions file
Valgrind is a programming tool for memory debugging, memory leak detection, and
profiling. The repo contains a Valgrind suppressions file
([`valgrind.supp`](/contrib/valgrind.supp))
which includes known Valgrind warnings in our dependencies that cannot be fixed
in-tree. Example use:
```shell
$ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin
$ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \
--show-leak-kinds=all src/test/test_bitcoin --log_level=test_suite
$ valgrind -v --leak-check=full src/bitcoind -printtoconsole
```
### Compiling for test coverage
LCOV can be used to generate a test coverage report based upon some test targets
execution. Some packages are required to generate the coverage report:
`c++filt`, `gcov`, `genhtml`, `lcov` and `python3`.
To install these dependencies on Debian 10:
```shell
sudo apt install binutils-common g++ lcov python3
```
To enable LCOV report generation during test runs:
```shell
cmake -GNinja .. -DENABLE_COVERAGE=ON
ninja coverage-check-all
```
A coverage report will now be accessible at `./check-all.coverage/index.html`.
To include branch coverage, you can add the `-DENABLE_BRANCH_COVERAGE=ON` option
to the `cmake` command line.
### Performance profiling with perf
Profiling is a good way to get a precise idea of where time is being spent in
code. One tool for doing profiling on Linux platforms is called
[`perf`](http://www.brendangregg.com/perf.html), and has been integrated into
the functional test framework. Perf can observe a running process and sample
(at some frequency) where its execution is.
Perf installation is contingent on which kernel version you're running; see
[this StackExchange
thread](https://askubuntu.com/questions/50145/how-to-install-perf-monitoring-tool)
for specific instructions.
Certain kernel parameters may need to be set for perf to be able to inspect the
running process' stack.
```sh
$ sudo sysctl -w kernel.perf_event_paranoid=-1
$ sudo sysctl -w kernel.kptr_restrict=0
```
Make sure you [understand the security
trade-offs](https://lwn.net/Articles/420403/) of setting these kernel
parameters.
To profile a running bitcoind process for 60 seconds, you could use an
invocation of `perf record` like this:
```sh
$ perf record \
-g --call-graph dwarf --per-thread -F 140 \
-p `pgrep bitcoind` -- sleep 60
```
You could then analyze the results by running
```sh
perf report --stdio | c++filt | less
```
or using a graphical tool like [Hotspot](https://github.com/KDAB/hotspot).
See the functional test documentation for how to invoke perf within tests.
### Sanitizers
Bitcoin ABC can be compiled with various "sanitizers" enabled, which add
instrumentation for issues regarding things like memory safety, thread race
conditions, or undefined behavior. This is controlled with the
`-DENABLE_SANITIZERS` cmake flag, which should be a semicolon separated list of
sanitizers to enable. The sanitizer list should correspond to supported
`-fsanitize=` options in your compiler. These sanitizers have runtime overhead,
so they are most useful when testing changes or producing debugging builds.
Some examples:
```bash
# Enable both the address sanitizer and the undefined behavior sanitizer
cmake -GNinja .. -DENABLE_SANITIZERS="address;undefined"
# Enable the thread sanitizer
cmake -GNinja .. -DENABLE_SANITIZERS=thread
```
If you are compiling with GCC you will typically need to install corresponding
"san" libraries to actually compile with these flags, e.g. libasan for the
address sanitizer, libtsan for the thread sanitizer, and libubsan for the
undefined sanitizer. If you are missing required libraries, the cmake script
will fail with an error when testing the sanitizer flags.
Note that the sanitizers will give a better output if they are run with a Debug
build configuration.
There are a number of known problems for which suppressions files are provided
under `test/sanitizer_suppressions`. These files are intended to be used with
the `suppressions` option from the sanitizers. If you are using the `check-*`
targets to run the tests, the suppression options are automatically set.
Otherwise they need to be set manually using environment variables; refer to
your compiler manual for the correct syntax.
The address sanitizer is known to fail in
[sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable
unless you also use `-DCRYPTO_USE_ASM=OFF` when running cmake.
We would like to fix sanitizer issues, so please send pull requests if you can
fix any errors found by the address sanitizer (or any other sanitizer).
Not all sanitizer options can be enabled at the same time, e.g. trying to build
with `-DENABLE_SANITIZERS=="address;thread" will fail in the cmake script as
these sanitizers are mutually incompatible. Refer to your compiler manual to
learn more about these options and which sanitizers are supported by your
compiler.
Examples:
Build and run the test suite with the address sanitizer enabled:
```bash
mkdir build_asan
cd build_asan
cmake -GNinja .. \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_SANITIZERS=address \
-DCRYPTO_USE_ASM=OFF
ninja check check-functional
```
Build and run the test suite with the thread sanitizer enabled (it can take a
very long time to complete):
```bash
mkdir build_tsan
cd build_tsan
cmake -GNinja .. \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_SANITIZERS=thread
ninja check check-functional
```
Build and run the test suite with the undefined sanitizer enabled:
```bash
mkdir build_ubsan
cd build_ubsan
cmake -GNinja .. \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_SANITIZERS=undefined
ninja check check-functional
```
Additional resources:
* [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
* [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html)
* [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html)
* [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html)
* [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
* [GCC Instrumentation Options](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
* [Google Sanitizers Wiki](https://github.com/google/sanitizers/wiki)
* [Issue #12691: Enable -fsanitize flags in Travis](https://github.com/bitcoin/bitcoin/issues/12691)
Locking/mutex usage notes
-------------------------
The code is multi-threaded, and uses mutexes and the
`LOCK` and `TRY_LOCK` macros to protect data structures.
Deadlocks due to inconsistent lock ordering (thread 1 locks `cs_main` and then
`cs_wallet`, while thread 2 locks them in the opposite order: result, deadlock
as each waits for the other to release its lock) are a problem. Compile with
`-DDEBUG_LOCKORDER` (or use `-DCMAKE_BUILD_TYPE=Debug`) to get lock order
inconsistencies reported in the debug.log file.
Re-architecting the core code so there are better-defined interfaces
between the various components is a goal, with any necessary locking
done by the components (e.g. see the self-contained `FillableSigningProvider` class
and its `cs_KeyStore` lock for example).
Threads
-------
- [Main thread (`bitcoind`)](https://www.bitcoinabc.org/doc/dev/bitcoind_8cpp.html#a0ddf1224851353fc92bfbff6f499fa97)
: Started from `main()` in `bitcoind.cpp`. Responsible for starting up and
shutting down the application.
- [ThreadImport (`b-loadblk`)](https://www.bitcoinabc.org/doc/dev/init_8cpp.html#ae9e290a0e829ec0198518de2eda579d1)
: Loads blocks from `blk*.dat` files or `-loadblock=<file>` on startup.
- [ThreadScriptCheck (`b-scriptch.x`)](https://www.bitcoinabc.org/doc/dev/validation_8cpp.html#a925a33e7952a157922b0bbb8dab29a20)
: Parallel script validation threads for transactions in blocks.
- [ThreadHTTP (`b-http`)](https://www.bitcoinabc.org/doc/dev/httpserver_8cpp.html#abb9f6ea8819672bd9a62d3695070709c)
: Libevent thread to listen for RPC and REST connections.
- [HTTP worker threads(`b-httpworker.x`)](https://www.bitcoinabc.org/doc/dev/httpserver_8cpp.html#aa6a7bc27265043bc0193220c5ae3a55f)
: Threads to service RPC and REST requests.
- [Indexer threads (`b-txindex`, etc)](https://www.bitcoinabc.org/doc/dev/class_base_index.html#a96a7407421fbf877509248bbe64f8d87)
: One thread per indexer.
- [SchedulerThread (`b-scheduler`)](https://www.bitcoinabc.org/doc/dev/class_c_scheduler.html#a14d2800815da93577858ea078aed1fba)
: Does asynchronous background tasks like dumping wallet contents, dumping
addrman and running asynchronous validationinterface callbacks.
- [TorControlThread (`b-torcontrol`)](https://www.bitcoinabc.org/doc/dev/torcontrol_8cpp.html#a4faed3692d57a0d7bdbecf3b37f72de0)
: Libevent thread for tor connections.
- Net threads:
- [ThreadMessageHandler (`b-msghand`)](https://www.bitcoinabc.org/doc/dev/class_c_connman.html#aacdbb7148575a31bb33bc345e2bf22a9)
: Application level message handling (sending and receiving). Almost
all net_processing and validation logic runs on this thread.
- [ThreadDNSAddressSeed (`b-dnsseed`)](https://www.bitcoinabc.org/doc/dev/class_c_connman.html#aa7c6970ed98a4a7bafbc071d24897d13)
: Loads addresses of peers from the DNS.
- [ThreadMapPort (`b-upnp`)](https://www.bitcoinabc.org/doc/dev/net_8cpp.html#a63f82a71c4169290c2db1651a9bbe249)
: Universal plug-and-play startup/shutdown.
- [ThreadSocketHandler (`b-net`)](https://www.bitcoinabc.org/doc/dev/class_c_connman.html#a765597cbfe99c083d8fa3d61bb464e34)
: Sends/Receives data from peers on port 8333.
- [ThreadOpenAddedConnections (`b-addcon`)](https://www.bitcoinabc.org/doc/dev/class_c_connman.html#a0b787caf95e52a346a2b31a580d60a62)
: Opens network connections to added nodes.
- [ThreadOpenConnections (`b-opencon`)](https://www.bitcoinabc.org/doc/dev/class_c_connman.html#a55e9feafc3bab78e5c9d408c207faa45)
: Initiates new connections to peers.
Ignoring IDE/editor files
--------------------------
In closed-source environments in which everyone uses the same IDE it is common
to add temporary files it produces to the project-wide `.gitignore` file.
However, in open source software such as Bitcoin ABC, where everyone uses
their own editors/IDE/tools, it is less common. Only you know what files your
editor produces and this may change from version to version. The canonical way
to do this is thus to create your local gitignore. Add this to `~/.gitconfig`:
```
[core]
excludesfile = /home/.../.gitignore_global
```
(alternatively, type the command `git config --global core.excludesfile ~/.gitignore_global`
on a terminal)
Then put your favorite tool's temporary filenames in that file, e.g.
```
# NetBeans
nbproject/
```
Another option is to create a per-repository excludes file `.git/info/exclude`.
These are not committed but apply only to one repository.
If a set of tools is used by the build system or scripts the repository (for
example, lcov) it is perfectly acceptable to add its files to `.gitignore`
and commit them.
Development guidelines
============================
A few non-style-related recommendations for developers, as well as points to
pay attention to for reviewers of Bitcoin ABC code.
Wallet
-------
- Make sure that no crashes happen with run-time option `-disablewallet`.
- *Rationale*: In RPC code that conditionally uses the wallet (such as
`validateaddress`) it is easy to forget that global pointer `pwalletMain`
can be NULL. See `test/functional/disablewallet.py` for functional tests
exercising the API with `-disablewallet`
- Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set
- *Rationale*: Otherwise compilation of the disable-wallet build will fail in environments without BerkeleyDB
General C++
-------------
- Assertions should not have side-effects
- *Rationale*: Even though the source code is set to refuse to compile
with assertions disabled, having side-effects in assertions is unexpected and
makes the code harder to understand
- If you use the `.h`, you must link the `.cpp`
- *Rationale*: Include files define the interface for the code in implementation files. Including one but
not linking the other is confusing. Please avoid that. Moving functions from
the `.h` to the `.cpp` should not result in build errors
- Use the RAII (Resource Acquisition Is Initialization) paradigm where possible. For example by using
`unique_ptr` for allocations in a function.
- *Rationale*: This avoids memory and resource leaks, and ensures exception safety
- Use `std::make_unique()` to construct objects owned by `unique_ptr`s
- *Rationale*: `std::make_unique` is concise and ensures exception safety in complex expressions.
C++ data structures
--------------------
- Never use the `std::map []` syntax when reading from a map, but instead use `.find()`
- *Rationale*: `[]` does an insert (of the default element) if the item doesn't
exist in the map yet. This has resulted in memory leaks in the past, as well as
race conditions (expecting read-read behavior). Using `[]` is fine for *writing* to a map
- Do not compare an iterator from one data structure with an iterator of
another data structure (even if of the same type)
- *Rationale*: Behavior is undefined. In C++ parlor this means "may reformat
the universe", in practice this has resulted in at least one hard-to-debug crash bug
- Watch out for out-of-bounds vector access. `&vch[vch.size()]` is illegal,
including `&vch[0]` for an empty vector. Use `vch.data()` and `vch.data() +
vch.size()` instead.
- Vector bounds checking is only enabled in debug mode. Do not rely on it
- Initialize all non-static class members where they are defined.
If this is skipped for a good reason (i.e., optimization on the critical
path), add an explicit comment about this
- *Rationale*: Ensure determinism by avoiding accidental use of uninitialized
values. Also, static analyzers balk about this.
Initializing the members in the declaration makes it easy to
spot uninitialized ones.
```cpp
class A
{
uint32_t m_count{0};
}
```
- By default, declare single-argument constructors `explicit`.
- *Rationale*: This is a precaution to avoid unintended conversions that might
arise when single-argument constructors are used as implicit conversion
functions.
- Use explicitly signed or unsigned `char`s, or even better `uint8_t` and
`int8_t`. Do not use bare `char` unless it is to pass to a third-party API.
This type can be signed or unsigned depending on the architecture, which can
lead to interoperability problems or dangerous conditions such as
out-of-bounds array accesses
- Prefer explicit constructions over implicit ones that rely on 'magical' C++ behavior
- *Rationale*: Easier to understand what is happening, thus easier to spot mistakes, even for those
that are not language lawyers
- Use `Span` as function argument when it can operate on any range-like container.
- *Rationale*: Compared to `Foo(const vector<int>&)` this avoids the need for a (potentially expensive)
conversion to vector if the caller happens to have the input stored in another type of container.
However, be aware of the pitfalls documented in [span.h](../src/span.h).
```cpp
void Foo(Span<const int> data);
std::vector<int> vec{1,2,3};
Foo(vec);
```
Strings and formatting
------------------------
- Use `std::string`, avoid C string manipulation functions
- *Rationale*: C++ string handling is marginally safer, less scope for
buffer overflows and surprises with `\0` characters. Also some C string manipulations
tend to act differently depending on platform, or even the user locale
- Use `ParseInt32`, `ParseInt64`, `ParseUInt32`, `ParseUInt64`, `ParseDouble` from `utilstrencodings.h` for number parsing
- *Rationale*: These functions do overflow checking, and avoid pesky locale issues
Variable names
--------------
The shadowing warning (`-Wshadow`) is enabled by default. It prevents issues rising
from using a different variable with the same name.
E.g. in member initializers, prepend `_` to the argument name shadowing the
member name:
```c++
class AddressBookPage
{
Mode m_mode;
}
AddressBookPage::AddressBookPage(Mode _mode) :
m_mode(_mode)
...
```
When using nested cycles, do not name the inner cycle variable the same as in
upper cycle etc.
Please name variables so that their names do not shadow variables defined in the source code.
Threads and synchronization
----------------------------
- Prefer `Mutex` type to `RecursiveMutex` one
- Consistently use [Clang Thread Safety Analysis](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) annotations to
get compile-time warnings about potential race conditions or deadlocks in code.
- In functions that are declared separately from where they are defined, the
thread safety annotations should be added exclusively to the function
declaration, to avoid shadowing the declaration's annotation and cause false
positives (lack of compile failure) if a new lock requirement is later added
to the declaration but the lock is not taken.
- Prefer locks that are in a class rather than global, and that are
internal to a class (private or protected) rather than public.
- Combine annotations in function declarations with run-time asserts in
function definitions:
```C++
// txmempool.h
class CTxMemPool {
public:
...
mutable RecursiveMutex cs;
...
void UpdateTransactionsFromBlock(...) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs);
...
}
// txmempool.cpp
void CTxMemPool::UpdateTransactionsFromBlock(...) {
AssertLockHeld(::cs_main);
AssertLockHeld(cs);
...
}
```
```C++
// validation.h
class CChainState {
protected:
...
Mutex m_chainstate_mutex;
...
public:
...
bool ActivateBestChain(
BlockValidationState& state,
std::shared_ptr<const CBlock> pblock = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex)
LOCKS_EXCLUDED(::cs_main);
...
bool PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex)
LOCKS_EXCLUDED(::cs_main);
...
}
// validation.cpp
bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex) {
AssertLockNotHeld(m_chainstate_mutex);
AssertLockNotHeld(::cs_main);
{
LOCK(cs_main);
...
}
return ActivateBestChain(state, std::shared_ptr<const CBlock>());
}
```
- Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential
deadlocks are introduced. As of 0.12, this is defined by default when
configuring with `-DCMAKE_BUILD_TYPE=Debug`
- When using `LOCK`/`TRY_LOCK` be aware that the lock exists in the context of
the current scope, so surround the statement and the code that needs the lock
with braces
OK:
```c++
{
TRY_LOCK(cs_vNodes, lockNodes);
...
}
```
Wrong:
```c++
TRY_LOCK(cs_vNodes, lockNodes);
{
...
}
```
Scripts
--------------------------
### Shebang
- Use `#!/usr/bin/env bash` instead of obsolete `#!/bin/bash`.
- [*Rationale*](https://github.com/dylanaraps/pure-bash-bible#shebang):
`#!/bin/bash` assumes it is always installed to /bin/ which can cause issues;
`#!/usr/bin/env bash` searches the user's PATH to find the bash binary.
OK:
```bash
#!/usr/bin/env bash
```
Wrong:
```bash
#!/bin/bash
```
Source code organization
--------------------------
- Implementation code should go into the `.cpp` file and not the `.h`, unless necessary due to template usage or
when performance due to inlining is critical
- *Rationale*: Shorter and simpler header files are easier to read, and reduce compile time
- Use only the lowercase alphanumerics (`a-z0-9`), underscore (`_`) and hyphen (`-`) in source code filenames.
- *Rationale*: `grep`:ing and auto-completing filenames is easier when using a consistent
naming pattern. Potential problems when building on case-insensitive filesystems are
avoided when using only lowercase characters in source code filenames.
- Don't import anything into the global namespace (`using namespace ...`). Use
fully specified types such as `std::string`.
- *Rationale*: Avoids symbol conflicts
- Terminate namespaces with a comment (`// namespace mynamespace`). The comment
should be placed on the same line as the brace closing the namespace, e.g.
```c++
namespace mynamespace {
...
} // namespace mynamespace
namespace {
...
} // namespace
```
- *Rationale*: Avoids confusion about the namespace context
Header Inclusions
-----------------
- Header inclusions should use angle brackets (`#include <>`).
The include path should be relative to the `src` folder.
e.g.: `#include <qt/test/guiutiltests.h>`
- Native C++ headers should be preferred over C compatibility headers.
e.g.: use `<cstdint>` instead of `<stdint.h>`
- In order to make the code consistent, header files should be included in the following order, with each
section separated by a newline:
1. In a .cpp file, the associated .h is in first position. In a test source, this is the header file under test.
2. The project headers.
3. The test headers.
4. The 3rd party libraries headers. Different libraries should be in different sections.
5. The system libraries.
All headers should be lexically ordered inside their block.
- Use include guards to avoid the problem of double inclusion. The header file
`foo/bar.h` should use the include guard identifier `BITCOIN_FOO_BAR_H`, e.g.
```c++
#ifndef BITCOIN_FOO_BAR_H
#define BITCOIN_FOO_BAR_H
...
#endif // BITCOIN_FOO_BAR_H
```
GUI
-----
- Do not display or manipulate dialogs in model code (classes `*Model`)
- *Rationale*: Model classes pass through events and data from the core, they
should not interact with the user. That's where View classes come in. The converse also
holds: try to not directly access core data structures from Views.
- Avoid adding slow or blocking code in the GUI thread. In particular do not
add new `interface::Node` and `interface::Wallet` method calls, even if they
may be fast now, in case they are changed to lock or communicate across
processes in the future.
Prefer to offload work from the GUI thread to worker threads (see
`RPCExecutor` in console code as an example) or take other steps (see
<https://doc.qt.io/archives/qq/qq27-responsive-guis.html>) to keep the GUI
responsive.
- *Rationale*: Blocking the GUI thread can increase latency, and lead to
hangs and deadlocks.
Unit Tests
-----------
- Test suite naming convention: The Boost test suite in file
`src/test/foo_tests.cpp` should be named `foo_tests`. Test suite names must
be unique.
Third party libraries
---------------------
Several parts of the repository are software maintained elsewhere.
Changes to these should preferably be sent upstream but bugfixes may also be
submitted to Bitcoin ABC so that they can be integrated quickly.
Cosmetic changes should be purely taken upstream.
Current third party libraries include:
- src/leveldb
- Upstream at <https://github.com/google/leveldb> ; Maintained by Google.
- **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb)
when merging upstream changes to Bitcoin ABC.
- src/secp256k1
- Upstream at <https://github.com/bitcoin-core/secp256k1/> ; actively maintained
by Bitcoin Core contributors.
Bitcoin ABC is using a modified version of libsecp256k1, some changes might
be directly submitted to Bitcoin ABC.
See the [secp256k1 README](../src/secp256k1/README.md) for details.
- src/crypto/ctaes
- Upstream at https://github.com/bitcoin-core/ctaes ; maintained by Bitcoin
Core contributors.
-- src/univalue
- - Upstream at https://github.com/bitcoin-core/univalue ; actively maintained by
- Bitcoin Core contributors, deviates from upstream https://github.com/jgarzik/univalue
-
Upgrading LevelDB
---------------------
Extra care must be taken when upgrading LevelDB. This section explains issues
you must be aware of.
### File Descriptor Counts
In most configurations we use the default LevelDB value for `max_open_files`,
which is 1000 at the time of this writing. If LevelDB actually uses this many
file descriptors it will cause problems with Bitcoin's `select()` loop, because
it may cause new sockets to be created where the fd value is >= 1024. For this
reason, on 64-bit Unix systems we rely on an internal LevelDB optimization that
uses `mmap()` + `close()` to open table files without actually retaining
references to the table file descriptors. If you are upgrading LevelDB, you must
sanity check the changes to make sure that this assumption remains valid.
In addition to reviewing the upstream changes in `env_posix.cc`, you can use `lsof` to
check this. For example, on Linux this command will show open `.ldb` file counts:
```bash
$ lsof -p $(pidof bitcoind) |\
awk 'BEGIN { fd=0; mem=0; } /ldb$/ { if ($4 == "mem") mem++; else fd++ } END { printf "mem = %s, fd = %s\n", mem, fd}'
mem = 119, fd = 0
```
The `mem` value shows how many files are mmap'ed, and the `fd` value shows you
many file descriptors these files are using. You should check that `fd` is a
small number (usually 0 on 64-bit hosts).
See the notes in the `SetMaxOpenFiles()` function in `dbwrapper.cc` for more
details.
### Consensus Compatibility
It is possible for LevelDB changes to inadvertently change consensus
compatibility between nodes. This happened in Bitcoin 0.8 (when LevelDB was
first introduced). When upgrading LevelDB you should review the upstream changes
to check for issues affecting consensus compatibility.
For example, if LevelDB had a bug that accidentally prevented a key from being
returned in an edge case, and that bug was fixed upstream, the bug "fix" would
be an incompatible consensus change. In this situation the correct behavior
would be to revert the upstream fix before applying the updates to Bitcoin ABC's
copy of LevelDB. In general you should be wary of any upstream changes affecting
what data is returned from LevelDB queries.
Git and GitHub tips
---------------------
- Github is not typically the source of truth for pull requests. See [CONTRIBUTING](../CONTRIBUTING.md) for instructions
on setting up your repo correctly.
- Similarly, your git remote origin should be set to: `ssh://vcs@reviews.bitcoinabc.org:2221/source/bitcoin-abc.git`
instead of github.com. See [CONTRIBUTING](../CONTRIBUTING.md).
For git and GitHub productivity tips, see [Productivity Notes](productivity.md).
Release notes
-------------
Release notes should be written for any PR that:
- introduces a notable new feature
- fixes a significant bug
- changes an API or configuration model
- makes any other visible change to the end-user experience.
Release notes should be added to the [/doc/release-notes.md](/doc/release-notes.md)
file, which is archived and cleared after each release.
RPC interface guidelines
--------------------------
A few guidelines for introducing and reviewing new RPC interfaces:
- Method naming: use consecutive lower-case names such as `getrawtransaction` and `submitblock`
- *Rationale*: Consistency with existing interface.
- Argument naming: use snake case `fee_delta` (and not, e.g. camel case `feeDelta`)
- *Rationale*: Consistency with existing interface.
- Use the JSON parser for parsing, don't manually parse integers or strings from
arguments unless absolutely necessary.
- *Rationale*: Introduces hand-rolled string manipulation code at both the caller and callee sites,
which is error prone, and it is easy to get things such as escaping wrong.
JSON already supports nested data structures, no need to re-invent the wheel.
- *Exception*: AmountFromValue can parse amounts as string. This was introduced because many JSON
parsers and formatters hard-code handling decimal numbers as floating point
values, resulting in potential loss of precision. This is unacceptable for
monetary values. **Always** use `AmountFromValue` and `ValueFromAmount` when
inputting or outputting monetary values. The only exceptions to this are
`prioritisetransaction` and `getblocktemplate` because their interface
is specified as-is in BIP22.
- Missing arguments and 'null' should be treated the same: as default values. If there is no
default value, both cases should fail in the same way. The easiest way to follow this
guideline is detect unspecified arguments with `params[x].isNull()` instead of
`params.size() <= x`. The former returns true if the argument is either null or missing,
while the latter returns true if is missing, and false if it is null.
- *Rationale*: Avoids surprises when switching to name-based arguments. Missing name-based arguments
are passed as 'null'.
- Try not to overload methods on argument type. E.g. don't make `getblock(true)` and `getblock("hash")`
do different things.
- *Rationale*: This is impossible to use with `bitcoin-cli`, and can be surprising to users.
- *Exception*: Some RPC calls can take both an `int` and `bool`, most notably when a bool was switched
to a multi-value, or due to other historical reasons. **Always** have false map to 0 and
true to 1 in this case.
- Don't forget to fill in the argument names correctly in the RPC command table.
- *Rationale*: If not, the call can not be used with name-based arguments.
- Set okSafeMode in the RPC command table to a sensible value: safe mode is when the
blockchain is regarded to be in a confused state, and the client deems it unsafe to
do anything irreversible such as send. Anything that just queries should be permitted.
- *Rationale*: Troubleshooting a node in safe mode is difficult if half the
RPCs don't work.
- Add every non-string RPC argument `(method, idx, name)` to the table `vRPCConvertParams` in `rpc/client.cpp`.
- *Rationale*: `bitcoin-cli` and the GUI debug console use this table to determine how to
convert a plaintext command line to JSON. If the types don't match, the method can be unusable
from there.
- A RPC method must either be a wallet method or a non-wallet method. Do not
introduce new methods such as `signrawtransaction` that differ in behavior
based on presence of a wallet.
- *Rationale*: As well as complicating the implementation and interfering
with the introduction of multi-wallet, wallet and non-wallet code should be
separated to avoid introducing circular dependencies between code units.
- Try to make the RPC response a JSON object.
- *Rationale*: If a RPC response is not a JSON object then it is harder to avoid API breakage if
new data in the response is needed.
- Wallet RPCs call BlockUntilSyncedToCurrentChain to maintain consistency with
`getblockchaininfo`'s state immediately prior to the call's execution. Wallet
RPCs whose behavior does *not* depend on the current chainstate may omit this
call.
- *Rationale*: In previous versions of Bitcoin Core, the wallet was always
in-sync with the chainstate (by virtue of them all being updated in the
same cs_main lock). In order to maintain the behavior that wallet RPCs
return results as of at least the highest best-known block an RPC
client may be aware of prior to entering a wallet RPC call, we must block
until the wallet is caught up to the chainstate as of the RPC call's entry.
This also makes the API much easier for RPC clients to reason about.
- Be aware of RPC method aliases and generally avoid registering the same
callback function pointer for different RPCs.
- *Rationale*: RPC methods registered with the same function pointer will be
considered aliases and only the first method name will show up in the
`help` RPC command list.
- *Exception*: Using RPC method aliases may be appropriate in cases where a
new RPC is replacing a deprecated RPC, to avoid both RPCs confusingly
showing up in the command list.
- Use the `UNIX_EPOCH_TIME` constant when describing UNIX epoch time or
timestamps in the documentation.
- *Rationale*: User-facing consistency.
- Use `fs::path::u8string()` and `fs::u8path()` functions when converting path
to JSON strings, not `fs::PathToString` and `fs::PathFromString`
- *Rationale*: JSON strings are Unicode strings, not byte strings, and
RFC8259 requires JSON to be encoded as UTF-8.
Internal interface guidelines
-----------------------------
Internal interfaces between parts of the codebase that are meant to be
independent (node, wallet, GUI), are defined in
[`src/interfaces/`](../src/interfaces/). The main interface classes defined
there are [`interfaces::Chain`](../src/interfaces/chain.h), used by wallet to
access the node's latest chain state,
[`interfaces::Node`](../src/interfaces/node.h), used by the GUI to control the
node, and [`interfaces::Wallet`](../src/interfaces/wallet.h), used by the GUI
to control an individual wallet. There are also more specialized interface
types like [`interfaces::Handler`](../src/interfaces/handler.h)
[`interfaces::ChainClient`](../src/interfaces/chain.h) passed to and from
various interface methods.
Interface classes are written in a particular style so node, wallet, and GUI
code doesn't need to run in the same process, and so the class declarations
work more easily with tools and libraries supporting interprocess
communication:
- Interface classes should be abstract and have methods that are [pure
virtual](https://en.cppreference.com/w/cpp/language/abstract_class). This
allows multiple implementations to inherit from the same interface class,
particularly so one implementation can execute functionality in the local
process, and other implementations can forward calls to remote processes.
- Interface method definitions should wrap existing functionality instead of
implementing new functionality. Any substantial new node or wallet
functionality should be implemented in [`src/node/`](../src/node/) or
[`src/wallet/`](../src/wallet/) and just exposed in
[`src/interfaces/`](../src/interfaces/) instead of being implemented there,
so it can be more modular and accessible to unit tests.
- Interface method parameter and return types should either be serializable or
be other interface classes. Interface methods shouldn't pass references to
objects that can't be serialized or accessed from another process.
Examples:
```c++
// Good: takes string argument and returns interface class pointer
virtual unique_ptr<interfaces::Wallet> loadWallet(std::string filename) = 0;
// Bad: returns CWallet reference that can't be used from another process
virtual CWallet& loadWallet(std::string filename) = 0;
```
```c++
// Good: accepts and returns primitive types
virtual bool findBlock(const uint256& hash, int& out_height, int64_t& out_time) = 0;
// Bad: returns pointer to internal node in a linked list inaccessible to
// other processes
virtual const CBlockIndex* findBlock(const uint256& hash) = 0;
```
```c++
// Good: takes plain callback type and returns interface pointer
using TipChangedFn = std::function<void(int block_height, int64_t block_time)>;
virtual std::unique_ptr<interfaces::Handler> handleTipChanged(TipChangedFn fn) = 0;
// Bad: returns boost connection specific to local process
using TipChangedFn = std::function<void(int block_height, int64_t block_time)>;
virtual boost::signals2::scoped_connection connectTipChanged(TipChangedFn fn) = 0;
```
- For consistency and friendliness to code generation tools, interface method
input and inout parameters should be ordered first and output parameters
should come last.
Example:
```c++
// Good: error output param is last
virtual bool broadcastTransaction(const CTransactionRef& tx, CAmount max_fee, std::string& error) = 0;
// Bad: error output param is between input params
virtual bool broadcastTransaction(const CTransactionRef& tx, std::string& error, CAmount max_fee) = 0;
```
- For friendliness to code generation tools, interface methods should not be
overloaded:
Example:
```c++
// Good: method names are unique
virtual bool disconnectByAddress(const CNetAddr& net_addr) = 0;
virtual bool disconnectById(NodeId id) = 0;
// Bad: methods are overloaded by type
virtual bool disconnect(const CNetAddr& net_addr) = 0;
virtual bool disconnect(NodeId id) = 0;
```
- For consistency and friendliness to code generation tools, interface method
names should be `lowerCamelCase` and standalone function names should be
`UpperCamelCase`.
Examples:
```c++
// Good: lowerCamelCase method name
virtual void blockConnected(const CBlock& block, int height) = 0;
// Bad: uppercase class method
virtual void BlockConnected(const CBlock& block, int height) = 0;
```
```c++
// Good: UpperCamelCase standalone function name
std::unique_ptr<Node> MakeNode(LocalInit& init);
// Bad: lowercase standalone function
std::unique_ptr<Node> makeNode(LocalInit& init);
```
Note: This last convention isn't generally followed outside of
[`src/interfaces/`](../src/interfaces/), though it did come up for discussion
before in [#14635](https://github.com/bitcoin/bitcoin/pull/14635).
diff --git a/src/univalue/.cirrus.yml b/src/univalue/.cirrus.yml
deleted file mode 100644
index f140fee12..000000000
--- a/src/univalue/.cirrus.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-env:
- MAKEJOBS: "-j4"
- RUN_TESTS: "true"
- BASE_OUTDIR: "$CIRRUS_WORKING_DIR/out_dir_base"
- DEBIAN_FRONTEND: "noninteractive"
-
-task:
- container:
- image: ubuntu:focal
- cpu: 1
- memory: 1G
- greedy: true # https://medium.com/cirruslabs/introducing-greedy-container-instances-29aad06dc2b4
-
- matrix:
- - name: "gcc"
- env:
- CC: "gcc"
- CXX: "g++"
- APT_PKGS: "gcc"
- - name: "clang"
- env:
- CC: "clang"
- CXX: "clang++"
- APT_PKGS: "clang"
- - name: "mingw"
- env:
- CC: ""
- CXX: ""
- UNIVALUE_CONFIG: "--host=x86_64-w64-mingw32"
- APT_PKGS: "g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 binutils-mingw-w64-x86-64"
- RUN_TESTS: "false"
-
- install_script:
- - apt update
- - apt install -y pkg-config build-essential libtool autotools-dev automake bsdmainutils
- - apt install -y $APT_PKGS
- autogen_script:
- - ./autogen.sh
- configure_script:
- - ./configure --cache-file=config.cache --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib $UNIVALUE_CONFIG
- make_script:
- - make $MAKEJOBS V=1
- test_script:
- - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS distcheck; fi
diff --git a/src/univalue/CMakeLists.txt b/src/univalue/CMakeLists.txt
index bb7d43314..1c177e63b 100644
--- a/src/univalue/CMakeLists.txt
+++ b/src/univalue/CMakeLists.txt
@@ -1,54 +1,44 @@
# Copyright (c) 2017 The Bitcoin developers
cmake_minimum_required(VERSION 3.16)
project(univalue)
option(UNIVALUE_BUILD_TESTS "Build univalue's unit tests" ON)
# TODO: Version info
add_library(univalue
lib/univalue.cpp
lib/univalue_get.cpp
lib/univalue_read.cpp
lib/univalue_write.cpp
)
target_include_directories(univalue
PUBLIC
include
PRIVATE
lib
)
if(UNIVALUE_BUILD_TESTS)
include(TestSuite)
create_test_suite(univalue)
function(create_univalue_test NAME FILES)
add_test_to_suite(univalue ${NAME} ${FILES})
target_link_libraries(${NAME} univalue)
endfunction()
create_univalue_test(unitester_test test/unitester.cpp)
target_compile_definitions(unitester_test
PUBLIC JSON_TEST_SRC="${PROJECT_SOURCE_DIR}/test"
)
create_univalue_test(no_nul_test test/no_nul.cpp)
create_univalue_test(object_test test/object.cpp)
# test_json is not meant to run in an automated test suite.
add_executable(json_test EXCLUDE_FROM_ALL test/test_json.cpp)
target_link_libraries(json_test univalue)
add_dependencies(check-univalue json_test)
endif(UNIVALUE_BUILD_TESTS)
-
-# Generate lib/univalue_escapes.h
-include(NativeExecutable)
-add_native_executable(univalue_gen gen/gen.cpp)
-native_target_include_directories(univalue_gen PUBLIC include)
-
-# Custom target to regenerate univalue_escapes.h
-add_custom_target(generate_univalue_escapes_h
- COMMAND univalue_gen > ${CMAKE_CURRENT_SOURCE_DIR}/lib/univalue_escapes.h
-)
diff --git a/src/univalue/COPYING b/src/univalue/COPYING
deleted file mode 100644
index 1fb429f35..000000000
--- a/src/univalue/COPYING
+++ /dev/null
@@ -1,19 +0,0 @@
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am
deleted file mode 100644
index 476f14b92..000000000
--- a/src/univalue/Makefile.am
+++ /dev/null
@@ -1,58 +0,0 @@
-include sources.mk
-ACLOCAL_AMFLAGS = -I build-aux/m4
-.PHONY: gen FORCE
-.INTERMEDIATE: $(GENBIN)
-
-include_HEADERS = $(UNIVALUE_DIST_HEADERS_INT)
-noinst_HEADERS = $(UNIVALUE_LIB_HEADERS_INT)
-
-lib_LTLIBRARIES = libunivalue.la
-
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = pc/libunivalue.pc
-
-libunivalue_la_SOURCES = $(UNIVALUE_LIB_SOURCES_INT)
-
-libunivalue_la_LDFLAGS = \
- -version-info $(LIBUNIVALUE_CURRENT):$(LIBUNIVALUE_REVISION):$(LIBUNIVALUE_AGE) \
- -no-undefined
-libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
-
-TESTS = test/object test/unitester test/no_nul
-
-GENBIN = gen/gen$(BUILD_EXEEXT)
-GEN_SRCS = gen/gen.cpp
-
-$(GENBIN): $(GEN_SRCS)
- @echo Building $@
- $(AM_V_at)c++ -I$(top_srcdir)/include -o $@ $<
-
-gen: $(GENBIN) FORCE
- @echo Updating lib/univalue_escapes.h
- $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h
-
-noinst_PROGRAMS = $(TESTS) test/test_json
-
-test_unitester_SOURCES = $(UNIVALUE_TEST_UNITESTER_INT)
-test_unitester_LDADD = libunivalue.la
-test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(UNIVALUE_TEST_DATA_DIR_INT)\"
-test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-
-test_test_json_SOURCES = $(UNIVALUE_TEST_JSON_INT)
-test_test_json_LDADD = libunivalue.la
-test_test_json_CXXFLAGS = -I$(top_srcdir)/include
-test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-
-test_no_nul_SOURCES = $(UNIVALUE_TEST_NO_NUL_INT)
-test_no_nul_LDADD = libunivalue.la
-test_no_nul_CXXFLAGS = -I$(top_srcdir)/include
-test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-
-test_object_SOURCES = $(UNIVALUE_TEST_OBJECT_INT)
-test_object_LDADD = libunivalue.la
-test_object_CXXFLAGS = -I$(top_srcdir)/include
-test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
-
-TEST_FILES = $(UNIVALUE_TEST_FILES_INT)
-
-EXTRA_DIST=$(UNIVALUE_TEST_FILES_INT) $(GEN_SRCS)
diff --git a/src/univalue/README.md b/src/univalue/README.md
deleted file mode 100644
index ddd9f329b..000000000
--- a/src/univalue/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-# UniValue
-
-## Summary
-
-A universal value class, with JSON encoding and decoding.
-
-UniValue is an abstract data type that may be a null, boolean, string,
-number, array container, or a key/value dictionary container, nested to
-an arbitrary depth.
-
-This class is aligned with the JSON standard, [RFC
-7159](https://tools.ietf.org/html/rfc7159.html).
-
-## Library usage
-
-This is a fork of univalue used by Bitcoin ABC. It is not maintained for usage
-by other projects. Notably, the API is broken in non-backward-compatible ways.
-
-Other projects looking for a maintained library should use the upstream
-univalue at https://github.com/jgarzik/univalue.
diff --git a/src/univalue/autogen.sh b/src/univalue/autogen.sh
deleted file mode 100755
index 4b38721fa..000000000
--- a/src/univalue/autogen.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-set -e
-srcdir="$(dirname $0)"
-cd "$srcdir"
-if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then
- LIBTOOLIZE="${GLIBTOOLIZE}"
- export LIBTOOLIZE
-fi
-autoreconf --install --force
diff --git a/src/univalue/build-aux/m4/.gitignore b/src/univalue/build-aux/m4/.gitignore
deleted file mode 100644
index f06368652..000000000
--- a/src/univalue/build-aux/m4/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.m4
diff --git a/src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4
deleted file mode 100644
index f7e513700..000000000
--- a/src/univalue/build-aux/m4/ax_cxx_compile_stdcxx.m4
+++ /dev/null
@@ -1,962 +0,0 @@
-# ===========================================================================
-# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
-#
-# DESCRIPTION
-#
-# Check for baseline language coverage in the compiler for the specified
-# version of the C++ standard. If necessary, add switches to CXX and
-# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
-# or '14' (for the C++14 standard).
-#
-# The second argument, if specified, indicates whether you insist on an
-# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
-# -std=c++11). If neither is specified, you get whatever works, with
-# preference for no added switch, and then for an extended mode.
-#
-# The third argument, if specified 'mandatory' or if left unspecified,
-# indicates that baseline support for the specified C++ standard is
-# required and that the macro should error out if no mode with that
-# support is found. If specified 'optional', then configuration proceeds
-# regardless, after defining HAVE_CXX${VERSION} if and only if a
-# supporting mode is found.
-#
-# LICENSE
-#
-# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
-# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
-# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
-# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
-# Copyright (c) 2015 Paul Norman <penorman@mac.com>
-# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
-# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
-# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
-# Copyright (c) 2020 Jason Merrill <jason@redhat.com>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved. This file is offered as-is, without any
-# warranty.
-
-#serial 12
-
-dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
-dnl (serial version number 13).
-
-AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
- m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
- [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
- [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
- [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
- m4_if([$2], [], [],
- [$2], [ext], [],
- [$2], [noext], [],
- [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
- m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
- [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
- [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
- [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
- AC_LANG_PUSH([C++])dnl
- ac_success=no
-
- m4_if([$2], [], [dnl
- AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
- ax_cv_cxx_compile_cxx$1,
- [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
- [ax_cv_cxx_compile_cxx$1=yes],
- [ax_cv_cxx_compile_cxx$1=no])])
- if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
- ac_success=yes
- fi])
-
- m4_if([$2], [noext], [], [dnl
- if test x$ac_success = xno; then
- for alternative in ${ax_cxx_compile_alternatives}; do
- switch="-std=gnu++${alternative}"
- cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
- AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
- $cachevar,
- [ac_save_CXX="$CXX"
- CXX="$CXX $switch"
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
- [eval $cachevar=yes],
- [eval $cachevar=no])
- CXX="$ac_save_CXX"])
- if eval test x\$$cachevar = xyes; then
- CXX="$CXX $switch"
- if test -n "$CXXCPP" ; then
- CXXCPP="$CXXCPP $switch"
- fi
- ac_success=yes
- break
- fi
- done
- fi])
-
- m4_if([$2], [ext], [], [dnl
- if test x$ac_success = xno; then
- dnl HP's aCC needs +std=c++11 according to:
- dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
- dnl Cray's crayCC needs "-h std=c++11"
- for alternative in ${ax_cxx_compile_alternatives}; do
- for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
- cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
- AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
- $cachevar,
- [ac_save_CXX="$CXX"
- CXX="$CXX $switch"
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
- [eval $cachevar=yes],
- [eval $cachevar=no])
- CXX="$ac_save_CXX"])
- if eval test x\$$cachevar = xyes; then
- CXX="$CXX $switch"
- if test -n "$CXXCPP" ; then
- CXXCPP="$CXXCPP $switch"
- fi
- ac_success=yes
- break
- fi
- done
- if test x$ac_success = xyes; then
- break
- fi
- done
- fi])
- AC_LANG_POP([C++])
- if test x$ax_cxx_compile_cxx$1_required = xtrue; then
- if test x$ac_success = xno; then
- AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
- fi
- fi
- if test x$ac_success = xno; then
- HAVE_CXX$1=0
- AC_MSG_NOTICE([No compiler with C++$1 support was found])
- else
- HAVE_CXX$1=1
- AC_DEFINE(HAVE_CXX$1,1,
- [define if the compiler supports basic C++$1 syntax])
- fi
- AC_SUBST(HAVE_CXX$1)
-])
-
-
-dnl Test body for checking C++11 support
-
-m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
- _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
-)
-
-
-dnl Test body for checking C++14 support
-
-m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
- _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
- _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
-)
-
-m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
- _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
- _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
- _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
-)
-
-dnl Tests for new features in C++11
-
-m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
-
-// If the compiler admits that it is not ready for C++11, why torture it?
-// Hopefully, this will speed up the test.
-
-#ifndef __cplusplus
-
-#error "This is not a C++ compiler"
-
-#elif __cplusplus < 201103L
-
-#error "This is not a C++11 compiler"
-
-#else
-
-namespace cxx11
-{
-
- namespace test_static_assert
- {
-
- template <typename T>
- struct check
- {
- static_assert(sizeof(int) <= sizeof(T), "not big enough");
- };
-
- }
-
- namespace test_final_override
- {
-
- struct Base
- {
- virtual ~Base() {}
- virtual void f() {}
- };
-
- struct Derived : public Base
- {
- virtual ~Derived() override {}
- virtual void f() override {}
- };
-
- }
-
- namespace test_double_right_angle_brackets
- {
-
- template < typename T >
- struct check {};
-
- typedef check<void> single_type;
- typedef check<check<void>> double_type;
- typedef check<check<check<void>>> triple_type;
- typedef check<check<check<check<void>>>> quadruple_type;
-
- }
-
- namespace test_decltype
- {
-
- int
- f()
- {
- int a = 1;
- decltype(a) b = 2;
- return a + b;
- }
-
- }
-
- namespace test_type_deduction
- {
-
- template < typename T1, typename T2 >
- struct is_same
- {
- static const bool value = false;
- };
-
- template < typename T >
- struct is_same<T, T>
- {
- static const bool value = true;
- };
-
- template < typename T1, typename T2 >
- auto
- add(T1 a1, T2 a2) -> decltype(a1 + a2)
- {
- return a1 + a2;
- }
-
- int
- test(const int c, volatile int v)
- {
- static_assert(is_same<int, decltype(0)>::value == true, "");
- static_assert(is_same<int, decltype(c)>::value == false, "");
- static_assert(is_same<int, decltype(v)>::value == false, "");
- auto ac = c;
- auto av = v;
- auto sumi = ac + av + 'x';
- auto sumf = ac + av + 1.0;
- static_assert(is_same<int, decltype(ac)>::value == true, "");
- static_assert(is_same<int, decltype(av)>::value == true, "");
- static_assert(is_same<int, decltype(sumi)>::value == true, "");
- static_assert(is_same<int, decltype(sumf)>::value == false, "");
- static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
- return (sumf > 0.0) ? sumi : add(c, v);
- }
-
- }
-
- namespace test_noexcept
- {
-
- int f() { return 0; }
- int g() noexcept { return 0; }
-
- static_assert(noexcept(f()) == false, "");
- static_assert(noexcept(g()) == true, "");
-
- }
-
- namespace test_constexpr
- {
-
- template < typename CharT >
- unsigned long constexpr
- strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
- {
- return *s ? strlen_c_r(s + 1, acc + 1) : acc;
- }
-
- template < typename CharT >
- unsigned long constexpr
- strlen_c(const CharT *const s) noexcept
- {
- return strlen_c_r(s, 0UL);
- }
-
- static_assert(strlen_c("") == 0UL, "");
- static_assert(strlen_c("1") == 1UL, "");
- static_assert(strlen_c("example") == 7UL, "");
- static_assert(strlen_c("another\0example") == 7UL, "");
-
- }
-
- namespace test_rvalue_references
- {
-
- template < int N >
- struct answer
- {
- static constexpr int value = N;
- };
-
- answer<1> f(int&) { return answer<1>(); }
- answer<2> f(const int&) { return answer<2>(); }
- answer<3> f(int&&) { return answer<3>(); }
-
- void
- test()
- {
- int i = 0;
- const int c = 0;
- static_assert(decltype(f(i))::value == 1, "");
- static_assert(decltype(f(c))::value == 2, "");
- static_assert(decltype(f(0))::value == 3, "");
- }
-
- }
-
- namespace test_uniform_initialization
- {
-
- struct test
- {
- static const int zero {};
- static const int one {1};
- };
-
- static_assert(test::zero == 0, "");
- static_assert(test::one == 1, "");
-
- }
-
- namespace test_lambdas
- {
-
- void
- test1()
- {
- auto lambda1 = [](){};
- auto lambda2 = lambda1;
- lambda1();
- lambda2();
- }
-
- int
- test2()
- {
- auto a = [](int i, int j){ return i + j; }(1, 2);
- auto b = []() -> int { return '0'; }();
- auto c = [=](){ return a + b; }();
- auto d = [&](){ return c; }();
- auto e = [a, &b](int x) mutable {
- const auto identity = [](int y){ return y; };
- for (auto i = 0; i < a; ++i)
- a += b--;
- return x + identity(a + b);
- }(0);
- return a + b + c + d + e;
- }
-
- int
- test3()
- {
- const auto nullary = [](){ return 0; };
- const auto unary = [](int x){ return x; };
- using nullary_t = decltype(nullary);
- using unary_t = decltype(unary);
- const auto higher1st = [](nullary_t f){ return f(); };
- const auto higher2nd = [unary](nullary_t f1){
- return [unary, f1](unary_t f2){ return f2(unary(f1())); };
- };
- return higher1st(nullary) + higher2nd(nullary)(unary);
- }
-
- }
-
- namespace test_variadic_templates
- {
-
- template <int...>
- struct sum;
-
- template <int N0, int... N1toN>
- struct sum<N0, N1toN...>
- {
- static constexpr auto value = N0 + sum<N1toN...>::value;
- };
-
- template <>
- struct sum<>
- {
- static constexpr auto value = 0;
- };
-
- static_assert(sum<>::value == 0, "");
- static_assert(sum<1>::value == 1, "");
- static_assert(sum<23>::value == 23, "");
- static_assert(sum<1, 2>::value == 3, "");
- static_assert(sum<5, 5, 11>::value == 21, "");
- static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
-
- }
-
- // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
- // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
- // because of this.
- namespace test_template_alias_sfinae
- {
-
- struct foo {};
-
- template<typename T>
- using member = typename T::member_type;
-
- template<typename T>
- void func(...) {}
-
- template<typename T>
- void func(member<T>*) {}
-
- void test();
-
- void test() { func<foo>(0); }
-
- }
-
-} // namespace cxx11
-
-#endif // __cplusplus >= 201103L
-
-]])
-
-
-dnl Tests for new features in C++14
-
-m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
-
-// If the compiler admits that it is not ready for C++14, why torture it?
-// Hopefully, this will speed up the test.
-
-#ifndef __cplusplus
-
-#error "This is not a C++ compiler"
-
-#elif __cplusplus < 201402L
-
-#error "This is not a C++14 compiler"
-
-#else
-
-namespace cxx14
-{
-
- namespace test_polymorphic_lambdas
- {
-
- int
- test()
- {
- const auto lambda = [](auto&&... args){
- const auto istiny = [](auto x){
- return (sizeof(x) == 1UL) ? 1 : 0;
- };
- const int aretiny[] = { istiny(args)... };
- return aretiny[0];
- };
- return lambda(1, 1L, 1.0f, '1');
- }
-
- }
-
- namespace test_binary_literals
- {
-
- constexpr auto ivii = 0b0000000000101010;
- static_assert(ivii == 42, "wrong value");
-
- }
-
- namespace test_generalized_constexpr
- {
-
- template < typename CharT >
- constexpr unsigned long
- strlen_c(const CharT *const s) noexcept
- {
- auto length = 0UL;
- for (auto p = s; *p; ++p)
- ++length;
- return length;
- }
-
- static_assert(strlen_c("") == 0UL, "");
- static_assert(strlen_c("x") == 1UL, "");
- static_assert(strlen_c("test") == 4UL, "");
- static_assert(strlen_c("another\0test") == 7UL, "");
-
- }
-
- namespace test_lambda_init_capture
- {
-
- int
- test()
- {
- auto x = 0;
- const auto lambda1 = [a = x](int b){ return a + b; };
- const auto lambda2 = [a = lambda1(x)](){ return a; };
- return lambda2();
- }
-
- }
-
- namespace test_digit_separators
- {
-
- constexpr auto ten_million = 100'000'000;
- static_assert(ten_million == 100000000, "");
-
- }
-
- namespace test_return_type_deduction
- {
-
- auto f(int& x) { return x; }
- decltype(auto) g(int& x) { return x; }
-
- template < typename T1, typename T2 >
- struct is_same
- {
- static constexpr auto value = false;
- };
-
- template < typename T >
- struct is_same<T, T>
- {
- static constexpr auto value = true;
- };
-
- int
- test()
- {
- auto x = 0;
- static_assert(is_same<int, decltype(f(x))>::value, "");
- static_assert(is_same<int&, decltype(g(x))>::value, "");
- return x;
- }
-
- }
-
-} // namespace cxx14
-
-#endif // __cplusplus >= 201402L
-
-]])
-
-
-dnl Tests for new features in C++17
-
-m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
-
-// If the compiler admits that it is not ready for C++17, why torture it?
-// Hopefully, this will speed up the test.
-
-#ifndef __cplusplus
-
-#error "This is not a C++ compiler"
-
-#elif __cplusplus < 201703L
-
-#error "This is not a C++17 compiler"
-
-#else
-
-#include <initializer_list>
-#include <utility>
-#include <type_traits>
-
-namespace cxx17
-{
-
- namespace test_constexpr_lambdas
- {
-
- constexpr int foo = [](){return 42;}();
-
- }
-
- namespace test::nested_namespace::definitions
- {
-
- }
-
- namespace test_fold_expression
- {
-
- template<typename... Args>
- int multiply(Args... args)
- {
- return (args * ... * 1);
- }
-
- template<typename... Args>
- bool all(Args... args)
- {
- return (args && ...);
- }
-
- }
-
- namespace test_extended_static_assert
- {
-
- static_assert (true);
-
- }
-
- namespace test_auto_brace_init_list
- {
-
- auto foo = {5};
- auto bar {5};
-
- static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
- static_assert(std::is_same<int, decltype(bar)>::value);
- }
-
- namespace test_typename_in_template_template_parameter
- {
-
- template<template<typename> typename X> struct D;
-
- }
-
- namespace test_fallthrough_nodiscard_maybe_unused_attributes
- {
-
- int f1()
- {
- return 42;
- }
-
- [[nodiscard]] int f2()
- {
- [[maybe_unused]] auto unused = f1();
-
- switch (f1())
- {
- case 17:
- f1();
- [[fallthrough]];
- case 42:
- f1();
- }
- return f1();
- }
-
- }
-
- namespace test_extended_aggregate_initialization
- {
-
- struct base1
- {
- int b1, b2 = 42;
- };
-
- struct base2
- {
- base2() {
- b3 = 42;
- }
- int b3;
- };
-
- struct derived : base1, base2
- {
- int d;
- };
-
- derived d1 {{1, 2}, {}, 4}; // full initialization
- derived d2 {{}, {}, 4}; // value-initialized bases
-
- }
-
- namespace test_general_range_based_for_loop
- {
-
- struct iter
- {
- int i;
-
- int& operator* ()
- {
- return i;
- }
-
- const int& operator* () const
- {
- return i;
- }
-
- iter& operator++()
- {
- ++i;
- return *this;
- }
- };
-
- struct sentinel
- {
- int i;
- };
-
- bool operator== (const iter& i, const sentinel& s)
- {
- return i.i == s.i;
- }
-
- bool operator!= (const iter& i, const sentinel& s)
- {
- return !(i == s);
- }
-
- struct range
- {
- iter begin() const
- {
- return {0};
- }
-
- sentinel end() const
- {
- return {5};
- }
- };
-
- void f()
- {
- range r {};
-
- for (auto i : r)
- {
- [[maybe_unused]] auto v = i;
- }
- }
-
- }
-
- namespace test_lambda_capture_asterisk_this_by_value
- {
-
- struct t
- {
- int i;
- int foo()
- {
- return [*this]()
- {
- return i;
- }();
- }
- };
-
- }
-
- namespace test_enum_class_construction
- {
-
- enum class byte : unsigned char
- {};
-
- byte foo {42};
-
- }
-
- namespace test_constexpr_if
- {
-
- template <bool cond>
- int f ()
- {
- if constexpr(cond)
- {
- return 13;
- }
- else
- {
- return 42;
- }
- }
-
- }
-
- namespace test_selection_statement_with_initializer
- {
-
- int f()
- {
- return 13;
- }
-
- int f2()
- {
- if (auto i = f(); i > 0)
- {
- return 3;
- }
-
- switch (auto i = f(); i + 4)
- {
- case 17:
- return 2;
-
- default:
- return 1;
- }
- }
-
- }
-
- namespace test_template_argument_deduction_for_class_templates
- {
-
- template <typename T1, typename T2>
- struct pair
- {
- pair (T1 p1, T2 p2)
- : m1 {p1},
- m2 {p2}
- {}
-
- T1 m1;
- T2 m2;
- };
-
- void f()
- {
- [[maybe_unused]] auto p = pair{13, 42u};
- }
-
- }
-
- namespace test_non_type_auto_template_parameters
- {
-
- template <auto n>
- struct B
- {};
-
- B<5> b1;
- B<'a'> b2;
-
- }
-
- namespace test_structured_bindings
- {
-
- int arr[2] = { 1, 2 };
- std::pair<int, int> pr = { 1, 2 };
-
- auto f1() -> int(&)[2]
- {
- return arr;
- }
-
- auto f2() -> std::pair<int, int>&
- {
- return pr;
- }
-
- struct S
- {
- int x1 : 2;
- volatile double y1;
- };
-
- S f3()
- {
- return {};
- }
-
- auto [ x1, y1 ] = f1();
- auto& [ xr1, yr1 ] = f1();
- auto [ x2, y2 ] = f2();
- auto& [ xr2, yr2 ] = f2();
- const auto [ x3, y3 ] = f3();
-
- }
-
- namespace test_exception_spec_type_system
- {
-
- struct Good {};
- struct Bad {};
-
- void g1() noexcept;
- void g2();
-
- template<typename T>
- Bad
- f(T*, T*);
-
- template<typename T1, typename T2>
- Good
- f(T1*, T2*);
-
- static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
-
- }
-
- namespace test_inline_variables
- {
-
- template<class T> void f(T)
- {}
-
- template<class T> inline T g(T)
- {
- return T{};
- }
-
- template<> inline void f<>(int)
- {}
-
- template<> int g<>(int)
- {
- return 5;
- }
-
- }
-
-} // namespace cxx17
-
-#endif // __cplusplus < 201703L
-
-]])
diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac
deleted file mode 100644
index f84eb9ac0..000000000
--- a/src/univalue/configure.ac
+++ /dev/null
@@ -1,72 +0,0 @@
-m4_define([libunivalue_major_version], [1])
-m4_define([libunivalue_minor_version], [1])
-m4_define([libunivalue_micro_version], [4])
-m4_define([libunivalue_interface_age], [4])
-# If you need a modifier for the version number.
-# Normally empty, but can be used to make "fixup" releases.
-m4_define([libunivalue_extraversion], [])
-
-dnl libtool versioning from libunivalue
-m4_define([libunivalue_current], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version - libunivalue_interface_age)])
-m4_define([libunivalue_binary_age], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version)])
-m4_define([libunivalue_revision], [libunivalue_interface_age])
-m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_interface_age)])
-m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()])
-
-
-AC_INIT([univalue], [1.1.1],
- [http://github.com/jgarzik/univalue/])
-
-dnl make the compilation flags quiet unless V=1 is used
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-
-AC_PREREQ(2.60)
-AC_CONFIG_SRCDIR([lib/univalue.cpp])
-AC_CONFIG_AUX_DIR([build-aux])
-AC_CONFIG_MACRO_DIR([build-aux/m4])
-AC_CONFIG_HEADERS([univalue-config.h])
-AM_INIT_AUTOMAKE([subdir-objects foreign])
-
-LIBUNIVALUE_MAJOR_VERSION=libunivalue_major_version
-LIBUNIVALUE_MINOR_VERSION=libunivalue_minor_version
-LIBUNIVALUE_MICRO_VERSION=libunivalue_micro_version
-LIBUNIVALUE_INTERFACE_AGE=libunivalue_interface_age
-
-# ABI version
-# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-LIBUNIVALUE_CURRENT=libunivalue_current
-LIBUNIVALUE_REVISION=libunivalue_revision
-LIBUNIVALUE_AGE=libunivalue_age
-
-AC_SUBST(LIBUNIVALUE_CURRENT)
-AC_SUBST(LIBUNIVALUE_REVISION)
-AC_SUBST(LIBUNIVALUE_AGE)
-
-LT_INIT
-LT_LANG([C++])
-
-dnl Require C++17 compiler (no GNU extensions)
-AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory], [nodefault])
-
-case $host in
- *mingw*)
- LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"
- ;;
-esac
-
-BUILD_EXEEXT=
-case $build in
- *mingw*)
- BUILD_EXEEXT=".exe"
- ;;
-esac
-
-AC_CONFIG_FILES([
- Makefile
- pc/libunivalue.pc
- pc/libunivalue-uninstalled.pc])
-
-AC_SUBST(LIBTOOL_APP_LDFLAGS)
-AC_SUBST(BUILD_EXEEXT)
-AC_OUTPUT
-
diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp
deleted file mode 100644
index ca5b470dd..000000000
--- a/src/univalue/gen/gen.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2014 BitPay Inc.
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or https://opensource.org/licenses/mit-license.php.
-
-//
-// To re-create univalue_escapes.h:
-// $ g++ -o gen gen.cpp
-// $ ./gen > univalue_escapes.h
-//
-
-#include <univalue.h>
-
-#include <cstdio>
-#include <cstring>
-#include <string>
-
-static bool initEscapes;
-static std::string escapes[256];
-
-static void initJsonEscape()
-{
- // Escape all lower control characters (some get overridden with smaller sequences below)
- for (int ch=0x00; ch<0x20; ++ch) {
- char tmpbuf[20];
- snprintf(tmpbuf, sizeof(tmpbuf), "\\u%04x", ch);
- escapes[ch] = std::string(tmpbuf);
- }
-
- escapes[(int)'"'] = "\\\"";
- escapes[(int)'\\'] = "\\\\";
- escapes[(int)'\b'] = "\\b";
- escapes[(int)'\f'] = "\\f";
- escapes[(int)'\n'] = "\\n";
- escapes[(int)'\r'] = "\\r";
- escapes[(int)'\t'] = "\\t";
- escapes[(int)'\x7f'] = "\\u007f"; // U+007F DELETE
-
- initEscapes = true;
-}
-
-static void outputEscape()
-{
- printf( "// Automatically generated file. Do not modify.\n"
- "#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n"
- "#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n"
- "static const char *escapes[256] = {\n");
-
- for (unsigned int i = 0; i < 256; i++) {
- if (escapes[i].empty()) {
- printf("\tnullptr,\n");
- } else {
- printf("\t\"");
-
- unsigned int si;
- for (si = 0; si < escapes[i].size(); si++) {
- char ch = escapes[i][si];
- switch (ch) {
- case '"':
- printf("\\\"");
- break;
- case '\\':
- printf("\\\\");
- break;
- default:
- printf("%c", escapes[i][si]);
- break;
- }
- }
-
- printf("\",\n");
- }
- }
-
- printf( "};\n"
- "#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n");
-}
-
-int main (int argc, char *argv[])
-{
- initJsonEscape();
- outputEscape();
- return 0;
-}
-
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index 4c9370a60..0d49f8f44 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -1,206 +1,206 @@
// Copyright 2014 BitPay Inc.
// Copyright 2015 Bitcoin Core Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#ifndef __UNIVALUE_H__
-#define __UNIVALUE_H__
+#ifndef BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H
+#define BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H
#include <charconv>
#include <cstdint>
#include <cstring>
#include <map>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
namespace {
struct UniValueStreamWriter;
}
class UniValue {
friend struct ::UniValueStreamWriter;
public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
UniValue() : typ(VNULL) {}
UniValue(UniValue::VType type, const std::string& value = std::string()) : typ(type), val(value) {}
UniValue(uint64_t val_) {
setInt(val_);
}
UniValue(int64_t val_) {
setInt(val_);
}
UniValue(bool val_) {
setBool(val_);
}
UniValue(int val_) {
setInt(val_);
}
UniValue(double val_) {
setFloat(val_);
}
UniValue(const std::string& val_) {
setStr(val_);
}
UniValue(const char *val_) {
std::string s(val_);
setStr(s);
}
void clear();
void reserve(size_t n) {
if (typ == VOBJ || typ == VARR) {
if (typ == VOBJ)
keys.reserve(n);
values.reserve(n);
} else if (typ != VNULL) {
val.reserve(n);
}
}
bool setNull();
bool setBool(bool val);
bool setNumStr(const std::string& val);
bool setInt(uint64_t val);
bool setInt(int64_t val);
bool setInt(int val_) { return setInt((int64_t)val_); }
bool setFloat(double val);
bool setStr(const std::string& val);
bool setArray();
bool setObject();
enum VType getType() const { return typ; }
const std::string& getValStr() const { return val; }
bool empty() const { return (values.size() == 0); }
size_t size() const { return values.size(); }
bool getBool() const { return isTrue(); }
void getObjMap(std::map<std::string,UniValue>& kv) const;
bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes) const;
const UniValue& operator[](const std::string& key) const;
const UniValue& operator[](size_t index) const;
bool exists(const std::string& key) const { size_t i; return findKey(key, i); }
bool isNull() const { return (typ == VNULL); }
bool isTrue() const { return (typ == VBOOL) && (val == "1"); }
bool isFalse() const { return (typ == VBOOL) && (val != "1"); }
bool isBool() const { return (typ == VBOOL); }
bool isStr() const { return (typ == VSTR); }
bool isNum() const { return (typ == VNUM); }
bool isArray() const { return (typ == VARR); }
bool isObject() const { return (typ == VOBJ); }
bool push_back(const UniValue& val);
bool push_backV(const std::vector<UniValue>& vec);
void __pushKV(const std::string& key, const UniValue& val);
bool pushKV(const std::string& key, const UniValue& val);
bool pushKVs(const UniValue& obj);
std::string write(unsigned int prettyIndent = 0,
unsigned int indentLevel = 0) const;
bool read(const char *raw, size_t len);
bool read(const char *raw) { return read(raw, strlen(raw)); }
bool read(const std::string& rawStr) {
return read(rawStr.data(), rawStr.size());
}
private:
UniValue::VType typ;
std::string val; // numbers are stored as C++ strings
std::vector<std::string> keys;
std::vector<UniValue> values;
bool findKey(const std::string& key, size_t& retIdx) const;
public:
// Strict type-specific getters, these throw std::runtime_error if the
// value is of unexpected type
const std::vector<std::string>& getKeys() const;
const std::vector<UniValue>& getValues() const;
template <typename Int>
auto getInt() const
{
static_assert(std::is_integral<Int>::value);
if (typ != VNUM) {
throw std::runtime_error("JSON value is not an integer as expected");
}
Int result;
const auto [first_nonmatching, error_condition] = std::from_chars(val.data(), val.data() + val.size(), result);
if (first_nonmatching != val.data() + val.size() || error_condition != std::errc{}) {
throw std::runtime_error("JSON integer out of range");
}
return result;
}
bool get_bool() const;
const std::string& get_str() const;
double get_real() const;
const UniValue& get_obj() const;
const UniValue& get_array() const;
enum VType type() const { return getType(); }
const UniValue& find_value(std::string_view key) const;
};
enum jtokentype {
JTOK_ERR = -1,
JTOK_NONE = 0, // eof
JTOK_OBJ_OPEN,
JTOK_OBJ_CLOSE,
JTOK_ARR_OPEN,
JTOK_ARR_CLOSE,
JTOK_COLON,
JTOK_COMMA,
JTOK_KW_NULL,
JTOK_KW_TRUE,
JTOK_KW_FALSE,
JTOK_NUMBER,
JTOK_STRING,
};
extern enum jtokentype getJsonToken(std::string& tokenVal,
unsigned int& consumed, const char *raw, const char *end);
extern const char *uvTypeName(UniValue::VType t);
static inline bool jsonTokenIsValue(enum jtokentype jtt)
{
switch (jtt) {
case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE:
case JTOK_NUMBER:
case JTOK_STRING:
return true;
default:
return false;
}
// not reached
}
static inline bool json_isspace(int ch)
{
switch (ch) {
case 0x20:
case 0x09:
case 0x0a:
case 0x0d:
return true;
default:
return false;
}
// not reached
}
extern const UniValue NullUniValue;
-#endif // __UNIVALUE_H__
+#endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H
diff --git a/src/univalue/lib/univalue_escapes.h b/src/univalue/include/univalue_escapes.h
similarity index 93%
rename from src/univalue/lib/univalue_escapes.h
rename to src/univalue/include/univalue_escapes.h
index 3f714f8e5..83767e8ac 100644
--- a/src/univalue/lib/univalue_escapes.h
+++ b/src/univalue/include/univalue_escapes.h
@@ -1,262 +1,261 @@
-// Automatically generated file. Do not modify.
-#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
-#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
+#ifndef BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_ESCAPES_H
+#define BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_ESCAPES_H
static const char *escapes[256] = {
"\\u0000",
"\\u0001",
"\\u0002",
"\\u0003",
"\\u0004",
"\\u0005",
"\\u0006",
"\\u0007",
"\\b",
"\\t",
"\\n",
"\\u000b",
"\\f",
"\\r",
"\\u000e",
"\\u000f",
"\\u0010",
"\\u0011",
"\\u0012",
"\\u0013",
"\\u0014",
"\\u0015",
"\\u0016",
"\\u0017",
"\\u0018",
"\\u0019",
"\\u001a",
"\\u001b",
"\\u001c",
"\\u001d",
"\\u001e",
"\\u001f",
nullptr,
nullptr,
"\\\"",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
"\\\\",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
"\\u007f",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
-#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H
+#endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_ESCAPES_H
diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/include/univalue_utffilter.h
similarity index 96%
rename from src/univalue/lib/univalue_utffilter.h
rename to src/univalue/include/univalue_utffilter.h
index c24ac58ea..f688eaaa3 100644
--- a/src/univalue/lib/univalue_utffilter.h
+++ b/src/univalue/include/univalue_utffilter.h
@@ -1,119 +1,119 @@
// Copyright 2016 Wladimir J. van der Laan
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
-#ifndef UNIVALUE_UTFFILTER_H
-#define UNIVALUE_UTFFILTER_H
+#ifndef BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_UTFFILTER_H
+#define BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_UTFFILTER_H
#include <string>
/**
* Filter that generates and validates UTF-8, as well as collates UTF-16
* surrogate pairs as specified in RFC4627.
*/
class JSONUTF8StringFilter
{
public:
explicit JSONUTF8StringFilter(std::string &s):
str(s), is_valid(true), codepoint(0), state(0), surpair(0)
{
}
// Write single 8-bit char (may be part of UTF-8 sequence)
void push_back(unsigned char ch)
{
if (state == 0) {
if (ch < 0x80) // 7-bit ASCII, fast direct pass-through
str.push_back(ch);
else if (ch < 0xc0) // Mid-sequence character, invalid in this state
is_valid = false;
else if (ch < 0xe0) { // Start of 2-byte sequence
codepoint = (ch & 0x1f) << 6;
state = 6;
} else if (ch < 0xf0) { // Start of 3-byte sequence
codepoint = (ch & 0x0f) << 12;
state = 12;
} else if (ch < 0xf8) { // Start of 4-byte sequence
codepoint = (ch & 0x07) << 18;
state = 18;
} else // Reserved, invalid
is_valid = false;
} else {
if ((ch & 0xc0) != 0x80) // Not a continuation, invalid
is_valid = false;
state -= 6;
codepoint |= (ch & 0x3f) << state;
if (state == 0)
push_back_u(codepoint);
}
}
// Write codepoint directly, possibly collating surrogate pairs
void push_back_u(unsigned int codepoint_)
{
if (state) // Only accept full codepoints in open state
is_valid = false;
if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair
if (surpair) // Two subsequent surrogate pair openers - fail
is_valid = false;
else
surpair = codepoint_;
} else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair
if (surpair) { // Open surrogate pair, expect second half
// Compute code point from UTF-16 surrogate pair
append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00));
surpair = 0;
} else // Second half doesn't follow a first half - fail
is_valid = false;
} else {
if (surpair) // First half of surrogate pair not followed by second - fail
is_valid = false;
else
append_codepoint(codepoint_);
}
}
// Check that we're in a state where the string can be ended
// No open sequences, no open surrogate pairs, etc
bool finalize()
{
if (state || surpair)
is_valid = false;
return is_valid;
}
private:
std::string &str;
bool is_valid;
// Current UTF-8 decoding state
unsigned int codepoint;
int state; // Top bit to be filled in for next UTF-8 byte, or 0
// Keep track of the following state to handle the following section of
// RFC4627:
//
// To escape an extended character that is not in the Basic Multilingual
// Plane, the character is represented as a twelve-character sequence,
// encoding the UTF-16 surrogate pair. So, for example, a string
// containing only the G clef character (U+1D11E) may be represented as
// "\uD834\uDD1E".
//
// Two subsequent \u.... may have to be replaced with one actual codepoint.
unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0
void append_codepoint(unsigned int codepoint_)
{
if (codepoint_ <= 0x7f)
str.push_back((char)codepoint_);
else if (codepoint_ <= 0x7FF) {
str.push_back((char)(0xC0 | (codepoint_ >> 6)));
str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} else if (codepoint_ <= 0xFFFF) {
str.push_back((char)(0xE0 | (codepoint_ >> 12)));
str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
} else if (codepoint_ <= 0x1FFFFF) {
str.push_back((char)(0xF0 | (codepoint_ >> 18)));
str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F)));
str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F)));
str.push_back((char)(0x80 | (codepoint_ & 0x3F)));
}
}
};
-#endif
+#endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_UTFFILTER_H
diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp
index 55ef9cbdc..ddf94f75b 100644
--- a/src/univalue/lib/univalue_read.cpp
+++ b/src/univalue/lib/univalue_read.cpp
@@ -1,467 +1,467 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <univalue.h>
-#include "univalue_utffilter.h"
+#include <univalue_utffilter.h>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
/*
* According to stackexchange, the original json test suite wanted
* to limit depth to 22. Widely-deployed PHP bails at depth 512,
* so we will follow PHP's lead, which should be more than sufficient
* (further stackexchange comments indicate depth > 32 rarely occurs).
*/
static constexpr size_t MAX_JSON_DEPTH = 512;
static bool json_isdigit(int ch)
{
return ((ch >= '0') && (ch <= '9'));
}
// convert hexadecimal string to unsigned integer
static const char *hatoui(const char *first, const char *last,
unsigned int& out)
{
unsigned int result = 0;
for (; first != last; ++first)
{
int digit;
if (json_isdigit(*first))
digit = *first - '0';
else if (*first >= 'a' && *first <= 'f')
digit = *first - 'a' + 10;
else if (*first >= 'A' && *first <= 'F')
digit = *first - 'A' + 10;
else
break;
result = 16 * result + digit;
}
out = result;
return first;
}
enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
const char *raw, const char *end)
{
tokenVal.clear();
consumed = 0;
const char *rawStart = raw;
while (raw < end && (json_isspace(*raw))) // skip whitespace
raw++;
if (raw >= end)
return JTOK_NONE;
switch (*raw) {
case '{':
raw++;
consumed = (raw - rawStart);
return JTOK_OBJ_OPEN;
case '}':
raw++;
consumed = (raw - rawStart);
return JTOK_OBJ_CLOSE;
case '[':
raw++;
consumed = (raw - rawStart);
return JTOK_ARR_OPEN;
case ']':
raw++;
consumed = (raw - rawStart);
return JTOK_ARR_CLOSE;
case ':':
raw++;
consumed = (raw - rawStart);
return JTOK_COLON;
case ',':
raw++;
consumed = (raw - rawStart);
return JTOK_COMMA;
case 'n':
case 't':
case 'f':
if (!strncmp(raw, "null", 4)) {
raw += 4;
consumed = (raw - rawStart);
return JTOK_KW_NULL;
} else if (!strncmp(raw, "true", 4)) {
raw += 4;
consumed = (raw - rawStart);
return JTOK_KW_TRUE;
} else if (!strncmp(raw, "false", 5)) {
raw += 5;
consumed = (raw - rawStart);
return JTOK_KW_FALSE;
} else
return JTOK_ERR;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
// part 1: int
std::string numStr;
const char *first = raw;
const char *firstDigit = first;
if (!json_isdigit(*firstDigit))
firstDigit++;
if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
return JTOK_ERR;
numStr += *raw; // copy first char
raw++;
if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
return JTOK_ERR;
while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
// part 2: frac
if (raw < end && *raw == '.') {
numStr += *raw; // copy .
raw++;
if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
// part 3: exp
if (raw < end && (*raw == 'e' || *raw == 'E')) {
numStr += *raw; // copy E
raw++;
if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
numStr += *raw;
raw++;
}
if (raw >= end || !json_isdigit(*raw))
return JTOK_ERR;
while (raw < end && json_isdigit(*raw)) { // copy digits
numStr += *raw;
raw++;
}
}
tokenVal = numStr;
consumed = (raw - rawStart);
return JTOK_NUMBER;
}
case '"': {
raw++; // skip "
std::string valStr;
JSONUTF8StringFilter writer(valStr);
while (true) {
if (raw >= end || (unsigned char)*raw < 0x20)
return JTOK_ERR;
else if (*raw == '\\') {
raw++; // skip backslash
if (raw >= end)
return JTOK_ERR;
switch (*raw) {
case '"': writer.push_back('\"'); break;
case '\\': writer.push_back('\\'); break;
case '/': writer.push_back('/'); break;
case 'b': writer.push_back('\b'); break;
case 'f': writer.push_back('\f'); break;
case 'n': writer.push_back('\n'); break;
case 'r': writer.push_back('\r'); break;
case 't': writer.push_back('\t'); break;
case 'u': {
unsigned int codepoint;
if (raw + 1 + 4 >= end ||
hatoui(raw + 1, raw + 1 + 4, codepoint) !=
raw + 1 + 4)
return JTOK_ERR;
writer.push_back_u(codepoint);
raw += 4;
break;
}
default:
return JTOK_ERR;
}
raw++; // skip esc'd char
}
else if (*raw == '"') {
raw++; // skip "
break; // stop scanning
}
else {
writer.push_back(static_cast<unsigned char>(*raw));
raw++;
}
}
if (!writer.finalize())
return JTOK_ERR;
tokenVal = valStr;
consumed = (raw - rawStart);
return JTOK_STRING;
}
default:
return JTOK_ERR;
}
}
enum expect_bits : unsigned {
EXP_OBJ_NAME = (1U << 0),
EXP_COLON = (1U << 1),
EXP_ARR_VALUE = (1U << 2),
EXP_VALUE = (1U << 3),
EXP_NOT_VALUE = (1U << 4),
};
#define expect(bit) (expectMask & (EXP_##bit))
#define setExpect(bit) (expectMask |= EXP_##bit)
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
bool UniValue::read(const char *raw, size_t size)
{
clear();
uint32_t expectMask = 0;
std::vector<UniValue*> stack;
std::string tokenVal;
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
const char* end = raw + size;
do {
last_tok = tok;
tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok == JTOK_NONE || tok == JTOK_ERR)
goto return_fail;
raw += consumed;
bool isValueOpen = jsonTokenIsValue(tok) ||
tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
if (expect(VALUE)) {
if (!isValueOpen)
goto return_fail;
clearExpect(VALUE);
} else if (expect(ARR_VALUE)) {
bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
if (!isArrValue)
goto return_fail;
clearExpect(ARR_VALUE);
} else if (expect(OBJ_NAME)) {
bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
if (!isObjName)
goto return_fail;
} else if (expect(COLON)) {
if (tok != JTOK_COLON)
goto return_fail;
clearExpect(COLON);
} else if (!expect(COLON) && (tok == JTOK_COLON)) {
goto return_fail;
}
if (expect(NOT_VALUE)) {
if (isValueOpen)
goto return_fail;
clearExpect(NOT_VALUE);
}
switch (tok) {
case JTOK_OBJ_OPEN:
case JTOK_ARR_OPEN: {
VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
if (!stack.size()) {
if (utyp == VOBJ)
setObject();
else
setArray();
stack.push_back(this);
} else {
UniValue tmpVal(utyp);
UniValue *top = stack.back();
top->values.push_back(tmpVal);
UniValue *newTop = &(top->values.back());
stack.push_back(newTop);
}
if (stack.size() > MAX_JSON_DEPTH)
goto return_fail;
if (utyp == VOBJ)
setExpect(OBJ_NAME);
else
setExpect(ARR_VALUE);
break;
}
case JTOK_OBJ_CLOSE:
case JTOK_ARR_CLOSE: {
if (!stack.size() || (last_tok == JTOK_COMMA))
goto return_fail;
VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
UniValue *top = stack.back();
if (utyp != top->getType())
goto return_fail;
stack.pop_back();
clearExpect(OBJ_NAME);
setExpect(NOT_VALUE);
break;
}
case JTOK_COLON: {
if (!stack.size())
goto return_fail;
UniValue *top = stack.back();
if (top->getType() != VOBJ)
goto return_fail;
setExpect(VALUE);
break;
}
case JTOK_COMMA: {
if (!stack.size() ||
(last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
goto return_fail;
UniValue *top = stack.back();
if (top->getType() == VOBJ)
setExpect(OBJ_NAME);
else
setExpect(ARR_VALUE);
break;
}
case JTOK_KW_NULL:
case JTOK_KW_TRUE:
case JTOK_KW_FALSE: {
UniValue tmpVal;
switch (tok) {
case JTOK_KW_NULL:
// do nothing more
break;
case JTOK_KW_TRUE:
tmpVal.setBool(true);
break;
case JTOK_KW_FALSE:
tmpVal.setBool(false);
break;
default: /* impossible */ break;
}
if (!stack.size()) {
*this = tmpVal;
break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
setExpect(NOT_VALUE);
break;
}
case JTOK_NUMBER: {
UniValue tmpVal(VNUM, tokenVal);
if (!stack.size()) {
*this = tmpVal;
break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
setExpect(NOT_VALUE);
break;
}
case JTOK_STRING: {
if (expect(OBJ_NAME)) {
UniValue *top = stack.back();
top->keys.push_back(tokenVal);
clearExpect(OBJ_NAME);
setExpect(COLON);
} else {
UniValue tmpVal(VSTR, tokenVal);
if (!stack.size()) {
*this = tmpVal;
break;
}
UniValue *top = stack.back();
top->values.push_back(tmpVal);
}
setExpect(NOT_VALUE);
break;
}
default:
goto return_fail;
}
} while (!stack.empty ());
/* Check that nothing follows the initial construct (parsed above). */
tok = getJsonToken(tokenVal, consumed, raw, end);
if (tok != JTOK_NONE)
goto return_fail;
return true;
return_fail:
clear();
return false;
}
diff --git a/src/univalue/pc/libunivalue-uninstalled.pc.in b/src/univalue/pc/libunivalue-uninstalled.pc.in
deleted file mode 100644
index b7f53e875..000000000
--- a/src/univalue/pc/libunivalue-uninstalled.pc.in
+++ /dev/null
@@ -1,9 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libunivalue
-Description: libunivalue, C++ universal value object and JSON library
-Version: @VERSION@
-Libs: ${pc_top_builddir}/${pcfiledir}/libunivalue.la
diff --git a/src/univalue/pc/libunivalue.pc.in b/src/univalue/pc/libunivalue.pc.in
deleted file mode 100644
index 358a2d5f7..000000000
--- a/src/univalue/pc/libunivalue.pc.in
+++ /dev/null
@@ -1,10 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libunivalue
-Description: libunivalue, C++ universal value object and JSON library
-Version: @VERSION@
-Libs: -L${libdir} -lunivalue
-Cflags: -I${includedir}
diff --git a/src/univalue/sources.mk b/src/univalue/sources.mk
index efab6d277..e15621637 100644
--- a/src/univalue/sources.mk
+++ b/src/univalue/sources.mk
@@ -1,95 +1,93 @@
# - All variables are namespaced with UNIVALUE_ to avoid colliding with
# downstream makefiles.
# - All Variables ending in _HEADERS or _SOURCES confuse automake, so the
# _INT postfix is applied.
# - Convenience variables, for example a UNIVALUE_TEST_DIR should not be used
# as they interfere with automatic dependency generation
# - The %reldir% is the relative path from the Makefile.am. This allows
# downstreams to use these variables without having to manually account for
# the path change.
UNIVALUE_INCLUDE_DIR_INT = %reldir%/include
UNIVALUE_DIST_HEADERS_INT =
UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue.h
-
-UNIVALUE_LIB_HEADERS_INT =
-UNIVALUE_LIB_HEADERS_INT += %reldir%/lib/univalue_utffilter.h
-UNIVALUE_LIB_HEADERS_INT += %reldir%/lib/univalue_escapes.h
+UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue_utffilter.h
+UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue_escapes.h
UNIVALUE_LIB_SOURCES_INT =
UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue.cpp
UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_get.cpp
UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_read.cpp
UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_write.cpp
UNIVALUE_TEST_DATA_DIR_INT = %reldir%/test
UNIVALUE_TEST_UNITESTER_INT =
UNIVALUE_TEST_UNITESTER_INT += %reldir%/test/unitester.cpp
UNIVALUE_TEST_JSON_INT =
UNIVALUE_TEST_JSON_INT += %reldir%/test/test_json.cpp
UNIVALUE_TEST_NO_NUL_INT =
UNIVALUE_TEST_NO_NUL_INT += %reldir%/test/no_nul.cpp
UNIVALUE_TEST_OBJECT_INT =
UNIVALUE_TEST_OBJECT_INT += %reldir%/test/object.cpp
UNIVALUE_TEST_FILES_INT =
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail1.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail2.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail3.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail4.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail5.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail6.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail7.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail8.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail9.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail10.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail11.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail12.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail13.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail14.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail15.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail16.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail17.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail18.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail19.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail20.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail21.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail22.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail23.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail24.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail25.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail26.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail27.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail28.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail29.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail30.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail31.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail32.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail33.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail34.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail35.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail36.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail37.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail38.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail39.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail40.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail41.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail42.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail44.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/fail45.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/pass1.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/pass2.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/pass3.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/pass4.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round1.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round2.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round3.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round4.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round5.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round6.json
UNIVALUE_TEST_FILES_INT += %reldir%/test/round7.json
diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp
index 81b1c5d3b..94c149b39 100644
--- a/src/univalue/test/unitester.cpp
+++ b/src/univalue/test/unitester.cpp
@@ -1,170 +1,162 @@
// Copyright 2014 BitPay Inc.
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or https://opensource.org/licenses/mit-license.php.
#include <univalue.h>
#include <cassert>
#include <cstdio>
#include <string>
#ifndef JSON_TEST_SRC
#error JSON_TEST_SRC must point to test source directory
#endif
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-#endif
-
std::string srcdir(JSON_TEST_SRC);
-static bool test_failed = false;
-
-#define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } }
-#define f_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", __func__); } }
static std::string rtrim(std::string s)
{
s.erase(s.find_last_not_of(" \n\r\t")+1);
return s;
}
static void runtest(std::string filename, const std::string& jdata)
{
std::string prefix = filename.substr(0, 4);
bool wantPass = (prefix == "pass") || (prefix == "roun");
bool wantFail = (prefix == "fail");
bool wantRoundTrip = (prefix == "roun");
assert(wantPass || wantFail);
UniValue val;
bool testResult = val.read(jdata);
if (wantPass) {
- d_assert(testResult == true);
+ assert(testResult == true);
} else {
- d_assert(testResult == false);
+ assert(testResult == false);
}
if (wantRoundTrip) {
std::string odata = val.write(0, 0);
assert(odata == rtrim(jdata));
}
}
static void runtest_file(const char *filename_)
{
std::string basename(filename_);
std::string filename = srcdir + "/" + basename;
FILE *f = fopen(filename.c_str(), "r");
assert(f != nullptr);
std::string jdata;
char buf[4096];
while (!feof(f)) {
int bread = fread(buf, 1, sizeof(buf), f);
assert(!ferror(f));
std::string s(buf, bread);
jdata += s;
}
assert(!ferror(f));
fclose(f);
runtest(basename, jdata);
}
static const char *filenames[] = {
"fail10.json",
"fail11.json",
"fail12.json",
"fail13.json",
"fail14.json",
"fail15.json",
"fail16.json",
"fail17.json",
//"fail18.json", // investigate
"fail19.json",
"fail1.json",
"fail20.json",
"fail21.json",
"fail22.json",
"fail23.json",
"fail24.json",
"fail25.json",
"fail26.json",
"fail27.json",
"fail28.json",
"fail29.json",
"fail2.json",
"fail30.json",
"fail31.json",
"fail32.json",
"fail33.json",
"fail34.json",
"fail35.json",
"fail36.json",
"fail37.json",
"fail38.json", // invalid unicode: only first half of surrogate pair
"fail39.json", // invalid unicode: only second half of surrogate pair
"fail40.json", // invalid unicode: broken UTF-8
"fail41.json", // invalid unicode: unfinished UTF-8
"fail42.json", // valid json with garbage following a nul byte
"fail44.json", // unterminated string
"fail45.json", // nested beyond max depth
"fail3.json",
"fail4.json", // extra comma
"fail5.json",
"fail6.json",
"fail7.json",
"fail8.json",
"fail9.json", // extra comma
"pass1.json",
"pass2.json",
"pass3.json",
"pass4.json",
"round1.json", // round-trip test
"round2.json", // unicode
"round3.json", // bare string
"round4.json", // bare number
"round5.json", // bare true
"round6.json", // bare false
"round7.json", // bare null
};
// Test \u handling
void unescape_unicode_test()
{
UniValue val;
bool testResult;
// Escaped ASCII (quote)
testResult = val.read("[\"\\u0022\"]");
- f_assert(testResult);
- f_assert(val[0].get_str() == "\"");
+ assert(testResult);
+ assert(val[0].get_str() == "\"");
// Escaped Basic Plane character, two-byte UTF-8
testResult = val.read("[\"\\u0191\"]");
- f_assert(testResult);
- f_assert(val[0].get_str() == "\xc6\x91");
+ assert(testResult);
+ assert(val[0].get_str() == "\xc6\x91");
// Escaped Basic Plane character, three-byte UTF-8
testResult = val.read("[\"\\u2191\"]");
- f_assert(testResult);
- f_assert(val[0].get_str() == "\xe2\x86\x91");
+ assert(testResult);
+ assert(val[0].get_str() == "\xe2\x86\x91");
// Escaped Supplementary Plane character U+1d161
testResult = val.read("[\"\\ud834\\udd61\"]");
- f_assert(testResult);
- f_assert(val[0].get_str() == "\xf0\x9d\x85\xa1");
+ assert(testResult);
+ assert(val[0].get_str() == "\xf0\x9d\x85\xa1");
}
int main (int argc, char *argv[])
{
- for (unsigned int fidx = 0; fidx < ARRAY_SIZE(filenames); fidx++) {
- runtest_file(filenames[fidx]);
+ for (const auto& f: filenames) {
+ runtest_file(f);
}
unescape_unicode_test();
- return test_failed ? 1 : 0;
+ return 0;
}
diff --git a/test/lint/README.md b/test/lint/README.md
index dfb6e0f8b..b8f338189 100644
--- a/test/lint/README.md
+++ b/test/lint/README.md
@@ -1,21 +1,20 @@
This folder contains lint scripts.
check-doc.py
============
Check for missing documentation of command line options.
git-subtree-check.sh
====================
Run this script from the root of the repository to verify that a subtree matches the contents of
the commit it claims to have been updated to.
To use, make sure that you have fetched the upstream repository branch in which the subtree is
maintained:
* for `src/secp256k1`: https://github.com/bitcoin-core/secp256k1.git (branch master)
* for `src/leveldb`: https://github.com/bitcoin-core/leveldb.git (branch bitcoin-fork)
-* for `src/univalue`: https://github.com/bitcoin-core/univalue.git (branch master)
* for `src/crypto/ctaes`: https://github.com/bitcoin-core/ctaes.git (branch master)
Usage: `git-subtree-check.sh DIR (COMMIT)`
`COMMIT` may be omitted, in which case `HEAD` is used.
diff --git a/test/lint/lint-files.py b/test/lint/lint-files.py
index 84b7d9122..25aa2bb74 100755
--- a/test/lint/lint-files.py
+++ b/test/lint/lint-files.py
@@ -1,264 +1,264 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
This checks that all files in the repository have correct filenames and permissions
"""
import os
import re
import sys
from subprocess import check_output
from typing import Dict, NoReturn, Optional
CMD_TOP_LEVEL = ["git", "rev-parse", "--show-toplevel"]
CMD_ALL_FILES = ["git", "ls-files", "-z", "--full-name", "--stage"]
CMD_SHEBANG_FILES = ["git", "grep", "--full-name", "--line-number", "-I", "^#!"]
ALL_SOURCE_FILENAMES_REGEXP = r"^.*\.(cpp|h|py|sh|rs)$"
ALLOWED_FILENAME_REGEXP = "^[a-zA-Z0-9/_.@][a-zA-Z0-9/_.@-]*$"
ALLOWED_SOURCE_FILENAME_REGEXP = "^[a-z0-9_./-]+$"
ALLOWED_PERMISSION_NON_EXECUTABLES = 0o644
ALLOWED_PERMISSION_EXECUTABLES = 0o755
ALLOWED_EXECUTABLE_SHEBANG = {
"py": [b"#!/usr/bin/env python3"],
"sh": [b"#!/usr/bin/env bash", b"#!/bin/sh"],
}
# JS files are treated as common files rather than source files, as they frequently
# use mixedCase in their names.
ALLOWED_FILENAME_EXCEPTION = "web/e.cash/pages/blog/[slug].js"
ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXPS = (
- "^src/(secp256k1/|univalue/|test/fuzz/FuzzedDataProvider.h)",
+ "^src/(secp256k1/|test/fuzz/FuzzedDataProvider.h)",
"^cmake/utils/EventCheckVersion.cpp$",
"^contrib/buildbot/test/test_endpoint_",
)
# Executable files that may not have a standard shebang
ALLOWED_PERMISSION_EXECUTABLES_EXCEPTIONS = (
# Windows PowerShell script
"contrib/teamcity/run-tests-windows.ps1",
)
class FileMeta(object):
def __init__(self, file_spec: str):
"""Parse a `git ls files --stage` output line."""
# 100755 5a150d5f8031fcd75e80a4dd9843afa33655f579 0 ci/test/00_setup_env.sh
meta_str, self.file_path = file_spec.split("\t", 2)
meta = meta_str.split()
# The octal file permission of the file. Internally, git only
# keeps an 'executable' bit, so this will always be 0o644 or 0o755.
self.permissions = int(meta[0], 8) & 0o7777
# We don't currently care about the other fields
@property
def extension(self) -> Optional[str]:
"""
Returns the file extension for a given filename string.
eg:
'ci/lint_run_all.sh' -> 'sh'
'ci/retry/retry' -> None
'contrib/devtools/split-debug.sh.in' -> 'in'
"""
return str(os.path.splitext(self.file_path)[1].strip(".") or None)
@property
def full_extension(self) -> Optional[str]:
"""
Returns the full file extension for a given filename string.
eg:
'ci/lint_run_all.sh' -> 'sh'
'ci/retry/retry' -> None
'contrib/devtools/split-debug.sh.in' -> 'sh.in'
"""
filename_parts = self.file_path.split(os.extsep, 1)
return filename_parts[1] if len(filename_parts) >= 2 else None
def get_git_file_metadata() -> Dict[str, FileMeta]:
"""
Return a dictionary mapping the name of all files in the repository to git tree
metadata.
"""
files_raw = check_output(CMD_ALL_FILES).decode("utf8").rstrip("\0").split("\0")
files = {}
for file_spec in files_raw:
meta = FileMeta(file_spec)
files[meta.file_path] = meta
return files
def check_all_filenames(files) -> int:
"""
Checks every file in the repository against an allowed regexp to make sure only
lowercase or uppercase alphanumerics (a-zA-Z0-9), underscores (_), hyphens (-),
at (@) and dots (.) are used in repository filenames.
"""
filenames = files.keys()
filename_regex = re.compile(ALLOWED_FILENAME_REGEXP)
failed_tests = 0
for filename in filenames:
if (
not filename_regex.match(filename)
and filename != ALLOWED_FILENAME_EXCEPTION
):
print(
f'File "{filename!r}" does not not match the allowed filename regexp '
f"('{ALLOWED_FILENAME_REGEXP}')."
)
failed_tests += 1
return failed_tests
def check_source_filenames(files) -> int:
"""
Checks only source files (*.cpp, *.h, *.py, *.sh, *.rs) against a stricter allowed
regexp to make sure only lowercase alphanumerics (a-z0-9), underscores (_),
hyphens (-) and dots (.) are used in source code filenames.
Additionally there is an exception regexp for directories or files which are
excepted from matching this regexp.
*.js files are not tested here, as they frequently use mixedCase names
"""
filenames = [
filename
for filename in files.keys()
if re.match(ALL_SOURCE_FILENAMES_REGEXP, filename, re.IGNORECASE)
]
filename_regex = re.compile(ALLOWED_SOURCE_FILENAME_REGEXP)
failed_tests = 0
for filename in filenames:
def filename_matches_exception(exception_regex: str) -> Optional[re.Match[str]]:
filename_exception_regex = re.compile(exception_regex)
return filename_exception_regex.match(filename)
if not filename_regex.match(filename) and not any(
filename_matches_exception(regex)
for regex in ALLOWED_SOURCE_FILENAME_EXCEPTION_REGEXPS
):
print(
f'File "{filename!r}" does not not match the allowed source filename '
f"regex ('{ALLOWED_SOURCE_FILENAME_REGEXP}') or the exception regex"
)
failed_tests += 1
return failed_tests
def check_all_file_permissions(files) -> int:
"""
Checks all files in the repository match an allowed executable or non-executable
file permission octal.
Additionally checks that for executable files, the file contains a shebang line
"""
failed_tests = 0
for filename, file_meta in files.items():
if file_meta.permissions == ALLOWED_PERMISSION_EXECUTABLES:
with open(filename, "rb") as f:
shebang = f.readline().rstrip(b"\n")
# For any file with executable permissions the first line must contain a shebang
if (
not shebang.startswith(b"#!")
and filename not in ALLOWED_PERMISSION_EXECUTABLES_EXCEPTIONS
):
print(
f'File "{filename}" has permission {ALLOWED_PERMISSION_EXECUTABLES:03o}'
f"(executable) and is thus expected to contain a shebang '#!'. Add "
f'shebang or do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES:03o} '
f'{filename}" to make it non-executable.'
)
failed_tests += 1
# For certain file extensions that have been defined, we also check that
# the shebang conforms to a specific allowable set of shebangs
if (
file_meta.extension in ALLOWED_EXECUTABLE_SHEBANG.keys()
and filename not in ALLOWED_PERMISSION_EXECUTABLES_EXCEPTIONS
):
if shebang not in ALLOWED_EXECUTABLE_SHEBANG[file_meta.extension]:
print(
f'File "{filename}" is missing expected shebang '
+ " or ".join(
[
x.decode("utf-8")
for x in ALLOWED_EXECUTABLE_SHEBANG[file_meta.extension]
]
)
)
failed_tests += 1
elif file_meta.permissions == ALLOWED_PERMISSION_NON_EXECUTABLES:
continue
else:
print(
f'File "{filename}" has unexpected permission {file_meta.permissions}.'
f' Do "chmod {ALLOWED_PERMISSION_NON_EXECUTABLES:03o} {filename}" (if '
f'non-executable) or "chmod {ALLOWED_PERMISSION_EXECUTABLES:03o} '
f'{filename}" (if executable).'
)
failed_tests += 1
return failed_tests
def check_shebang_file_permissions(files_meta) -> int:
"""
Checks every file that contains a shebang line to ensure it has an executable
permission
"""
filenames = check_output(CMD_SHEBANG_FILES).decode("utf8").strip().split("\n")
# The git grep command we use returns files which contain a shebang on any line
# within the file so we need to filter the list to only files with the shebang on
# the first line
filenames = [
filename.split(":1:")[0] for filename in filenames if ":1:" in filename
]
failed_tests = 0
for filename in filenames:
file_meta = files_meta[filename]
if file_meta.permissions != ALLOWED_PERMISSION_EXECUTABLES:
# These file types are typically expected to be sourced and not executed
# directly
if file_meta.full_extension in ["bash", "init", "openrc", "sh.in"]:
continue
print(
f'File "{filename}" contains a shebang line, but has the file '
f"permission {file_meta.permissions} instead of the expected "
f"executable permission {ALLOWED_PERMISSION_EXECUTABLES:03o}. Do "
f'"chmod {ALLOWED_PERMISSION_EXECUTABLES:03o} {filename}" (or remove '
f"the shebang line)."
)
failed_tests += 1
return failed_tests
def main() -> NoReturn:
root_dir = check_output(CMD_TOP_LEVEL).decode("utf8").strip()
os.chdir(root_dir)
files = get_git_file_metadata()
failed_tests = 0
failed_tests += check_all_filenames(files)
failed_tests += check_source_filenames(files)
failed_tests += check_all_file_permissions(files)
failed_tests += check_shebang_file_permissions(files)
if failed_tests:
# Specific errors are printed on stdout in the various functions above
sys.exit(1)
else:
# There should be no stdout in case of success
sys.exit(0)
if __name__ == "__main__":
main()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, May 21, 22:39 (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5866077
Default Alt Text
(169 KB)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment