aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/devtools/previous_release.py217
-rwxr-xr-xcontrib/devtools/previous_release.sh152
-rwxr-xr-xcontrib/devtools/security-check.py104
-rwxr-xr-xcontrib/devtools/test-security-check.py19
-rwxr-xr-xcontrib/devtools/test_deterministic_coverage.sh1
-rwxr-xr-xcontrib/gitian-build.py2
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml4
-rw-r--r--contrib/macdeploy/README.md65
-rwxr-xr-xcontrib/macdeploy/gen-sdk94
-rw-r--r--contrib/testgen/README.md4
-rw-r--r--contrib/testgen/base58.py2
11 files changed, 446 insertions, 218 deletions
diff --git a/contrib/devtools/previous_release.py b/contrib/devtools/previous_release.py
new file mode 100755
index 0000000000..5599051cf3
--- /dev/null
+++ b/contrib/devtools/previous_release.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2018-2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Download or build previous releases.
+# Needs curl and tar to download a release, or the build dependencies when
+# building a release.
+
+import argparse
+import contextlib
+from fnmatch import fnmatch
+import os
+from pathlib import Path
+import re
+import shutil
+import subprocess
+import sys
+import hashlib
+
+
+@contextlib.contextmanager
+def pushd(new_dir) -> None:
+ previous_dir = os.getcwd()
+ os.chdir(new_dir)
+ try:
+ yield
+ finally:
+ os.chdir(previous_dir)
+
+
+def download_binary(tag, args) -> int:
+ if Path(tag).is_dir():
+ if not args.remove_dir:
+ print('Using cached {}'.format(tag))
+ return 0
+ shutil.rmtree(tag)
+ Path(tag).mkdir()
+ bin_path = 'bin/bitcoin-core-{}'.format(tag[1:])
+ match = re.compile('v(.*)(rc[0-9]+)$').search(tag)
+ if match:
+ bin_path = 'bin/bitcoin-core-{}/test.{}'.format(
+ match.group(1), match.group(2))
+ tarball = 'bitcoin-{tag}-{platform}.tar.gz'.format(
+ tag=tag[1:], platform=args.platform)
+ sha256Sums = "SHA256SUMS-{tag}.asc".format(tag=tag[1:])
+ tarballUrl = 'https://bitcoincore.org/{bin_path}/{tarball}'.format(
+ bin_path=bin_path, tarball=tarball)
+ sha256SumsUrl = 'https://bitcoincore.org/{bin_path}/SHA256SUMS.asc'.format(
+ bin_path=bin_path)
+
+ print('Fetching: {tarballUrl}'.format(tarballUrl=tarballUrl))
+ print('Fetching: {sha256SumsUrl}'.format(sha256SumsUrl=sha256SumsUrl))
+
+ header, status = subprocess.Popen(
+ ['curl', '--head', tarballUrl], stdout=subprocess.PIPE).communicate()
+ if re.search("404 Not Found", header.decode("utf-8")):
+ print("Binary tag was not found")
+ return 1
+
+ curlCmds = [
+ ['curl', '--remote-name', tarballUrl],
+ ['curl', '--output', sha256Sums, sha256SumsUrl],
+ ]
+
+ for cmd in curlCmds:
+ ret = subprocess.run(cmd).returncode
+ if ret:
+ return ret
+
+ hasher = hashlib.sha256()
+ with open(tarball, "rb") as afile:
+ hasher.update(afile.read())
+ tarballHash = hasher.hexdigest()
+ tarballHash = '{} {}\n'.format(tarballHash, tarball)
+ with open(sha256Sums, 'r', encoding="utf-8") as afile:
+ shasums = afile.readlines()
+
+ if tarballHash not in shasums:
+ print("Checksum did not match")
+ Path(tarball).unlink()
+ return 1
+ print("Checksum matched")
+
+ # Bitcoin Core Release Signing Keys v0.11.0+
+ signingKey = "01EA5486DE18A882D4C2684590C8019E36C2E964"
+
+ isKeyPresent = subprocess.run(
+ ["gpg", "--list-keys", signingKey]).returncode
+ if isKeyPresent:
+ return isKeyPresent
+
+ isVerified = subprocess.run(
+ ["gpg", "--verify", sha256Sums]).returncode
+ if isVerified:
+ return isVerified
+
+ # Extract tarball
+ ret = subprocess.run(['tar', '-zxf', tarball, '-C', tag,
+ '--strip-components=1',
+ 'bitcoin-{tag}'.format(tag=tag[1:])]).returncode
+ if ret:
+ return ret
+
+ Path(tarball).unlink()
+ Path(sha256Sums).unlink()
+ return 0
+
+
+def build_release(tag, args) -> int:
+ githubUrl = "https://github.com/bitcoin/bitcoin"
+ if args.remove_dir:
+ if Path(tag).is_dir():
+ shutil.rmtree(tag)
+ if not Path(tag).is_dir():
+ # fetch new tags
+ subprocess.run(
+ ["git", "fetch", githubUrl, "--tags"])
+ output = subprocess.check_output(['git', 'tag', '-l', tag])
+ if not output:
+ print('Tag {} not found'.format(tag))
+ return 1
+ ret = subprocess.run([
+ 'git', 'clone', githubUrl, tag
+ ]).returncode
+ if ret:
+ return ret
+ with pushd(tag):
+ ret = subprocess.run(['git', 'checkout', tag]).returncode
+ if ret:
+ return ret
+ host = args.host
+ if args.depends:
+ with pushd('depends'):
+ ret = subprocess.run(['make', 'NO_QT=1']).returncode
+ if ret:
+ return ret
+ host = os.environ.get(
+ 'HOST', subprocess.check_output(['./config.guess']))
+ config_flags = '--prefix={pwd}/depends/{host} '.format(
+ pwd=os.getcwd(),
+ host=host) + args.config_flags
+ cmds = [
+ './autogen.sh',
+ './configure {}'.format(config_flags),
+ 'make',
+ ]
+ for cmd in cmds:
+ ret = subprocess.run(cmd.split()).returncode
+ if ret:
+ return ret
+ # Move binaries, so they're in the same place as in the
+ # release download
+ Path('bin').mkdir(exist_ok=True)
+ files = ['bitcoind', 'bitcoin-cli', 'bitcoin-tx']
+ for f in files:
+ Path('src/'+f).rename('bin/'+f)
+ return 0
+
+
+def check_host(args) -> int:
+ args.host = os.environ.get('HOST', subprocess.check_output(
+ './depends/config.guess').decode())
+ if args.download_binary:
+ platforms = {
+ 'x86_64-*-linux*': 'x86_64-linux-gnu',
+ 'x86_64-apple-darwin*': 'osx64',
+ }
+ args.platform = ''
+ for pattern, target in platforms.items():
+ if fnmatch(args.host, pattern):
+ args.platform = target
+ if not args.platform:
+ print('Not sure which binary to download for {}'.format(args.host))
+ return 1
+ return 0
+
+
+def main(args) -> int:
+ Path(args.target_dir).mkdir(exist_ok=True, parents=True)
+ print("Releases directory: {}".format(args.target_dir))
+ ret = check_host(args)
+ if ret:
+ return ret
+ if args.download_binary:
+ with pushd(args.target_dir):
+ for tag in args.tags:
+ ret = download_binary(tag, args)
+ if ret:
+ return ret
+ return 0
+ args.config_flags = os.environ.get('CONFIG_FLAGS', '')
+ args.config_flags += ' --without-gui --disable-tests --disable-bench'
+ with pushd(args.target_dir):
+ for tag in args.tags:
+ ret = build_release(tag, args)
+ if ret:
+ return ret
+ return 0
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('-r', '--remove-dir', action='store_true',
+ help='remove existing directory.')
+ parser.add_argument('-d', '--depends', action='store_true',
+ help='use depends.')
+ parser.add_argument('-b', '--download-binary', action='store_true',
+ help='download release binary.')
+ parser.add_argument('-t', '--target-dir', action='store',
+ help='target directory.', default='releases')
+ parser.add_argument('tags', nargs='+',
+ help="release tags. e.g.: v0.18.1 v0.20.0rc2")
+ args = parser.parse_args()
+ sys.exit(main(args))
diff --git a/contrib/devtools/previous_release.sh b/contrib/devtools/previous_release.sh
deleted file mode 100755
index d375291f47..0000000000
--- a/contrib/devtools/previous_release.sh
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2018-2020 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#
-# Build previous releases.
-
-export LC_ALL=C
-
-CONFIG_FLAGS=""
-FUNCTIONAL_TESTS=0
-DELETE_EXISTING=0
-USE_DEPENDS=0
-DOWNLOAD_BINARY=0
-CONFIG_FLAGS=""
-TARGET="releases"
-
-while getopts ":hfrdbt:" opt; do
- case $opt in
- h)
- echo "Usage: .previous_release.sh [options] tag1 tag2"
- echo " options:"
- echo " -h Print this message"
- echo " -f Configure for functional tests"
- echo " -r Remove existing directory"
- echo " -d Use depends"
- echo " -b Download release binary"
- echo " -t Target directory (default: releases)"
- exit 0
- ;;
- f)
- FUNCTIONAL_TESTS=1
- CONFIG_FLAGS="$CONFIG_FLAGS --without-gui --disable-tests --disable-bench"
- ;;
- r)
- DELETE_EXISTING=1
- ;;
- d)
- USE_DEPENDS=1
- ;;
- b)
- DOWNLOAD_BINARY=1
- ;;
- t)
- TARGET=$OPTARG
- ;;
- \?)
- echo "Invalid option: -$OPTARG" >&2
- exit 1
- ;;
- esac
-done
-
-shift $((OPTIND-1))
-
-if [ -z "$1" ]; then
- echo "Specify release tag(s), e.g.: .previous_release v0.15.1"
- exit 1
-fi
-
-if [ ! -d "$TARGET" ]; then
- mkdir -p $TARGET
-fi
-
-if [ "$DOWNLOAD_BINARY" -eq "1" ]; then
- HOST="${HOST:-$(./depends/config.guess)}"
- case "$HOST" in
- x86_64-*-linux*)
- PLATFORM=x86_64-linux-gnu
- ;;
- x86_64-apple-darwin*)
- PLATFORM=osx64
- ;;
- *)
- echo "Not sure which binary to download for $HOST."
- exit 1
- ;;
- esac
-fi
-
-echo "Releases directory: $TARGET"
-pushd "$TARGET" || exit 1
-{
- for tag in "$@"
- do
- if [ "$DELETE_EXISTING" -eq "1" ]; then
- if [ -d "$tag" ]; then
- rm -r "$tag"
- fi
- fi
-
- if [ "$DOWNLOAD_BINARY" -eq "0" ]; then
-
- if [ ! -d "$tag" ]; then
- if [ -z $(git tag -l "$tag") ]; then
- echo "Tag $tag not found"
- exit 1
- fi
-
- git clone https://github.com/bitcoin/bitcoin "$tag"
- pushd "$tag" || exit 1
- {
- git checkout "$tag"
- if [ "$USE_DEPENDS" -eq "1" ]; then
- pushd depends || exit 1
- {
- if [ "$FUNCTIONAL_TESTS" -eq "1" ]; then
- make NO_QT=1
- else
- make
- fi
- HOST="${HOST:-$(./config.guess)}"
- }
- popd || exit 1
- CONFIG_FLAGS="--prefix=$PWD/depends/$HOST $CONFIG_FLAGS"
- fi
- ./autogen.sh
- ./configure $CONFIG_FLAGS
- make
- # Move binaries, so they're in the same place as in the release download:
- mkdir bin
- mv src/bitcoind src/bitcoin-cli src/bitcoin-tx bin
- if [ "$FUNCTIONAL_TESTS" -eq "0" ]; then
- mv src/qt/bitcoin-qt bin
- fi
- }
- popd || exit 1
- fi
- else
- if [ -d "$tag" ]; then
- echo "Using cached $tag"
- else
- mkdir "$tag"
- if [[ "$tag" =~ v(.*)(rc[0-9]+)$ ]]; then
- BIN_PATH="bin/bitcoin-core-${BASH_REMATCH[1]}/test.${BASH_REMATCH[2]}"
- else
- BIN_PATH="bin/bitcoin-core-${tag:1}"
- fi
- URL="https://bitcoincore.org/$BIN_PATH/bitcoin-${tag:1}-$PLATFORM.tar.gz"
- echo "Fetching: $URL"
- if ! curl -O -f $URL; then
- echo "Download failed."
- exit 1
- fi
- tar -zxf "bitcoin-${tag:1}-$PLATFORM.tar.gz" -C "$tag" --strip-components=1 "bitcoin-${tag:1}"
- rm "bitcoin-${tag:1}-$PLATFORM.tar.gz"
- fi
- fi
- done
-}
-popd || exit 1
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index ca587ca9e5..dc74de9198 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -40,25 +40,48 @@ def get_ELF_program_headers(executable):
stdout = run_command([READELF_CMD, '-l', '-W', executable])
in_headers = False
- count = 0
headers = []
for line in stdout.splitlines():
if line.startswith('Program Headers:'):
in_headers = True
+ count = 0
if line == '':
in_headers = False
if in_headers:
if count == 1: # header line
- ofs_typ = line.find('Type')
- ofs_offset = line.find('Offset')
- ofs_flags = line.find('Flg')
- ofs_align = line.find('Align')
- if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1:
+ header = [x.strip() for x in line.split()]
+ ofs_typ = header.index('Type')
+ ofs_flags = header.index('Flg')
+ # assert readelf output is what we expect
+ if ofs_typ == -1 or ofs_flags == -1:
raise ValueError('Cannot parse elfread -lW output')
elif count > 1:
- typ = line[ofs_typ:ofs_offset].rstrip()
- flags = line[ofs_flags:ofs_align].rstrip()
- headers.append((typ, flags))
+ splitline = [x.strip() for x in line.split()]
+ typ = splitline[ofs_typ]
+ if not typ.startswith('[R'): # skip [Requesting ...]
+ splitline = [x.strip() for x in line.split()]
+ flags = splitline[ofs_flags]
+ # check for 'R', ' E'
+ if splitline[ofs_flags + 1] is 'E':
+ flags += ' E'
+ headers.append((typ, flags, []))
+ count += 1
+
+ if line.startswith(' Section to Segment mapping:'):
+ in_mapping = True
+ count = 0
+ if line == '':
+ in_mapping = False
+ if in_mapping:
+ if count == 1: # header line
+ ofs_segment = line.find('Segment')
+ ofs_sections = line.find('Sections...')
+ if ofs_segment == -1 or ofs_sections == -1:
+ raise ValueError('Cannot parse elfread -lW output')
+ elif count > 1:
+ segment = int(line[ofs_segment:ofs_sections].strip())
+ sections = line[ofs_sections:].strip().split()
+ headers[segment][2].extend(sections)
count += 1
return headers
@@ -68,7 +91,7 @@ def check_ELF_NX(executable) -> bool:
'''
have_wx = False
have_gnu_stack = False
- for (typ, flags) in get_ELF_program_headers(executable):
+ for (typ, flags, _) in get_ELF_program_headers(executable):
if typ == 'GNU_STACK':
have_gnu_stack = True
if 'W' in flags and 'E' in flags: # section is both writable and executable
@@ -82,7 +105,7 @@ def check_ELF_RELRO(executable) -> bool:
Dynamic section must have BIND_NOW flag
'''
have_gnu_relro = False
- for (typ, flags) in get_ELF_program_headers(executable):
+ for (typ, flags, _) in get_ELF_program_headers(executable):
# Note: not checking flags == 'R': here as linkers set the permission differently
# This does not affect security: the permission flags of the GNU_RELRO program
# header are ignored, the PT_LOAD header determines the effective permissions.
@@ -113,6 +136,62 @@ def check_ELF_Canary(executable) -> bool:
ok = True
return ok
+def check_ELF_separate_code(executable):
+ '''
+ Check that sections are appropriately separated in virtual memory,
+ based on their permissions. This checks for missing -Wl,-z,separate-code
+ and potentially other problems.
+ '''
+ EXPECTED_FLAGS = {
+ # Read + execute
+ '.init': 'R E',
+ '.plt': 'R E',
+ '.plt.got': 'R E',
+ '.plt.sec': 'R E',
+ '.text': 'R E',
+ '.fini': 'R E',
+ # Read-only data
+ '.interp': 'R',
+ '.note.gnu.property': 'R',
+ '.note.gnu.build-id': 'R',
+ '.note.ABI-tag': 'R',
+ '.gnu.hash': 'R',
+ '.dynsym': 'R',
+ '.dynstr': 'R',
+ '.gnu.version': 'R',
+ '.gnu.version_r': 'R',
+ '.rela.dyn': 'R',
+ '.rela.plt': 'R',
+ '.rodata': 'R',
+ '.eh_frame_hdr': 'R',
+ '.eh_frame': 'R',
+ '.qtmetadata': 'R',
+ '.gcc_except_table': 'R',
+ '.stapsdt.base': 'R',
+ # Writable data
+ '.init_array': 'RW',
+ '.fini_array': 'RW',
+ '.dynamic': 'RW',
+ '.got': 'RW',
+ '.data': 'RW',
+ '.bss': 'RW',
+ }
+ # For all LOAD program headers get mapping to the list of sections,
+ # and for each section, remember the flags of the associated program header.
+ flags_per_section = {}
+ for (typ, flags, sections) in get_ELF_program_headers(executable):
+ if typ == 'LOAD':
+ for section in sections:
+ assert(section not in flags_per_section)
+ flags_per_section[section] = flags
+ # Spot-check ELF LOAD program header flags per section
+ # If these sections exist, check them against the expected R/W/E flags
+ for (section, flags) in flags_per_section.items():
+ if section in EXPECTED_FLAGS:
+ if EXPECTED_FLAGS[section] != flags:
+ return False
+ return True
+
def get_PE_dll_characteristics(executable) -> int:
'''Get PE DllCharacteristics bits'''
stdout = run_command([OBJDUMP_CMD, '-x', executable])
@@ -225,7 +304,8 @@ CHECKS = {
('PIE', check_ELF_PIE),
('NX', check_ELF_NX),
('RELRO', check_ELF_RELRO),
- ('Canary', check_ELF_Canary)
+ ('Canary', check_ELF_Canary),
+ ('separate_code', check_ELF_separate_code),
],
'PE': [
('DYNAMIC_BASE', check_PE_DYNAMIC_BASE),
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index ea70b27941..ec2d886653 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -20,10 +20,9 @@ def write_testcode(filename):
''')
def call_security_check(cc, source, executable, options):
- subprocess.check_call([cc,source,'-o',executable] + options)
- p = subprocess.Popen(['./security-check.py',executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
- (stdout, stderr) = p.communicate()
- return (p.returncode, stdout.rstrip())
+ subprocess.run([cc,source,'-o',executable] + options, check=True)
+ p = subprocess.run(['./contrib/devtools/security-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
+ return (p.returncode, p.stdout.rstrip())
class TestSecurityChecks(unittest.TestCase):
def test_ELF(self):
@@ -32,15 +31,17 @@ class TestSecurityChecks(unittest.TestCase):
cc = 'gcc'
write_testcode(source)
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
(1, executable+': failed PIE NX RELRO Canary'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
(1, executable+': failed PIE RELRO Canary'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-no-pie','-fno-PIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
(1, executable+': failed PIE RELRO'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE', '-Wl,-z,separate-code']),
(1, executable+': failed RELRO'))
- self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']),
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,noseparate-code']),
+ (1, executable+': failed separate_code'))
+ self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE', '-Wl,-z,separate-code']),
(0, ''))
def test_PE(self):
diff --git a/contrib/devtools/test_deterministic_coverage.sh b/contrib/devtools/test_deterministic_coverage.sh
index 95b1553215..8501c72f04 100755
--- a/contrib/devtools/test_deterministic_coverage.sh
+++ b/contrib/devtools/test_deterministic_coverage.sh
@@ -16,7 +16,6 @@ GCOV_EXECUTABLE="gcov"
NON_DETERMINISTIC_TESTS=(
"blockfilter_index_tests/blockfilter_index_initial_sync" # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... }
"coinselector_tests/knapsack_solver_test" # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2))
- "denialofservice_tests/DoS_mapOrphans" # denialofservice_tests.cpp: it = mapOrphanTransactions.lower_bound(InsecureRand256());
"fs_tests/fsbridge_fstream" # deterministic test failure?
"miner_tests/CreateNewBlock_validity" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
"scheduler_tests/manythreads" # scheduler.cpp: CScheduler::serviceQueue()
diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py
index 4a3df93cea..d498c9e2c8 100755
--- a/contrib/gitian-build.py
+++ b/contrib/gitian-build.py
@@ -209,7 +209,7 @@ def main():
args.macos = 'm' in args.os
# Disable for MacOS if no SDK found
- if args.macos and not os.path.isfile('gitian-builder/inputs/MacOSX10.14.sdk.tar.gz'):
+ if args.macos and not os.path.isfile('gitian-builder/inputs/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz'):
print('Cannot build for MacOS, SDK does not exist. Will build for other OSes')
args.macos = False
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index bbae7201e5..e0aaafc15a 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -32,7 +32,7 @@ remotes:
- "url": "https://github.com/bitcoin/bitcoin.git"
"dir": "bitcoin"
files:
-- "MacOSX10.14.sdk.tar.gz"
+- "Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz"
script: |
set -e -o pipefail
@@ -90,7 +90,7 @@ script: |
BASEPREFIX="${PWD}/depends"
mkdir -p ${BASEPREFIX}/SDKs
- tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/MacOSX10.14.sdk.tar.gz
+ tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz
# Build dependencies for each host
for i in $HOSTS; do
diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md
index 68ebb5def1..fe677e3a1f 100644
--- a/contrib/macdeploy/README.md
+++ b/contrib/macdeploy/README.md
@@ -14,55 +14,44 @@ When complete, it will have produced `Bitcoin-Qt.dmg`.
## SDK Extraction
-Our current macOS SDK (`macOSX10.14.sdk`) can be extracted from
-[Xcode_10.2.1.xip](https://download.developer.apple.com/Developer_Tools/Xcode_10.2.1/Xcode_10.2.1.xip).
+### Step 1: Obtaining `Xcode.app`
+
+Our current macOS SDK
+(`Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz`) can be
+extracted from
+[Xcode_11.3.1.xip](https://download.developer.apple.com/Developer_Tools/Xcode_11.3.1/Xcode_11.3.1.xip).
An Apple ID is needed to download this.
-`Xcode.app` is packaged in a `.xip` archive.
-This makes the SDK less-trivial to extract on non-macOS machines.
-One approach (tested on Debian Buster) is outlined below:
+After Xcode version 7.x, Apple started shipping the `Xcode.app` in a `.xip`
+archive. This makes the SDK less-trivial to extract on non-macOS machines. One
+approach (tested on Debian Buster) is outlined below:
```bash
+# Install/clone tools needed for extracting Xcode.app
+apt install cpio
+git clone https://github.com/bitcoin-core/apple-sdk-tools.git
-apt install clang cpio git liblzma-dev libxml2-dev libssl-dev make
-
-git clone https://github.com/tpoechtrager/xar
-pushd xar/xar
-./configure
-make
-make install
-popd
-
-git clone https://github.com/NiklasRosenstein/pbzx
-pushd pbzx
-clang -llzma -lxar pbzx.c -o pbzx -Wl,-rpath=/usr/local/lib
-popd
-
-xar -xf Xcode_10.2.1.xip -C .
-
-./pbzx/pbzx -n Content | cpio -i
-
-find Xcode.app -type d -name MacOSX.sdk -exec sh -c 'tar --transform="s/MacOSX.sdk/MacOSX10.14.sdk/" -c -C$(dirname {}) MacOSX.sdk/ | gzip -9n > MacOSX10.14.sdk.tar.gz' \;
+# Unpack Xcode_11.3.1.xip and place the resulting Xcode.app in your current
+# working directory
+python3 apple-sdk-tools/extract_xcode.py -f Xcode_11.3.1.xip | cpio -d -i
```
-on macOS the process is more straightforward:
+On macOS the process is more straightforward:
```bash
-xip -x Xcode_10.2.1.xip
-tar -s "/MacOSX.sdk/MacOSX10.14.sdk/" -C Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.14.sdk.tar.gz MacOSX.sdk
+xip -x Xcode_11.3.1.xip
```
-Our previously used macOS SDK (`MacOSX10.11.sdk`) can be extracted from
-[Xcode 7.3.1 dmg](https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/Xcode_7.3.1/Xcode_7.3.1.dmg).
-The script [`extract-osx-sdk.sh`](./extract-osx-sdk.sh) automates this. First
-ensure the DMG file is in the current directory, and then run the script. You
-may wish to delete the `intermediate 5.hfs` file and `MacOSX10.11.sdk` (the
-directory) when you've confirmed the extraction succeeded.
+### Step 2: Generating `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz` from `Xcode.app`
+
+To generate `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz`, run
+the script [`gen-sdk`](./gen-sdk) with the path to `Xcode.app` (extracted in the
+previous stage) as the first argument.
```bash
-apt-get install p7zip-full sleuthkit
-contrib/macdeploy/extract-osx-sdk.sh
-rm -rf 5.hfs MacOSX10.11.sdk
+# Generate a Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz from
+# the supplied Xcode.app
+./contrib/macdeploy/gen-sdk '/path/to/Xcode.app'
```
## Deterministic macOS DMG Notes
@@ -91,13 +80,13 @@ and its `libLTO.so` rather than those from `llvmgcc`, as it was originally done
To complicate things further, all builds must target an Apple SDK. These SDKs are free to
download, but not redistributable. To obtain it, register for an Apple Developer Account,
-then download [Xcode 10.2.1](https://download.developer.apple.com/Developer_Tools/Xcode_10.2.1/Xcode_10.2.1.xip).
+then download [Xcode_11.3.1](https://download.developer.apple.com/Developer_Tools/Xcode_11.3.1/Xcode_11.3.1.xip).
This file is many gigabytes in size, but most (but not all) of what we need is
contained only in a single directory:
```bash
-Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk
+Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
```
See the SDK Extraction notes above for how to obtain it.
diff --git a/contrib/macdeploy/gen-sdk b/contrib/macdeploy/gen-sdk
new file mode 100755
index 0000000000..457d8f5e64
--- /dev/null
+++ b/contrib/macdeploy/gen-sdk
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+import argparse
+import plistlib
+import pathlib
+import sys
+import tarfile
+import gzip
+import os
+import contextlib
+
+@contextlib.contextmanager
+def cd(path):
+ """Context manager that restores PWD even if an exception was raised."""
+ old_pwd = os.getcwd()
+ os.chdir(str(path))
+ try:
+ yield
+ finally:
+ os.chdir(old_pwd)
+
+def run():
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
+
+ parser.add_argument('xcode_app', metavar='XCODEAPP', nargs=1)
+ parser.add_argument("-o", metavar='OUTSDKTGZ', nargs=1, dest='out_sdktgz', required=False)
+
+ args = parser.parse_args()
+
+ xcode_app = pathlib.Path(args.xcode_app[0]).resolve()
+ assert xcode_app.is_dir(), "The supplied Xcode.app path '{}' either does not exist or is not a directory".format(xcode_app)
+
+ xcode_app_plist = xcode_app.joinpath("Contents/version.plist")
+ with xcode_app_plist.open('rb') as fp:
+ pl = plistlib.load(fp)
+ xcode_version = pl['CFBundleShortVersionString']
+ xcode_build_id = pl['ProductBuildVersion']
+ print("Found Xcode (version: {xcode_version}, build id: {xcode_build_id})".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id))
+
+ sdk_dir = xcode_app.joinpath("Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
+ sdk_plist = sdk_dir.joinpath("System/Library/CoreServices/SystemVersion.plist")
+ with sdk_plist.open('rb') as fp:
+ pl = plistlib.load(fp)
+ sdk_version = pl['ProductVersion']
+ sdk_build_id = pl['ProductBuildVersion']
+ print("Found MacOSX SDK (version: {sdk_version}, build id: {sdk_build_id})".format(sdk_version=sdk_version, sdk_build_id=sdk_build_id))
+
+ out_name = "Xcode-{xcode_version}-{xcode_build_id}-extracted-SDK-with-libcxx-headers".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id)
+
+ xcode_libcxx_dir = xcode_app.joinpath("Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1")
+ assert xcode_libcxx_dir.is_dir()
+
+ if args.out_sdktgz:
+ out_sdktgz_path = pathlib.Path(args.out_sdktgz_path)
+ else:
+ # Construct our own out_sdktgz if not specified on the command line
+ out_sdktgz_path = pathlib.Path("./{}.tar.gz".format(out_name))
+
+ def tarfp_add_with_base_change(tarfp, dir_to_add, alt_base_dir):
+ """Add all files in dir_to_add to tarfp, but prepent MEMBERPREFIX to the files'
+ names
+
+ e.g. if the only file under /root/bazdir is /root/bazdir/qux, invoking:
+
+ tarfp_add_with_base_change(tarfp, "foo/bar", "/root/bazdir")
+
+ would result in the following members being added to tarfp:
+
+ foo/bar/ -> corresponding to /root/bazdir
+ foo/bar/qux -> corresponding to /root/bazdir/qux
+
+ """
+ def change_tarinfo_base(tarinfo):
+ if tarinfo.name and tarinfo.name.startswith("./"):
+ tarinfo.name = str(pathlib.Path(alt_base_dir, tarinfo.name))
+ if tarinfo.linkname and tarinfo.linkname.startswith("./"):
+ tarinfo.linkname = str(pathlib.Path(alt_base_dir, tarinfo.linkname))
+ return tarinfo
+ with cd(dir_to_add):
+ tarfp.add(".", recursive=True, filter=change_tarinfo_base)
+
+ print("Creating output .tar.gz file...")
+ with out_sdktgz_path.open("wb") as fp:
+ with gzip.GzipFile(fileobj=fp, compresslevel=9, mtime=0) as gzf:
+ with tarfile.open(mode="w", fileobj=gzf) as tarfp:
+ print("Adding MacOSX SDK {} files...".format(sdk_version))
+ tarfp_add_with_base_change(tarfp, sdk_dir, out_name)
+ print("Adding libc++ headers...")
+ tarfp_add_with_base_change(tarfp, xcode_libcxx_dir, "{}/usr/include/c++/v1".format(out_name))
+ print("Done! Find the resulting gzipped tarball at:")
+ print(out_sdktgz_path.resolve())
+
+if __name__ == '__main__':
+ run()
diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md
index 573a71a675..eaca473b40 100644
--- a/contrib/testgen/README.md
+++ b/contrib/testgen/README.md
@@ -4,5 +4,5 @@ Utilities to generate test vectors for the data-driven Bitcoin tests.
Usage:
- PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_keys_valid.json
- PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_keys_invalid.json
+ PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json
+ PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json
diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py
index da67cb2d90..c7ebac50d4 100644
--- a/contrib/testgen/base58.py
+++ b/contrib/testgen/base58.py
@@ -107,7 +107,7 @@ def get_bcaddress_version(strAddress):
if __name__ == '__main__':
# Test case (from http://gitorious.org/bitcoin/python-base58.git)
- assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') is 0
+ assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') == 0
_ohai = 'o hai'.encode('ascii')
_tmp = b58encode(_ohai)
assert _tmp == 'DYB3oMS'