aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/config.ini.in1
-rwxr-xr-xtest/functional/feature_assumevalid.py11
-rwxr-xr-xtest/functional/feature_block.py6
-rwxr-xr-xtest/functional/feature_config_args.py2
-rwxr-xr-xtest/functional/feature_notifications.py3
-rwxr-xr-xtest/functional/feature_nulldummy.py12
-rwxr-xr-xtest/functional/feature_startupnotify.py1
-rwxr-xr-xtest/functional/feature_syscall_sandbox.py34
-rwxr-xr-xtest/functional/feature_taproot.py18
-rwxr-xr-xtest/functional/feature_versionbits_warning.py3
-rwxr-xr-xtest/functional/interface_usdt_mempool.py80
-rwxr-xr-xtest/functional/interface_usdt_net.py15
-rwxr-xr-xtest/functional/interface_usdt_utxocache.py49
-rwxr-xr-xtest/functional/interface_usdt_validation.py34
-rwxr-xr-xtest/functional/mempool_accept.py6
-rwxr-xr-xtest/functional/mempool_dust.py9
-rwxr-xr-xtest/functional/p2p_segwit.py12
-rwxr-xr-xtest/functional/rpc_createmultisig.py11
-rwxr-xr-xtest/functional/rpc_psbt.py22
-rwxr-xr-xtest/functional/rpc_signer.py3
-rwxr-xr-xtest/functional/rpc_signrawtransactionwithkey.py21
-rw-r--r--test/functional/test_framework/key.py345
-rw-r--r--test/functional/test_framework/secp256k1.py346
-rwxr-xr-xtest/functional/test_framework/test_framework.py14
-rw-r--r--test/functional/test_framework/wallet.py7
-rwxr-xr-xtest/functional/test_framework/wallet_util.py23
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_blank.py14
-rwxr-xr-xtest/functional/wallet_createwallet.py12
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py9
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py9
-rwxr-xr-xtest/functional/wallet_listsinceblock.py9
-rwxr-xr-xtest/functional/wallet_send.py8
-rwxr-xr-xtest/functional/wallet_signer.py3
-rwxr-xr-xtest/fuzz/test_runner.py22
-rwxr-xr-xtest/lint/lint-assertions.py10
-rw-r--r--test/sanitizer_suppressions/ubsan1
37 files changed, 612 insertions, 574 deletions
diff --git a/test/config.ini.in b/test/config.ini.in
index 5888ef443b..af3d994c84 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -25,5 +25,4 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
@ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true
@ENABLE_EXTERNAL_SIGNER_TRUE@ENABLE_EXTERNAL_SIGNER=true
-@ENABLE_SYSCALL_SANDBOX_TRUE@ENABLE_SYSCALL_SANDBOX=true
@ENABLE_USDT_TRACEPOINTS_TRUE@ENABLE_USDT_TRACEPOINTS=true
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 36ee79dab9..613d2eab14 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -35,7 +35,6 @@ from test_framework.blocktools import (
create_block,
create_coinbase,
)
-from test_framework.key import ECKey
from test_framework.messages import (
CBlockHeader,
COutPoint,
@@ -46,9 +45,13 @@ from test_framework.messages import (
msg_headers,
)
from test_framework.p2p import P2PInterface
-from test_framework.script import (CScript, OP_TRUE)
+from test_framework.script import (
+ CScript,
+ OP_TRUE,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+from test_framework.wallet_util import generate_keypair
class BaseNode(P2PInterface):
@@ -90,9 +93,7 @@ class AssumeValidTest(BitcoinTestFramework):
self.blocks = []
# Get a pubkey for the coinbase TXO
- coinbase_key = ECKey()
- coinbase_key.generate()
- coinbase_pubkey = coinbase_key.get_pubkey().get_bytes()
+ _, coinbase_pubkey = generate_keypair()
# Create the first block with a coinbase output to our key
height = 1
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 1080e77c40..765db97445 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -14,7 +14,6 @@ from test_framework.blocktools import (
get_legacy_sigopcount_block,
MAX_BLOCK_SIGOPS,
)
-from test_framework.key import ECKey
from test_framework.messages import (
CBlock,
COIN,
@@ -55,6 +54,7 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
)
+from test_framework.wallet_util import generate_keypair
from data import invalid_txs
@@ -98,9 +98,7 @@ class FullBlockTest(BitcoinTestFramework):
self.bootstrap_p2p() # Add one p2p connection to the node
self.block_heights = {}
- self.coinbase_key = ECKey()
- self.coinbase_key.generate()
- self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes()
+ self.coinbase_key, self.coinbase_pubkey = generate_keypair()
self.tip = None
self.blocks = {}
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index e5fc4f6885..2927355bda 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -253,7 +253,7 @@ class ConfArgsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
"DNS seeding disabled",
- "Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet), -addnode is not provided and all -seednode(s) attempted\n",
+ "Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet) and neither -addnode nor -seednode are provided\n",
]):
self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1'])
assert time.time() - start < 60
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 8cb633d454..adf6c13973 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -30,9 +30,6 @@ class NotificationsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
- # The experimental syscall sandbox feature (-sandbox) is not compatible with -alertnotify,
- # -blocknotify, -walletnotify or -shutdownnotify (which all invoke execve).
- self.disable_syscall_sandbox = True
def setup_network(self):
self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHARS_DISALLOWED)
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index c95657dbbb..7b2a29bdb4 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -35,8 +35,7 @@ from test_framework.util import (
assert_raises_rpc_error,
)
from test_framework.wallet import getnewdestination
-from test_framework.key import ECKey
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
@@ -71,12 +70,9 @@ class NULLDUMMYTest(BitcoinTestFramework):
return tx_from_hex(signedtx["hex"])
def run_test(self):
- eckey = ECKey()
- eckey.generate()
- self.privkey = bytes_to_wif(eckey.get_bytes())
- self.pubkey = eckey.get_pubkey().get_bytes().hex()
- cms = self.nodes[0].createmultisig(1, [self.pubkey])
- wms = self.nodes[0].createmultisig(1, [self.pubkey], 'p2sh-segwit')
+ self.privkey, self.pubkey = generate_keypair(wif=True)
+ cms = self.nodes[0].createmultisig(1, [self.pubkey.hex()])
+ wms = self.nodes[0].createmultisig(1, [self.pubkey.hex()], 'p2sh-segwit')
self.ms_address = cms["address"]
ms_unlock_details = {"scriptPubKey": address_to_scriptpubkey(self.ms_address).hex(),
"redeemScript": cms["redeemScript"]}
diff --git a/test/functional/feature_startupnotify.py b/test/functional/feature_startupnotify.py
index 4eb6791123..a8e62c6244 100755
--- a/test/functional/feature_startupnotify.py
+++ b/test/functional/feature_startupnotify.py
@@ -15,7 +15,6 @@ FILE_NAME = "test.txt"
class StartupNotifyTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.disable_syscall_sandbox = True
def run_test(self):
tmpdir_file = self.nodes[0].datadir_path / FILE_NAME
diff --git a/test/functional/feature_syscall_sandbox.py b/test/functional/feature_syscall_sandbox.py
deleted file mode 100755
index 2200f6c2e6..0000000000
--- a/test/functional/feature_syscall_sandbox.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2021-2022 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test bitcoind aborts if a disallowed syscall is used when compiled with the syscall sandbox."""
-
-from test_framework.test_framework import BitcoinTestFramework, SkipTest
-
-
-class SyscallSandboxTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 1
-
- def skip_test_if_missing_module(self):
- if not self.is_syscall_sandbox_compiled():
- raise SkipTest("bitcoind has not been built with syscall sandbox enabled.")
- if self.disable_syscall_sandbox:
- raise SkipTest("--nosandbox passed to test runner.")
-
- def run_test(self):
- disallowed_syscall_terminated_bitcoind = False
- expected_log_entry = 'ERROR: The syscall "getgroups" (syscall number 115) is not allowed by the syscall sandbox'
- with self.nodes[0].assert_debug_log([expected_log_entry]):
- self.log.info("Invoking disallowed syscall")
- try:
- self.nodes[0].invokedisallowedsyscall()
- except ConnectionError:
- disallowed_syscall_terminated_bitcoind = True
- assert disallowed_syscall_terminated_bitcoind
- self.nodes = []
-
-
-if __name__ == "__main__":
- SyscallSandboxTest().main()
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 8be2040d91..e32319961e 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -97,14 +97,15 @@ from test_framework.util import (
assert_equal,
random_bytes,
)
+from test_framework.wallet_util import generate_keypair
from test_framework.key import (
generate_privkey,
compute_xonly_pubkey,
sign_schnorr,
tweak_add_privkey,
ECKey,
- SECP256K1
)
+from test_framework import secp256k1
from test_framework.address import (
hash160,
program_to_witness,
@@ -694,7 +695,7 @@ def spenders_taproot_active():
# Generate an invalid public key
while True:
invalid_pub = random_bytes(32)
- if not SECP256K1.is_x_coord(int.from_bytes(invalid_pub, 'big')):
+ if not secp256k1.GE.is_valid_x(int.from_bytes(invalid_pub, 'big')):
break
# Implement a test case that detects validation logic which maps invalid public keys to the
@@ -738,7 +739,11 @@ def spenders_taproot_active():
scripts = [
("pk_codesep", CScript(random_checksig_style(pubs[1]) + bytes([OP_CODESEPARATOR]))), # codesep after checksig
("codesep_pk", CScript(bytes([OP_CODESEPARATOR]) + random_checksig_style(pubs[1]))), # codesep before checksig
- ("branched_codesep", CScript([random_bytes(random.randrange(511)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
+ ("branched_codesep", CScript([random_bytes(random.randrange(2, 511)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
+ # Note that the first data push in the "branched_codesep" script has the purpose of
+ # randomizing the sighash, both by varying script size and content. In order to
+ # avoid MINIMALDATA script verification errors caused by not-minimal-encoded data
+ # pushes (e.g. `OP_PUSH1 1` instead of `OP_1`), we set a minimum data size of 2 bytes.
]
random.shuffle(scripts)
tap = taproot_construct(pubs[0], scripts)
@@ -1186,11 +1191,8 @@ def spenders_taproot_active():
# Also add a few legacy spends into the mix, so that transactions which combine taproot and pre-taproot spends get tested too.
for compressed in [False, True]:
- eckey1 = ECKey()
- eckey1.set(generate_privkey(), compressed)
- pubkey1 = eckey1.get_pubkey().get_bytes()
- eckey2 = ECKey()
- eckey2.set(generate_privkey(), compressed)
+ eckey1, pubkey1 = generate_keypair(compressed=compressed)
+ eckey2, _ = generate_keypair(compressed=compressed)
for p2sh in [False, True]:
for witv0 in [False, True]:
for hashtype in VALID_SIGHASHES_ECDSA + [random.randrange(0x04, 0x80), random.randrange(0x84, 0x100)]:
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 0a9e1d4448..073d3de812 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -28,9 +28,6 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- # The experimental syscall sandbox feature (-sandbox) is not compatible with -alertnotify
- # (which invokes execve).
- self.disable_syscall_sandbox = True
def setup_network(self):
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
diff --git a/test/functional/interface_usdt_mempool.py b/test/functional/interface_usdt_mempool.py
index 7f088a3ca8..f138fa44cc 100755
--- a/test/functional/interface_usdt_mempool.py
+++ b/test/functional/interface_usdt_mempool.py
@@ -139,6 +139,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
EXPECTED_ADDED_EVENTS = 1
handled_added_events = 0
+ event = None
self.log.info("Hooking into mempool:added tracepoint...")
node = self.nodes[0]
@@ -147,11 +148,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_added_event(_, data, __):
- nonlocal handled_added_events
+ nonlocal event, handled_added_events
event = bpf["added_events"].event(data)
- assert_equal(txid, bytes(event.hash)[::-1].hex())
- assert_equal(vsize, event.vsize)
- assert_equal(fee, event.fee)
handled_added_events += 1
bpf["added_events"].open_perf_buffer(handle_added_event)
@@ -159,9 +157,6 @@ class MempoolTracepointTest(BitcoinTestFramework):
self.log.info("Sending transaction...")
fee = Decimal(31200)
tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)
- # expected data
- txid = tx["txid"]
- vsize = tx["tx"].get_vsize()
self.log.info("Polling buffer...")
bpf.perf_buffer_poll(timeout=200)
@@ -169,10 +164,13 @@ class MempoolTracepointTest(BitcoinTestFramework):
self.log.info("Cleaning up mempool...")
self.generate(node, 1)
- bpf.cleanup()
-
self.log.info("Ensuring mempool:added event was handled successfully...")
assert_equal(EXPECTED_ADDED_EVENTS, handled_added_events)
+ assert_equal(bytes(event.hash)[::-1].hex(), tx["txid"])
+ assert_equal(event.vsize, tx["tx"].get_vsize())
+ assert_equal(event.fee, fee)
+
+ bpf.cleanup()
self.generate(self.wallet, 1)
def removed_test(self):
@@ -181,6 +179,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
EXPECTED_REMOVED_EVENTS = 1
handled_removed_events = 0
+ event = None
self.log.info("Hooking into mempool:removed tracepoint...")
node = self.nodes[0]
@@ -189,13 +188,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_removed_event(_, data, __):
- nonlocal handled_removed_events
+ nonlocal event, handled_removed_events
event = bpf["removed_events"].event(data)
- assert_equal(txid, bytes(event.hash)[::-1].hex())
- assert_equal(reason, event.reason.decode("UTF-8"))
- assert_equal(vsize, event.vsize)
- assert_equal(fee, event.fee)
- assert_equal(entry_time, event.entry_time)
handled_removed_events += 1
bpf["removed_events"].open_perf_buffer(handle_removed_event)
@@ -203,10 +197,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
self.log.info("Sending transaction...")
fee = Decimal(31200)
tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)
- # expected data
txid = tx["txid"]
- reason = "expiry"
- vsize = tx["tx"].get_vsize()
self.log.info("Fast-forwarding time to mempool expiry...")
entry_time = node.getmempoolentry(txid)["time"]
@@ -220,10 +211,15 @@ class MempoolTracepointTest(BitcoinTestFramework):
self.log.info("Polling buffer...")
bpf.perf_buffer_poll(timeout=200)
- bpf.cleanup()
-
self.log.info("Ensuring mempool:removed event was handled successfully...")
assert_equal(EXPECTED_REMOVED_EVENTS, handled_removed_events)
+ assert_equal(bytes(event.hash)[::-1].hex(), txid)
+ assert_equal(event.reason.decode("UTF-8"), "expiry")
+ assert_equal(event.vsize, tx["tx"].get_vsize())
+ assert_equal(event.fee, fee)
+ assert_equal(event.entry_time, entry_time)
+
+ bpf.cleanup()
self.generate(self.wallet, 1)
def replaced_test(self):
@@ -232,6 +228,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
EXPECTED_REPLACED_EVENTS = 1
handled_replaced_events = 0
+ event = None
self.log.info("Hooking into mempool:replaced tracepoint...")
node = self.nodes[0]
@@ -240,15 +237,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_replaced_event(_, data, __):
- nonlocal handled_replaced_events
+ nonlocal event, handled_replaced_events
event = bpf["replaced_events"].event(data)
- assert_equal(replaced_txid, bytes(event.replaced_hash)[::-1].hex())
- assert_equal(replaced_vsize, event.replaced_vsize)
- assert_equal(replaced_fee, event.replaced_fee)
- assert_equal(replaced_entry_time, event.replaced_entry_time)
- assert_equal(replacement_txid, bytes(event.replacement_hash)[::-1].hex())
- assert_equal(replacement_vsize, event.replacement_vsize)
- assert_equal(replacement_fee, event.replacement_fee)
handled_replaced_events += 1
bpf["replaced_events"].open_perf_buffer(handle_replaced_event)
@@ -267,21 +257,20 @@ class MempoolTracepointTest(BitcoinTestFramework):
from_node=node, utxo_to_spend=utxo, fee=replacement_fee / COIN
)
- # expected data
- replaced_txid = original_tx["txid"]
- replaced_vsize = original_tx["tx"].get_vsize()
- replaced_fee = original_fee
- replaced_entry_time = entry_time
- replacement_txid = replacement_tx["txid"]
- replacement_vsize = replacement_tx["tx"].get_vsize()
-
self.log.info("Polling buffer...")
bpf.perf_buffer_poll(timeout=200)
- bpf.cleanup()
-
self.log.info("Ensuring mempool:replaced event was handled successfully...")
assert_equal(EXPECTED_REPLACED_EVENTS, handled_replaced_events)
+ assert_equal(bytes(event.replaced_hash)[::-1].hex(), original_tx["txid"])
+ assert_equal(event.replaced_vsize, original_tx["tx"].get_vsize())
+ assert_equal(event.replaced_fee, original_fee)
+ assert_equal(event.replaced_entry_time, entry_time)
+ assert_equal(bytes(event.replacement_hash)[::-1].hex(), replacement_tx["txid"])
+ assert_equal(event.replacement_vsize, replacement_tx["tx"].get_vsize())
+ assert_equal(event.replacement_fee, replacement_fee)
+
+ bpf.cleanup()
self.generate(self.wallet, 1)
def rejected_test(self):
@@ -290,6 +279,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
EXPECTED_REJECTED_EVENTS = 1
handled_rejected_events = 0
+ event = None
self.log.info("Adding P2P connection...")
node = self.nodes[0]
@@ -301,10 +291,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_rejected_event(_, data, __):
- nonlocal handled_rejected_events
+ nonlocal event, handled_rejected_events
event = bpf["rejected_events"].event(data)
- assert_equal(txid, bytes(event.hash)[::-1].hex())
- assert_equal(reason, event.reason.decode("UTF-8"))
handled_rejected_events += 1
bpf["rejected_events"].open_perf_buffer(handle_rejected_event)
@@ -313,17 +301,15 @@ class MempoolTracepointTest(BitcoinTestFramework):
tx = self.wallet.create_self_transfer(fee_rate=Decimal(0))
node.p2ps[0].send_txs_and_test([tx["tx"]], node, success=False)
- # expected data
- txid = tx["tx"].hash
- reason = "min relay fee not met"
-
self.log.info("Polling buffer...")
bpf.perf_buffer_poll(timeout=200)
- bpf.cleanup()
-
self.log.info("Ensuring mempool:rejected event was handled successfully...")
assert_equal(EXPECTED_REJECTED_EVENTS, handled_rejected_events)
+ assert_equal(bytes(event.hash)[::-1].hex(), tx["tx"].hash)
+ assert_equal(event.reason.decode("UTF-8"), "min relay fee not met")
+
+ bpf.cleanup()
self.generate(self.wallet, 1)
def run_test(self):
diff --git a/test/functional/interface_usdt_net.py b/test/functional/interface_usdt_net.py
index 2235da702b..d1f94637c9 100755
--- a/test/functional/interface_usdt_net.py
+++ b/test/functional/interface_usdt_net.py
@@ -116,13 +116,10 @@ class NetTracepointTest(BitcoinTestFramework):
fn_name="trace_outbound_message")
bpf = BPF(text=net_tracepoints_program, usdt_contexts=[ctx], debug=0)
- # The handle_* function is a ctypes callback function called from C. When
- # we assert in the handle_* function, the AssertError doesn't propagate
- # back to Python. The exception is ignored. We manually count and assert
- # that the handle_* functions succeeded.
EXPECTED_INOUTBOUND_VERSION_MSG = 1
checked_inbound_version_msg = 0
checked_outbound_version_msg = 0
+ events = []
def check_p2p_message(event, inbound):
nonlocal checked_inbound_version_msg, checked_outbound_version_msg
@@ -142,12 +139,13 @@ class NetTracepointTest(BitcoinTestFramework):
checked_outbound_version_msg += 1
def handle_inbound(_, data, __):
+ nonlocal events
event = ctypes.cast(data, ctypes.POINTER(P2PMessage)).contents
- check_p2p_message(event, True)
+ events.append((event, True))
def handle_outbound(_, data, __):
event = ctypes.cast(data, ctypes.POINTER(P2PMessage)).contents
- check_p2p_message(event, False)
+ events.append((event, False))
bpf["inbound_messages"].open_perf_buffer(handle_inbound)
bpf["outbound_messages"].open_perf_buffer(handle_outbound)
@@ -158,12 +156,15 @@ class NetTracepointTest(BitcoinTestFramework):
bpf.perf_buffer_poll(timeout=200)
self.log.info(
- "check that we got both an inbound and outbound version message")
+ "check receipt and content of in- and outbound version messages")
+ for event, inbound in events:
+ check_p2p_message(event, inbound)
assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG,
checked_inbound_version_msg)
assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG,
checked_outbound_version_msg)
+
bpf.cleanup()
diff --git a/test/functional/interface_usdt_utxocache.py b/test/functional/interface_usdt_utxocache.py
index 6774db7c5f..5f2ba49026 100755
--- a/test/functional/interface_usdt_utxocache.py
+++ b/test/functional/interface_usdt_utxocache.py
@@ -188,13 +188,16 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
nonlocal handle_uncache_succeeds
event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents
self.log.info(f"handle_utxocache_uncache(): {event}")
- assert_equal(block_1_coinbase_txid, bytes(event.txid[::-1]).hex())
- assert_equal(0, event.index) # prevout index
- assert_equal(EARLY_BLOCK_HEIGHT, event.height)
- assert_equal(50 * COIN, event.value)
- assert_equal(True, event.is_coinbase)
-
- handle_uncache_succeeds += 1
+ try:
+ assert_equal(block_1_coinbase_txid, bytes(event.txid[::-1]).hex())
+ assert_equal(0, event.index) # prevout index
+ assert_equal(EARLY_BLOCK_HEIGHT, event.height)
+ assert_equal(50 * COIN, event.value)
+ assert_equal(True, event.is_coinbase)
+ except AssertionError:
+ self.log.exception("Assertion failed")
+ else:
+ handle_uncache_succeeds += 1
bpf["utxocache_uncache"].open_perf_buffer(handle_utxocache_uncache)
@@ -260,24 +263,32 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents
self.log.info(f"handle_utxocache_add(): {event}")
add = expected_utxocache_adds.pop(0)
- assert_equal(add["txid"], bytes(event.txid[::-1]).hex())
- assert_equal(add["index"], event.index)
- assert_equal(add["height"], event.height)
- assert_equal(add["value"], event.value)
- assert_equal(add["is_coinbase"], event.is_coinbase)
- handle_add_succeeds += 1
+ try:
+ assert_equal(add["txid"], bytes(event.txid[::-1]).hex())
+ assert_equal(add["index"], event.index)
+ assert_equal(add["height"], event.height)
+ assert_equal(add["value"], event.value)
+ assert_equal(add["is_coinbase"], event.is_coinbase)
+ except AssertionError:
+ self.log.exception("Assertion failed")
+ else:
+ handle_add_succeeds += 1
def handle_utxocache_spent(_, data, __):
nonlocal handle_spent_succeeds
event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents
self.log.info(f"handle_utxocache_spent(): {event}")
spent = expected_utxocache_spents.pop(0)
- assert_equal(spent["txid"], bytes(event.txid[::-1]).hex())
- assert_equal(spent["index"], event.index)
- assert_equal(spent["height"], event.height)
- assert_equal(spent["value"], event.value)
- assert_equal(spent["is_coinbase"], event.is_coinbase)
- handle_spent_succeeds += 1
+ try:
+ assert_equal(spent["txid"], bytes(event.txid[::-1]).hex())
+ assert_equal(spent["index"], event.index)
+ assert_equal(spent["height"], event.height)
+ assert_equal(spent["value"], event.value)
+ assert_equal(spent["is_coinbase"], event.is_coinbase)
+ except AssertionError:
+ self.log.exception("Assertion failed")
+ else:
+ handle_spent_succeeds += 1
bpf["utxocache_add"].open_perf_buffer(handle_utxocache_add)
bpf["utxocache_spent"].open_perf_buffer(handle_utxocache_spent)
diff --git a/test/functional/interface_usdt_validation.py b/test/functional/interface_usdt_validation.py
index 4323aef771..f9d9b525cd 100755
--- a/test/functional/interface_usdt_validation.py
+++ b/test/functional/interface_usdt_validation.py
@@ -85,13 +85,10 @@ class ValidationTracepointTest(BitcoinTestFramework):
self.sigops,
self.duration)
- # The handle_* function is a ctypes callback function called from C. When
- # we assert in the handle_* function, the AssertError doesn't propagate
- # back to Python. The exception is ignored. We manually count and assert
- # that the handle_* functions succeeded.
BLOCKS_EXPECTED = 2
blocks_checked = 0
expected_blocks = dict()
+ events = []
self.log.info("hook into the validation:block_connected tracepoint")
ctx = USDT(pid=self.nodes[0].process.pid)
@@ -101,19 +98,10 @@ class ValidationTracepointTest(BitcoinTestFramework):
usdt_contexts=[ctx], debug=0)
def handle_blockconnected(_, data, __):
- nonlocal expected_blocks, blocks_checked
+ nonlocal events, blocks_checked
event = ctypes.cast(data, ctypes.POINTER(Block)).contents
self.log.info(f"handle_blockconnected(): {event}")
- block_hash = bytes(event.hash[::-1]).hex()
- block = expected_blocks[block_hash]
- assert_equal(block["hash"], block_hash)
- assert_equal(block["height"], event.height)
- assert_equal(len(block["tx"]), event.transactions)
- assert_equal(len([tx["vin"] for tx in block["tx"]]), event.inputs)
- assert_equal(0, event.sigops) # no sigops in coinbase tx
- # only plausibility checks
- assert event.duration > 0
- del expected_blocks[block_hash]
+ events.append(event)
blocks_checked += 1
bpf["block_connected"].open_perf_buffer(
@@ -126,12 +114,24 @@ class ValidationTracepointTest(BitcoinTestFramework):
expected_blocks[block_hash] = self.nodes[0].getblock(block_hash, 2)
bpf.perf_buffer_poll(timeout=200)
- bpf.cleanup()
- self.log.info(f"check that we traced {BLOCKS_EXPECTED} blocks")
+ self.log.info(f"check that we correctly traced {BLOCKS_EXPECTED} blocks")
+ for event in events:
+ block_hash = bytes(event.hash[::-1]).hex()
+ block = expected_blocks[block_hash]
+ assert_equal(block["hash"], block_hash)
+ assert_equal(block["height"], event.height)
+ assert_equal(len(block["tx"]), event.transactions)
+ assert_equal(len([tx["vin"] for tx in block["tx"]]), event.inputs)
+ assert_equal(0, event.sigops) # no sigops in coinbase tx
+ # only plausibility checks
+ assert event.duration > 0
+ del expected_blocks[block_hash]
assert_equal(BLOCKS_EXPECTED, blocks_checked)
assert_equal(0, len(expected_blocks))
+ bpf.cleanup()
+
if __name__ == '__main__':
ValidationTracepointTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 737a8d0a2e..8f3aec96a7 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -9,7 +9,6 @@ from decimal import Decimal
import math
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.key import ECKey
from test_framework.messages import (
MAX_BIP125_RBF_SEQUENCE,
COIN,
@@ -44,6 +43,7 @@ from test_framework.util import (
assert_raises_rpc_error,
)
from test_framework.wallet import MiniWallet
+from test_framework.wallet_util import generate_keypair
class MempoolAcceptanceTest(BitcoinTestFramework):
@@ -283,9 +283,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
rawtxs=[tx.serialize().hex()],
)
tx = tx_from_hex(raw_tx_reference)
- key = ECKey()
- key.generate()
- pubkey = key.get_pubkey().get_bytes()
+ _, pubkey = generate_keypair()
tx.vout[0].scriptPubKey = keys_to_multisig_script([pubkey] * 3, k=2) # Some bare multisig script (2-of-3)
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bare-multisig'}],
diff --git a/test/functional/mempool_dust.py b/test/functional/mempool_dust.py
index 41a26e82da..f4e385a112 100755
--- a/test/functional/mempool_dust.py
+++ b/test/functional/mempool_dust.py
@@ -5,7 +5,6 @@
"""Test dust limit mempool policy (`-dustrelayfee` parameter)"""
from decimal import Decimal
-from test_framework.key import ECKey
from test_framework.messages import (
COIN,
CTxOut,
@@ -32,6 +31,7 @@ from test_framework.util import (
get_fee,
)
from test_framework.wallet import MiniWallet
+from test_framework.wallet_util import generate_keypair
DUST_RELAY_TX_FEE = 3000 # default setting [sat/kvB]
@@ -74,11 +74,8 @@ class DustRelayFeeTest(BitcoinTestFramework):
self.wallet = MiniWallet(self.nodes[0])
# prepare output scripts of each standard type
- key = ECKey()
- key.generate(compressed=False)
- uncompressed_pubkey = key.get_pubkey().get_bytes()
- key.generate(compressed=True)
- pubkey = key.get_pubkey().get_bytes()
+ _, uncompressed_pubkey = generate_keypair(compressed=False)
+ _, pubkey = generate_keypair(compressed=True)
output_scripts = (
(key_to_p2pk_script(uncompressed_pubkey), "P2PK (uncompressed)"),
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index b0900e49b8..bfae190c66 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -14,7 +14,6 @@ from test_framework.blocktools import (
create_block,
create_coinbase,
)
-from test_framework.key import ECKey
from test_framework.messages import (
MAX_BIP125_RBF_SEQUENCE,
CBlockHeader,
@@ -89,6 +88,7 @@ from test_framework.util import (
assert_raises_rpc_error,
)
from test_framework.wallet import MiniWallet
+from test_framework.wallet_util import generate_keypair
MAX_SIGOP_COST = 80000
@@ -1448,9 +1448,7 @@ class SegWitTest(BitcoinTestFramework):
# Segwit transactions using uncompressed pubkeys are not accepted
# under default policy, but should still pass consensus.
- key = ECKey()
- key.generate(False)
- pubkey = key.get_pubkey().get_bytes()
+ key, pubkey = generate_keypair(compressed=False)
assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey
utxo = self.utxo.pop(0)
@@ -1544,11 +1542,7 @@ class SegWitTest(BitcoinTestFramework):
@subtest
def test_signature_version_1(self):
-
- key = ECKey()
- key.generate()
- pubkey = key.get_pubkey().get_bytes()
-
+ key, pubkey = generate_keypair()
witness_script = key_to_p2pk_script(pubkey)
script_pubkey = script_to_p2wsh_script(witness_script)
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 0f79ce8588..65d7b4c422 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -12,13 +12,13 @@ from test_framework.address import address_to_scriptpubkey
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create, drop_origins
-from test_framework.key import ECPubKey, ECKey
+from test_framework.key import ECPubKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_raises_rpc_error,
assert_equal,
)
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
from test_framework.wallet import (
MiniWallet,
getnewdestination,
@@ -38,10 +38,9 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
self.priv = []
node0, node1, node2 = self.nodes
for _ in range(self.nkeys):
- k = ECKey()
- k.generate()
- self.pub.append(k.get_pubkey().get_bytes().hex())
- self.priv.append(bytes_to_wif(k.get_bytes(), k.is_compressed))
+ privkey, pubkey = generate_keypair(wif=True)
+ self.pub.append(pubkey.hex())
+ self.priv.append(privkey)
if self.is_bdb_compiled():
self.final = node2.getnewaddress()
else:
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index e2fb4673ea..c4ed4da0f2 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -8,7 +8,7 @@ from decimal import Decimal
from itertools import product
from test_framework.descriptors import descsum_create
-from test_framework.key import ECKey, H_POINT
+from test_framework.key import H_POINT
from test_framework.messages import (
COutPoint,
CTransaction,
@@ -43,8 +43,8 @@ from test_framework.util import (
random_bytes,
)
from test_framework.wallet_util import (
- bytes_to_wif,
- get_generate_key
+ generate_keypair,
+ get_generate_key,
)
import json
@@ -710,9 +710,7 @@ class PSBTTest(BitcoinTestFramework):
self.log.info("Test that we can fund psbts with external inputs specified")
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
+ privkey, _ = generate_keypair(wif=True)
self.nodes[1].createwallet("extfund")
wallet = self.nodes[1].get_wallet_rpc("extfund")
@@ -825,11 +823,9 @@ class PSBTTest(BitcoinTestFramework):
self.nodes[1].createwallet(wallet_name="scriptwatchonly", disable_private_keys=True)
watchonly = self.nodes[1].get_wallet_rpc("scriptwatchonly")
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
+ privkey, pubkey = generate_keypair(wif=True)
- desc = descsum_create("wsh(pkh({}))".format(eckey.get_pubkey().get_bytes().hex()))
+ desc = descsum_create("wsh(pkh({}))".format(pubkey.hex()))
if self.options.descriptors:
res = watchonly.importdescriptors([{"desc": desc, "timestamp": "now"}])
else:
@@ -846,11 +842,9 @@ class PSBTTest(BitcoinTestFramework):
# Same test but for taproot
if self.options.descriptors:
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
+ privkey, pubkey = generate_keypair(wif=True)
- desc = descsum_create("tr({},pk({}))".format(H_POINT, eckey.get_pubkey().get_bytes().hex()))
+ desc = descsum_create("tr({},pk({}))".format(H_POINT, pubkey.hex()))
res = watchonly.importdescriptors([{"desc": desc, "timestamp": "now"}])
assert res[0]["success"]
addr = self.nodes[0].deriveaddresses(desc)[0]
diff --git a/test/functional/rpc_signer.py b/test/functional/rpc_signer.py
index 4300190387..5ba0d35835 100755
--- a/test/functional/rpc_signer.py
+++ b/test/functional/rpc_signer.py
@@ -27,9 +27,6 @@ class RPCSignerTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
- # The experimental syscall sandbox feature (-sandbox) is not compatible with -signer (which
- # invokes execve).
- self.disable_syscall_sandbox = True
self.extra_args = [
[],
diff --git a/test/functional/rpc_signrawtransactionwithkey.py b/test/functional/rpc_signrawtransactionwithkey.py
index 580f63063d..ac7a86704f 100755
--- a/test/functional/rpc_signrawtransactionwithkey.py
+++ b/test/functional/rpc_signrawtransactionwithkey.py
@@ -11,7 +11,6 @@ from test_framework.address import (
address_to_scriptpubkey,
script_to_p2sh,
)
-from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -23,16 +22,16 @@ from test_framework.script_util import (
script_to_p2sh_p2wsh_script,
script_to_p2wsh_script,
)
+from test_framework.wallet import (
+ getnewdestination,
+)
from test_framework.wallet_util import (
- bytes_to_wif,
+ generate_keypair,
)
from decimal import (
Decimal,
)
-from test_framework.wallet import (
- getnewdestination,
-)
class SignRawTransactionWithKeyTest(BitcoinTestFramework):
@@ -80,11 +79,8 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
def witness_script_test(self):
self.log.info("Test signing transaction to P2SH-P2WSH addresses without wallet")
# Create a new P2SH-P2WSH 1-of-1 multisig address:
- eckey = ECKey()
- eckey.generate()
- embedded_privkey = bytes_to_wif(eckey.get_bytes())
- embedded_pubkey = eckey.get_pubkey().get_bytes().hex()
- p2sh_p2wsh_address = self.nodes[1].createmultisig(1, [embedded_pubkey], "p2sh-segwit")
+ embedded_privkey, embedded_pubkey = generate_keypair(wif=True)
+ p2sh_p2wsh_address = self.nodes[1].createmultisig(1, [embedded_pubkey.hex()], "p2sh-segwit")
# send transaction to P2SH-P2WSH 1-of-1 multisig address
self.block_hash = self.generate(self.nodes[0], COINBASE_MATURITY + 1)
self.blk_idx = 0
@@ -109,10 +105,7 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
def verify_txn_with_witness_script(self, tx_type):
self.log.info("Test with a {} script as the witnessScript".format(tx_type))
- eckey = ECKey()
- eckey.generate()
- embedded_privkey = bytes_to_wif(eckey.get_bytes())
- embedded_pubkey = eckey.get_pubkey().get_bytes().hex()
+ embedded_privkey, embedded_pubkey = generate_keypair(wif=True)
witness_script = {
'P2PKH': key_to_p2pkh_script(embedded_pubkey).hex(),
'P2PK': key_to_p2pk_script(embedded_pubkey).hex()
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index efb4934ff0..c250fc6fe8 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -1,7 +1,7 @@
# Copyright (c) 2019-2020 Pieter Wuille
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test-only secp256k1 elliptic curve implementation
+"""Test-only secp256k1 elliptic curve protocols implementation
WARNING: This code is slow, uses bad randomness, does not properly protect
keys, and is trivially vulnerable to side channel attacks. Do not use for
@@ -13,9 +13,13 @@ import os
import random
import unittest
+from test_framework import secp256k1
+
# Point with no known discrete log.
H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
+# Order of the secp256k1 curve
+ORDER = secp256k1.GE.ORDER
def TaggedHash(tag, data):
ss = hashlib.sha256(tag.encode('utf-8')).digest()
@@ -23,233 +27,18 @@ def TaggedHash(tag, data):
ss += data
return hashlib.sha256(ss).digest()
-def jacobi_symbol(n, k):
- """Compute the Jacobi symbol of n modulo k
-
- See https://en.wikipedia.org/wiki/Jacobi_symbol
-
- For our application k is always prime, so this is the same as the Legendre symbol."""
- assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k"
- n %= k
- t = 0
- while n != 0:
- while n & 1 == 0:
- n >>= 1
- r = k & 7
- t ^= (r == 3 or r == 5)
- n, k = k, n
- t ^= (n & k & 3 == 3)
- n = n % k
- if k == 1:
- return -1 if t else 1
- return 0
-
-def modsqrt(a, p):
- """Compute the square root of a modulo p when p % 4 = 3.
-
- The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
-
- Limiting this function to only work for p % 4 = 3 means we don't need to
- iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd
- is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4)
-
- secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4.
- """
- if p % 4 != 3:
- raise NotImplementedError("modsqrt only implemented for p % 4 = 3")
- sqrt = pow(a, (p + 1)//4, p)
- if pow(sqrt, 2, p) == a % p:
- return sqrt
- return None
-
-class EllipticCurve:
- def __init__(self, p, a, b):
- """Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
- self.p = p
- self.a = a % p
- self.b = b % p
-
- def affine(self, p1):
- """Convert a Jacobian point tuple p1 to affine form, or None if at infinity.
-
- An affine point is represented as the Jacobian (x, y, 1)"""
- x1, y1, z1 = p1
- if z1 == 0:
- return None
- inv = pow(z1, -1, self.p)
- inv_2 = (inv**2) % self.p
- inv_3 = (inv_2 * inv) % self.p
- return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
-
- def has_even_y(self, p1):
- """Whether the point p1 has an even Y coordinate when expressed in affine coordinates."""
- return not (p1[2] == 0 or self.affine(p1)[1] & 1)
-
- def negate(self, p1):
- """Negate a Jacobian point tuple p1."""
- x1, y1, z1 = p1
- return (x1, (self.p - y1) % self.p, z1)
-
- def on_curve(self, p1):
- """Determine whether a Jacobian tuple p is on the curve (and not infinity)"""
- x1, y1, z1 = p1
- z2 = pow(z1, 2, self.p)
- z4 = pow(z2, 2, self.p)
- return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0
-
- def is_x_coord(self, x):
- """Test whether x is a valid X coordinate on the curve."""
- x_3 = pow(x, 3, self.p)
- return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
-
- def lift_x(self, x):
- """Given an X coordinate on the curve, return a corresponding affine point for which the Y coordinate is even."""
- x_3 = pow(x, 3, self.p)
- v = x_3 + self.a * x + self.b
- y = modsqrt(v, self.p)
- if y is None:
- return None
- return (x, self.p - y if y & 1 else y, 1)
-
- def double(self, p1):
- """Double a Jacobian tuple p1
-
- See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling"""
- x1, y1, z1 = p1
- if z1 == 0:
- return (0, 1, 0)
- y1_2 = (y1**2) % self.p
- y1_4 = (y1_2**2) % self.p
- x1_2 = (x1**2) % self.p
- s = (4*x1*y1_2) % self.p
- m = 3*x1_2
- if self.a:
- m += self.a * pow(z1, 4, self.p)
- m = m % self.p
- x2 = (m**2 - 2*s) % self.p
- y2 = (m*(s - x2) - 8*y1_4) % self.p
- z2 = (2*y1*z1) % self.p
- return (x2, y2, z2)
-
- def add_mixed(self, p1, p2):
- """Add a Jacobian tuple p1 and an affine tuple p2
-
- See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)"""
- x1, y1, z1 = p1
- x2, y2, z2 = p2
- assert z2 == 1
- # Adding to the point at infinity is a no-op
- if z1 == 0:
- return p2
- z1_2 = (z1**2) % self.p
- z1_3 = (z1_2 * z1) % self.p
- u2 = (x2 * z1_2) % self.p
- s2 = (y2 * z1_3) % self.p
- if x1 == u2:
- if (y1 != s2):
- # p1 and p2 are inverses. Return the point at infinity.
- return (0, 1, 0)
- # p1 == p2. The formulas below fail when the two points are equal.
- return self.double(p1)
- h = u2 - x1
- r = s2 - y1
- h_2 = (h**2) % self.p
- h_3 = (h_2 * h) % self.p
- u1_h_2 = (x1 * h_2) % self.p
- x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
- y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p
- z3 = (h*z1) % self.p
- return (x3, y3, z3)
-
- def add(self, p1, p2):
- """Add two Jacobian tuples p1 and p2
-
- See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition"""
- x1, y1, z1 = p1
- x2, y2, z2 = p2
- # Adding the point at infinity is a no-op
- if z1 == 0:
- return p2
- if z2 == 0:
- return p1
- # Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1
- if z1 == 1:
- return self.add_mixed(p2, p1)
- if z2 == 1:
- return self.add_mixed(p1, p2)
- z1_2 = (z1**2) % self.p
- z1_3 = (z1_2 * z1) % self.p
- z2_2 = (z2**2) % self.p
- z2_3 = (z2_2 * z2) % self.p
- u1 = (x1 * z2_2) % self.p
- u2 = (x2 * z1_2) % self.p
- s1 = (y1 * z2_3) % self.p
- s2 = (y2 * z1_3) % self.p
- if u1 == u2:
- if (s1 != s2):
- # p1 and p2 are inverses. Return the point at infinity.
- return (0, 1, 0)
- # p1 == p2. The formulas below fail when the two points are equal.
- return self.double(p1)
- h = u2 - u1
- r = s2 - s1
- h_2 = (h**2) % self.p
- h_3 = (h_2 * h) % self.p
- u1_h_2 = (u1 * h_2) % self.p
- x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
- y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p
- z3 = (h*z1*z2) % self.p
- return (x3, y3, z3)
-
- def mul(self, ps):
- """Compute a (multi) point multiplication
-
- ps is a list of (Jacobian tuple, scalar) pairs.
- """
- r = (0, 1, 0)
- for i in range(255, -1, -1):
- r = self.double(r)
- for (p, n) in ps:
- if ((n >> i) & 1):
- r = self.add(r, p)
- return r
-
-SECP256K1_FIELD_SIZE = 2**256 - 2**32 - 977
-SECP256K1 = EllipticCurve(SECP256K1_FIELD_SIZE, 0, 7)
-SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
-SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
-SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
-
-class ECPubKey():
+
+class ECPubKey:
"""A secp256k1 public key"""
def __init__(self):
"""Construct an uninitialized public key"""
- self.valid = False
+ self.p = None
def set(self, data):
"""Construct a public key from a serialization in compressed or uncompressed format"""
- if (len(data) == 65 and data[0] == 0x04):
- p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1)
- self.valid = SECP256K1.on_curve(p)
- if self.valid:
- self.p = p
- self.compressed = False
- elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)):
- x = int.from_bytes(data[1:33], 'big')
- if SECP256K1.is_x_coord(x):
- p = SECP256K1.lift_x(x)
- # Make the Y coordinate odd if required (lift_x always produces
- # a point with an even Y coordinate).
- if data[0] & 1:
- p = SECP256K1.negate(p)
- self.p = p
- self.valid = True
- self.compressed = True
- else:
- self.valid = False
- else:
- self.valid = False
+ self.p = secp256k1.GE.from_bytes(data)
+ self.compressed = len(data) == 33
@property
def is_compressed(self):
@@ -257,24 +46,21 @@ class ECPubKey():
@property
def is_valid(self):
- return self.valid
+ return self.p is not None
def get_bytes(self):
- assert self.valid
- p = SECP256K1.affine(self.p)
- if p is None:
- return None
+ assert self.is_valid
if self.compressed:
- return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big')
+ return self.p.to_bytes_compressed()
else:
- return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big')
+ return self.p.to_bytes_uncompressed()
def verify_ecdsa(self, sig, msg, low_s=True):
"""Verify a strictly DER-encoded ECDSA signature against this pubkey.
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
ECDSA verifier algorithm"""
- assert self.valid
+ assert self.is_valid
# Extract r and s from the DER formatted signature. Return false for
# any DER encoding errors.
@@ -310,24 +96,22 @@ class ECPubKey():
s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big')
# Verify that r and s are within the group order
- if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER:
+ if r < 1 or s < 1 or r >= ORDER or s >= ORDER:
return False
- if low_s and s >= SECP256K1_ORDER_HALF:
+ if low_s and s >= secp256k1.GE.ORDER_HALF:
return False
z = int.from_bytes(msg, 'big')
# Run verifier algorithm on r, s
- w = pow(s, -1, SECP256K1_ORDER)
- u1 = z*w % SECP256K1_ORDER
- u2 = r*w % SECP256K1_ORDER
- R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)]))
- if R is None or (R[0] % SECP256K1_ORDER) != r:
+ w = pow(s, -1, ORDER)
+ R = secp256k1.GE.mul((z * w, secp256k1.G), (r * w, self.p))
+ if R.infinity or (int(R.x) % ORDER) != r:
return False
return True
def generate_privkey():
"""Generate a valid random 32-byte private key."""
- return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
+ return random.randrange(1, ORDER).to_bytes(32, 'big')
def rfc6979_nonce(key):
"""Compute signing nonce using RFC6979."""
@@ -339,7 +123,7 @@ def rfc6979_nonce(key):
v = hmac.new(k, v, 'sha256').digest()
return hmac.new(k, v, 'sha256').digest()
-class ECKey():
+class ECKey:
"""A secp256k1 private key"""
def __init__(self):
@@ -349,7 +133,7 @@ class ECKey():
"""Construct a private key object with given 32-byte secret and compressed flag."""
assert len(secret) == 32
secret = int.from_bytes(secret, 'big')
- self.valid = (secret > 0 and secret < SECP256K1_ORDER)
+ self.valid = (secret > 0 and secret < ORDER)
if self.valid:
self.secret = secret
self.compressed = compressed
@@ -375,9 +159,7 @@ class ECKey():
"""Compute an ECPubKey object for this secret key."""
assert self.valid
ret = ECPubKey()
- p = SECP256K1.mul([(SECP256K1_G, self.secret)])
- ret.p = p
- ret.valid = True
+ ret.p = self.secret * secp256k1.G
ret.compressed = self.compressed
return ret
@@ -392,12 +174,12 @@ class ECKey():
if rfc6979:
k = int.from_bytes(rfc6979_nonce(self.secret.to_bytes(32, 'big') + msg), 'big')
else:
- k = random.randrange(1, SECP256K1_ORDER)
- R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
- r = R[0] % SECP256K1_ORDER
- s = (pow(k, -1, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
- if low_s and s > SECP256K1_ORDER_HALF:
- s = SECP256K1_ORDER - s
+ k = random.randrange(1, ORDER)
+ R = k * secp256k1.G
+ r = int(R.x) % ORDER
+ s = (pow(k, -1, ORDER) * (z + self.secret * r)) % ORDER
+ if low_s and s > secp256k1.GE.ORDER_HALF:
+ s = ORDER - s
# Represent in DER format. The byte representations of r and s have
# length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33
# bytes).
@@ -413,10 +195,10 @@ def compute_xonly_pubkey(key):
assert len(key) == 32
x = int.from_bytes(key, 'big')
- if x == 0 or x >= SECP256K1_ORDER:
+ if x == 0 or x >= ORDER:
return (None, None)
- P = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, x)]))
- return (P[0].to_bytes(32, 'big'), not SECP256K1.has_even_y(P))
+ P = x * secp256k1.G
+ return (P.to_bytes_xonly(), not P.y.is_even())
def tweak_add_privkey(key, tweak):
"""Tweak a private key (after negating it if needed)."""
@@ -425,14 +207,14 @@ def tweak_add_privkey(key, tweak):
assert len(tweak) == 32
x = int.from_bytes(key, 'big')
- if x == 0 or x >= SECP256K1_ORDER:
+ if x == 0 or x >= ORDER:
return None
- if not SECP256K1.has_even_y(SECP256K1.mul([(SECP256K1_G, x)])):
- x = SECP256K1_ORDER - x
+ if not (x * secp256k1.G).y.is_even():
+ x = ORDER - x
t = int.from_bytes(tweak, 'big')
- if t >= SECP256K1_ORDER:
+ if t >= ORDER:
return None
- x = (x + t) % SECP256K1_ORDER
+ x = (x + t) % ORDER
if x == 0:
return None
return x.to_bytes(32, 'big')
@@ -443,19 +225,16 @@ def tweak_add_pubkey(key, tweak):
assert len(key) == 32
assert len(tweak) == 32
- x_coord = int.from_bytes(key, 'big')
- if x_coord >= SECP256K1_FIELD_SIZE:
- return None
- P = SECP256K1.lift_x(x_coord)
+ P = secp256k1.GE.from_bytes_xonly(key)
if P is None:
return None
t = int.from_bytes(tweak, 'big')
- if t >= SECP256K1_ORDER:
+ if t >= ORDER:
return None
- Q = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, t), (P, 1)]))
- if Q is None:
+ Q = t * secp256k1.G + P
+ if Q.infinity:
return None
- return (Q[0].to_bytes(32, 'big'), not SECP256K1.has_even_y(Q))
+ return (Q.to_bytes_xonly(), not Q.y.is_even())
def verify_schnorr(key, sig, msg):
"""Verify a Schnorr signature (see BIP 340).
@@ -468,23 +247,20 @@ def verify_schnorr(key, sig, msg):
assert len(msg) == 32
assert len(sig) == 64
- x_coord = int.from_bytes(key, 'big')
- if x_coord == 0 or x_coord >= SECP256K1_FIELD_SIZE:
- return False
- P = SECP256K1.lift_x(x_coord)
+ P = secp256k1.GE.from_bytes_xonly(key)
if P is None:
return False
r = int.from_bytes(sig[0:32], 'big')
- if r >= SECP256K1_FIELD_SIZE:
+ if r >= secp256k1.FE.SIZE:
return False
s = int.from_bytes(sig[32:64], 'big')
- if s >= SECP256K1_ORDER:
+ if s >= ORDER:
return False
- e = int.from_bytes(TaggedHash("BIP0340/challenge", sig[0:32] + key + msg), 'big') % SECP256K1_ORDER
- R = SECP256K1.mul([(SECP256K1_G, s), (P, SECP256K1_ORDER - e)])
- if not SECP256K1.has_even_y(R):
+ e = int.from_bytes(TaggedHash("BIP0340/challenge", sig[0:32] + key + msg), 'big') % ORDER
+ R = secp256k1.GE.mul((s, secp256k1.G), (-e, P))
+ if R.infinity or not R.y.is_even():
return False
- if ((r * R[2] * R[2]) % SECP256K1_FIELD_SIZE) != R[0]:
+ if r != R.x:
return False
return True
@@ -499,23 +275,24 @@ def sign_schnorr(key, msg, aux=None, flip_p=False, flip_r=False):
assert len(aux) == 32
sec = int.from_bytes(key, 'big')
- if sec == 0 or sec >= SECP256K1_ORDER:
+ if sec == 0 or sec >= ORDER:
return None
- P = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, sec)]))
- if SECP256K1.has_even_y(P) == flip_p:
- sec = SECP256K1_ORDER - sec
+ P = sec * secp256k1.G
+ if P.y.is_even() == flip_p:
+ sec = ORDER - sec
t = (sec ^ int.from_bytes(TaggedHash("BIP0340/aux", aux), 'big')).to_bytes(32, 'big')
- kp = int.from_bytes(TaggedHash("BIP0340/nonce", t + P[0].to_bytes(32, 'big') + msg), 'big') % SECP256K1_ORDER
+ kp = int.from_bytes(TaggedHash("BIP0340/nonce", t + P.to_bytes_xonly() + msg), 'big') % ORDER
assert kp != 0
- R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, kp)]))
- k = kp if SECP256K1.has_even_y(R) != flip_r else SECP256K1_ORDER - kp
- e = int.from_bytes(TaggedHash("BIP0340/challenge", R[0].to_bytes(32, 'big') + P[0].to_bytes(32, 'big') + msg), 'big') % SECP256K1_ORDER
- return R[0].to_bytes(32, 'big') + ((k + e * sec) % SECP256K1_ORDER).to_bytes(32, 'big')
+ R = kp * secp256k1.G
+ k = kp if R.y.is_even() != flip_r else ORDER - kp
+ e = int.from_bytes(TaggedHash("BIP0340/challenge", R.to_bytes_xonly() + P.to_bytes_xonly() + msg), 'big') % ORDER
+ return R.to_bytes_xonly() + ((k + e * sec) % ORDER).to_bytes(32, 'big')
+
class TestFrameworkKey(unittest.TestCase):
def test_schnorr(self):
"""Test the Python Schnorr implementation."""
- byte_arrays = [generate_privkey() for _ in range(3)] + [v.to_bytes(32, 'big') for v in [0, SECP256K1_ORDER - 1, SECP256K1_ORDER, 2**256 - 1]]
+ byte_arrays = [generate_privkey() for _ in range(3)] + [v.to_bytes(32, 'big') for v in [0, ORDER - 1, ORDER, 2**256 - 1]]
keys = {}
for privkey in byte_arrays: # build array of key/pubkey pairs
pubkey, _ = compute_xonly_pubkey(privkey)
diff --git a/test/functional/test_framework/secp256k1.py b/test/functional/test_framework/secp256k1.py
new file mode 100644
index 0000000000..2e9e419da5
--- /dev/null
+++ b/test/functional/test_framework/secp256k1.py
@@ -0,0 +1,346 @@
+# Copyright (c) 2022-2023 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""Test-only implementation of low-level secp256k1 field and group arithmetic
+
+It is designed for ease of understanding, not performance.
+
+WARNING: This code is slow and trivially vulnerable to side channel attacks. Do not use for
+anything but tests.
+
+Exports:
+* FE: class for secp256k1 field elements
+* GE: class for secp256k1 group elements
+* G: the secp256k1 generator point
+"""
+
+
+class FE:
+ """Objects of this class represent elements of the field GF(2**256 - 2**32 - 977).
+
+ They are represented internally in numerator / denominator form, in order to delay inversions.
+ """
+
+ # The size of the field (also its modulus and characteristic).
+ SIZE = 2**256 - 2**32 - 977
+
+ def __init__(self, a=0, b=1):
+ """Initialize a field element a/b; both a and b can be ints or field elements."""
+ if isinstance(a, FE):
+ num = a._num
+ den = a._den
+ else:
+ num = a % FE.SIZE
+ den = 1
+ if isinstance(b, FE):
+ den = (den * b._num) % FE.SIZE
+ num = (num * b._den) % FE.SIZE
+ else:
+ den = (den * b) % FE.SIZE
+ assert den != 0
+ if num == 0:
+ den = 1
+ self._num = num
+ self._den = den
+
+ def __add__(self, a):
+ """Compute the sum of two field elements (second may be int)."""
+ if isinstance(a, FE):
+ return FE(self._num * a._den + self._den * a._num, self._den * a._den)
+ return FE(self._num + self._den * a, self._den)
+
+ def __radd__(self, a):
+ """Compute the sum of an integer and a field element."""
+ return FE(a) + self
+
+ def __sub__(self, a):
+ """Compute the difference of two field elements (second may be int)."""
+ if isinstance(a, FE):
+ return FE(self._num * a._den - self._den * a._num, self._den * a._den)
+ return FE(self._num - self._den * a, self._den)
+
+ def __rsub__(self, a):
+ """Compute the difference of an integer and a field element."""
+ return FE(a) - self
+
+ def __mul__(self, a):
+ """Compute the product of two field elements (second may be int)."""
+ if isinstance(a, FE):
+ return FE(self._num * a._num, self._den * a._den)
+ return FE(self._num * a, self._den)
+
+ def __rmul__(self, a):
+ """Compute the product of an integer with a field element."""
+ return FE(a) * self
+
+ def __truediv__(self, a):
+ """Compute the ratio of two field elements (second may be int)."""
+ return FE(self, a)
+
+ def __pow__(self, a):
+ """Raise a field element to an integer power."""
+ return FE(pow(self._num, a, FE.SIZE), pow(self._den, a, FE.SIZE))
+
+ def __neg__(self):
+ """Negate a field element."""
+ return FE(-self._num, self._den)
+
+ def __int__(self):
+ """Convert a field element to an integer in range 0..p-1. The result is cached."""
+ if self._den != 1:
+ self._num = (self._num * pow(self._den, -1, FE.SIZE)) % FE.SIZE
+ self._den = 1
+ return self._num
+
+ def sqrt(self):
+ """Compute the square root of a field element if it exists (None otherwise).
+
+ Due to the fact that our modulus is of the form (p % 4) == 3, the Tonelli-Shanks
+ algorithm (https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm) is simply
+ raising the argument to the power (p + 1) / 4.
+
+ To see why: (p-1) % 2 = 0, so 2 divides the order of the multiplicative group,
+ and thus only half of the non-zero field elements are squares. An element a is
+ a (nonzero) square when Euler's criterion, a^((p-1)/2) = 1 (mod p), holds. We're
+ looking for x such that x^2 = a (mod p). Given a^((p-1)/2) = 1, that is equivalent
+ to x^2 = a^(1 + (p-1)/2) mod p. As (1 + (p-1)/2) is even, this is equivalent to
+ x = a^((1 + (p-1)/2)/2) mod p, or x = a^((p+1)/4) mod p."""
+ v = int(self)
+ s = pow(v, (FE.SIZE + 1) // 4, FE.SIZE)
+ if s**2 % FE.SIZE == v:
+ return FE(s)
+ return None
+
+ def is_square(self):
+ """Determine if this field element has a square root."""
+ # A more efficient algorithm is possible here (Jacobi symbol).
+ return self.sqrt() is not None
+
+ def is_even(self):
+ """Determine whether this field element, represented as integer in 0..p-1, is even."""
+ return int(self) & 1 == 0
+
+ def __eq__(self, a):
+ """Check whether two field elements are equal (second may be an int)."""
+ if isinstance(a, FE):
+ return (self._num * a._den - self._den * a._num) % FE.SIZE == 0
+ return (self._num - self._den * a) % FE.SIZE == 0
+
+ def to_bytes(self):
+ """Convert a field element to a 32-byte array (BE byte order)."""
+ return int(self).to_bytes(32, 'big')
+
+ @staticmethod
+ def from_bytes(b):
+ """Convert a 32-byte array to a field element (BE byte order, no overflow allowed)."""
+ v = int.from_bytes(b, 'big')
+ if v >= FE.SIZE:
+ return None
+ return FE(v)
+
+ def __str__(self):
+ """Convert this field element to a 64 character hex string."""
+ return f"{int(self):064x}"
+
+ def __repr__(self):
+ """Get a string representation of this field element."""
+ return f"FE(0x{int(self):x})"
+
+
+class GE:
+ """Objects of this class represent secp256k1 group elements (curve points or infinity)
+
+ Normal points on the curve have fields:
+ * x: the x coordinate (a field element)
+ * y: the y coordinate (a field element, satisfying y^2 = x^3 + 7)
+ * infinity: False
+
+ The point at infinity has field:
+ * infinity: True
+ """
+
+ # Order of the group (number of points on the curve, plus 1 for infinity)
+ ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
+
+ # Number of valid distinct x coordinates on the curve.
+ ORDER_HALF = ORDER // 2
+
+ def __init__(self, x=None, y=None):
+ """Initialize a group element with specified x and y coordinates, or infinity."""
+ if x is None:
+ # Initialize as infinity.
+ assert y is None
+ self.infinity = True
+ else:
+ # Initialize as point on the curve (and check that it is).
+ fx = FE(x)
+ fy = FE(y)
+ assert fy**2 == fx**3 + 7
+ self.infinity = False
+ self.x = fx
+ self.y = fy
+
+ def __add__(self, a):
+ """Add two group elements together."""
+ # Deal with infinity: a + infinity == infinity + a == a.
+ if self.infinity:
+ return a
+ if a.infinity:
+ return self
+ if self.x == a.x:
+ if self.y != a.y:
+ # A point added to its own negation is infinity.
+ assert self.y + a.y == 0
+ return GE()
+ else:
+ # For identical inputs, use the tangent (doubling formula).
+ lam = (3 * self.x**2) / (2 * self.y)
+ else:
+ # For distinct inputs, use the line through both points (adding formula).
+ lam = (self.y - a.y) / (self.x - a.x)
+ # Determine point opposite to the intersection of that line with the curve.
+ x = lam**2 - (self.x + a.x)
+ y = lam * (self.x - x) - self.y
+ return GE(x, y)
+
+ @staticmethod
+ def mul(*aps):
+ """Compute a (batch) scalar group element multiplication.
+
+ GE.mul((a1, p1), (a2, p2), (a3, p3)) is identical to a1*p1 + a2*p2 + a3*p3,
+ but more efficient."""
+ # Reduce all the scalars modulo order first (so we can deal with negatives etc).
+ naps = [(a % GE.ORDER, p) for a, p in aps]
+ # Start with point at infinity.
+ r = GE()
+ # Iterate over all bit positions, from high to low.
+ for i in range(255, -1, -1):
+ # Double what we have so far.
+ r = r + r
+ # Add then add the points for which the corresponding scalar bit is set.
+ for (a, p) in naps:
+ if (a >> i) & 1:
+ r += p
+ return r
+
+ def __rmul__(self, a):
+ """Multiply an integer with a group element."""
+ if self == G:
+ return FAST_G.mul(a)
+ return GE.mul((a, self))
+
+ def __neg__(self):
+ """Compute the negation of a group element."""
+ if self.infinity:
+ return self
+ return GE(self.x, -self.y)
+
+ def to_bytes_compressed(self):
+ """Convert a non-infinite group element to 33-byte compressed encoding."""
+ assert not self.infinity
+ return bytes([3 - self.y.is_even()]) + self.x.to_bytes()
+
+ def to_bytes_uncompressed(self):
+ """Convert a non-infinite group element to 65-byte uncompressed encoding."""
+ assert not self.infinity
+ return b'\x04' + self.x.to_bytes() + self.y.to_bytes()
+
+ def to_bytes_xonly(self):
+ """Convert (the x coordinate of) a non-infinite group element to 32-byte xonly encoding."""
+ assert not self.infinity
+ return self.x.to_bytes()
+
+ @staticmethod
+ def lift_x(x):
+ """Return group element with specified field element as x coordinate (and even y)."""
+ y = (FE(x)**3 + 7).sqrt()
+ if y is None:
+ return None
+ if not y.is_even():
+ y = -y
+ return GE(x, y)
+
+ @staticmethod
+ def from_bytes(b):
+ """Convert a compressed or uncompressed encoding to a group element."""
+ assert len(b) in (33, 65)
+ if len(b) == 33:
+ if b[0] != 2 and b[0] != 3:
+ return None
+ x = FE.from_bytes(b[1:])
+ if x is None:
+ return None
+ r = GE.lift_x(x)
+ if r is None:
+ return None
+ if b[0] == 3:
+ r = -r
+ return r
+ else:
+ if b[0] != 4:
+ return None
+ x = FE.from_bytes(b[1:33])
+ y = FE.from_bytes(b[33:])
+ if y**2 != x**3 + 7:
+ return None
+ return GE(x, y)
+
+ @staticmethod
+ def from_bytes_xonly(b):
+ """Convert a point given in xonly encoding to a group element."""
+ assert len(b) == 32
+ x = FE.from_bytes(b)
+ if x is None:
+ return None
+ return GE.lift_x(x)
+
+ @staticmethod
+ def is_valid_x(x):
+ """Determine whether the provided field element is a valid X coordinate."""
+ return (FE(x)**3 + 7).is_square()
+
+ def __str__(self):
+ """Convert this group element to a string."""
+ if self.infinity:
+ return "(inf)"
+ return f"({self.x},{self.y})"
+
+ def __repr__(self):
+ """Get a string representation for this group element."""
+ if self.infinity:
+ return "GE()"
+ return f"GE(0x{int(self.x):x},0x{int(self.y):x})"
+
+# The secp256k1 generator point
+G = GE.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
+
+
+class FastGEMul:
+ """Table for fast multiplication with a constant group element.
+
+ Speed up scalar multiplication with a fixed point P by using a precomputed lookup table with
+ its powers of 2:
+
+ table = [P, 2*P, 4*P, (2^3)*P, (2^4)*P, ..., (2^255)*P]
+
+ During multiplication, the points corresponding to each bit set in the scalar are added up,
+ i.e. on average ~128 point additions take place.
+ """
+
+ def __init__(self, p):
+ self.table = [p] # table[i] = (2^i) * p
+ for _ in range(255):
+ p = p + p
+ self.table.append(p)
+
+ def mul(self, a):
+ result = GE()
+ a = a % GE.ORDER
+ for bit in range(a.bit_length()):
+ if a & (1 << bit):
+ result += self.table[bit]
+ return result
+
+# Precomputed table with multiples of G for fast multiplication
+FAST_G = FastGEMul(G)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index d4dc90a517..d3aae3fb9a 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -103,7 +103,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.supports_cli = True
self.bind_to_localhost_only = True
self.parse_args()
- self.disable_syscall_sandbox = self.options.nosandbox or self.options.valgrind
self.default_wallet_name = "default_wallet" if self.options.descriptors else ""
self.wallet_data_filename = "wallet.dat"
# Optional list of wallet names that can be set in set_test_params to
@@ -160,8 +159,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser = argparse.ArgumentParser(usage="%(prog)s [options]")
parser.add_argument("--nocleanup", dest="nocleanup", default=False, action="store_true",
help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_argument("--nosandbox", dest="nosandbox", default=False, action="store_true",
- help="Don't use the syscall sandbox")
parser.add_argument("--noshutdown", dest="noshutdown", default=False, action="store_true",
help="Don't stop bitcoinds after the test execution")
parser.add_argument("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"),
@@ -188,7 +185,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser.add_argument("--perf", dest="perf", default=False, action="store_true",
help="profile running nodes with perf for the duration of the test")
parser.add_argument("--valgrind", dest="valgrind", default=False, action="store_true",
- help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown. valgrind 3.14 or later required. Forces --nosandbox.")
+ help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown. valgrind 3.14 or later required.")
parser.add_argument("--randomseed", type=int,
help="set a random seed for deterministically reproducing a previous test run")
parser.add_argument("--timeout-factor", dest="timeout_factor", type=float, help="adjust test timeouts by a factor. Setting it to 0 disables all timeouts")
@@ -497,11 +494,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args = [[]] * num_nodes
if versions is None:
versions = [None] * num_nodes
- if self.is_syscall_sandbox_compiled() and not self.disable_syscall_sandbox:
- for i in range(len(extra_args)):
- # The -sandbox argument is not present in the v22.0 release.
- if versions[i] is None or versions[i] >= 229900:
- extra_args[i] = extra_args[i] + ["-sandbox=log-and-abort"]
if binary is None:
binary = [get_bin_from_version(v, 'bitcoind', self.options.bitcoind) for v in versions]
if binary_cli is None:
@@ -987,7 +979,3 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def is_bdb_compiled(self):
"""Checks whether the wallet module was compiled with BDB support."""
return self.config["components"].getboolean("USE_BDB")
-
- def is_syscall_sandbox_compiled(self):
- """Checks whether the syscall sandbox was compiled."""
- return self.config["components"].getboolean("ENABLE_SYSCALL_SANDBOX")
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 1d546e12bd..271095ea21 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -20,6 +20,7 @@ from test_framework.address import (
key_to_p2wpkh,
output_key_to_p2tr,
)
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.descriptors import descsum_create
from test_framework.key import (
ECKey,
@@ -53,7 +54,7 @@ from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
)
-from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.wallet_util import generate_keypair
DEFAULT_FEE = Decimal("0.0001")
@@ -395,9 +396,7 @@ def getnewdestination(address_type='bech32m'):
'legacy', 'p2sh-segwit', 'bech32' and 'bech32m'. Can be used when a random
destination is needed, but no compiled wallet is available (e.g. as
replacement to the getnewaddress/getaddressinfo RPCs)."""
- key = ECKey()
- key.generate()
- pubkey = key.get_pubkey().get_bytes()
+ key, pubkey = generate_keypair()
if address_type == 'legacy':
scriptpubkey = key_to_p2pkh_script(pubkey)
address = key_to_p2pkh(pubkey)
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index 410d85cd8c..319f120297 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -63,12 +63,9 @@ def get_generate_key():
"""Generate a fresh key
Returns a named tuple of privkey, pubkey and all address and scripts."""
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
- pubkey = eckey.get_pubkey().get_bytes().hex()
+ privkey, pubkey = generate_keypair(wif=True)
return Key(privkey=privkey,
- pubkey=pubkey,
+ pubkey=pubkey.hex(),
p2pkh_script=key_to_p2pkh_script(pubkey).hex(),
p2pkh_addr=key_to_p2pkh(pubkey),
p2wpkh_script=key_to_p2wpkh_script(pubkey).hex(),
@@ -114,8 +111,14 @@ def bytes_to_wif(b, compressed=True):
b += b'\x01'
return byte_to_base58(b, 239)
-def generate_wif_key():
- # Makes a WIF privkey for imports
- k = ECKey()
- k.generate()
- return bytes_to_wif(k.get_bytes(), k.is_compressed)
+def generate_keypair(compressed=True, wif=False):
+ """Generate a new random keypair and return the corresponding ECKey /
+ bytes objects. The private key can also be provided as WIF (wallet
+ import format) string instead, which is often useful for wallet RPC
+ interaction."""
+ privkey = ECKey()
+ privkey.generate(compressed)
+ pubkey = privkey.get_pubkey().get_bytes()
+ if wif:
+ privkey = bytes_to_wif(privkey.get_bytes(), compressed)
+ return privkey, pubkey
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index c834086b6f..e5df29b135 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -210,7 +210,6 @@ BASE_SCRIPTS = [
'rpc_users.py',
'rpc_whitelist.py',
'feature_proxy.py',
- 'feature_syscall_sandbox.py',
'wallet_signrawtransactionwithwallet.py --legacy-wallet',
'wallet_signrawtransactionwithwallet.py --descriptors',
'rpc_signrawtransactionwithkey.py',
diff --git a/test/functional/wallet_blank.py b/test/functional/wallet_blank.py
index eda3fda35b..4836eba3b2 100755
--- a/test/functional/wallet_blank.py
+++ b/test/functional/wallet_blank.py
@@ -10,11 +10,10 @@ from test_framework.address import (
ADDRESS_BCRT1_UNSPENDABLE,
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR,
)
-from test_framework.key import ECKey
from test_framework.util import (
assert_equal,
)
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
class WalletBlankTest(BitcoinTestFramework):
@@ -50,10 +49,8 @@ class WalletBlankTest(BitcoinTestFramework):
assert_equal(info["descriptors"], False)
assert_equal(info["blank"], True)
- eckey = ECKey()
- eckey.generate(compressed=comp)
-
- wallet.importpubkey(eckey.get_pubkey().get_bytes().hex())
+ _, pubkey = generate_keypair(compressed=comp)
+ wallet.importpubkey(pubkey.hex())
assert_equal(wallet.getwalletinfo()["blank"], False)
def test_importprivkey(self):
@@ -67,10 +64,7 @@ class WalletBlankTest(BitcoinTestFramework):
assert_equal(info["descriptors"], False)
assert_equal(info["blank"], True)
- eckey = ECKey()
- eckey.generate(compressed=comp)
- wif = bytes_to_wif(eckey.get_bytes(), eckey.is_compressed)
-
+ wif, _ = generate_keypair(compressed=comp, wif=True)
wallet.importprivkey(wif)
assert_equal(wallet.getwalletinfo()["blank"], False)
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index a4e6f96cce..75b507c387 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -7,13 +7,13 @@
from test_framework.address import key_to_p2wpkh
from test_framework.descriptors import descsum_create
-from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
-from test_framework.wallet_util import bytes_to_wif, generate_wif_key
+from test_framework.wallet_util import generate_keypair
+
EMPTY_PASSPHRASE_MSG = "Empty string given as passphrase, wallet will not be encrypted."
LEGACY_WALLET_MSG = "Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future."
@@ -50,14 +50,12 @@ class CreateWalletTest(BitcoinTestFramework):
w1.importpubkey(w0.getaddressinfo(address1)['pubkey'])
self.log.info('Test that private keys cannot be imported')
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
+ privkey, pubkey = generate_keypair(wif=True)
assert_raises_rpc_error(-4, 'Cannot import private keys to a wallet with private keys disabled', w1.importprivkey, privkey)
if self.options.descriptors:
result = w1.importdescriptors([{'desc': descsum_create('wpkh(' + privkey + ')'), 'timestamp': 'now'}])
else:
- result = w1.importmulti([{'scriptPubKey': {'address': key_to_p2wpkh(eckey.get_pubkey().get_bytes())}, 'timestamp': 'now', 'keys': [privkey]}])
+ result = w1.importmulti([{'scriptPubKey': {'address': key_to_p2wpkh(pubkey)}, 'timestamp': 'now', 'keys': [privkey]}])
assert not result[0]['success']
assert 'warnings' not in result[0]
assert_equal(result[0]['error']['code'], -4)
@@ -77,7 +75,7 @@ class CreateWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getnewaddress)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getrawchangeaddress)
# Import private key
- w3.importprivkey(generate_wif_key())
+ w3.importprivkey(generate_keypair(wif=True)[0])
# Imported private keys are currently ignored by the keypool
assert_equal(w3.getwalletinfo()['keypoolsize'], 0)
assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getnewaddress)
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index c88e0b3f6e..46706d6ad2 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -10,7 +10,6 @@ from itertools import product
from math import ceil
from test_framework.descriptors import descsum_create
-from test_framework.key import ECKey
from test_framework.messages import (
COIN,
)
@@ -25,7 +24,7 @@ from test_framework.util import (
count_bytes,
find_vout_for_address,
)
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \
"Please allow other inputs to be automatically selected or include more coins manually"
@@ -999,11 +998,7 @@ class RawTransactionsTest(BitcoinTestFramework):
def test_external_inputs(self):
self.log.info("Test funding with external inputs")
-
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
-
+ privkey, _ = generate_keypair(wif=True)
self.nodes[2].createwallet("extfund")
wallet = self.nodes[2].get_wallet_rpc("extfund")
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index 77b407579f..5fe7c4b591 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -7,7 +7,6 @@ from decimal import Decimal
from test_framework.address import key_to_p2wpkh
from test_framework.blocktools import COINBASE_MATURITY
-from test_framework.key import ECKey
from test_framework.messages import (
CMerkleBlock,
from_hex,
@@ -17,7 +16,7 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
class ImportPrunedFundsTest(BitcoinTestFramework):
@@ -40,10 +39,8 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
# pubkey
address2 = self.nodes[0].getnewaddress()
# privkey
- eckey = ECKey()
- eckey.generate()
- address3_privkey = bytes_to_wif(eckey.get_bytes())
- address3 = key_to_p2wpkh(eckey.get_pubkey().get_bytes())
+ address3_privkey, address3_pubkey = generate_keypair(wif=True)
+ address3 = key_to_p2wpkh(address3_pubkey)
self.nodes[0].importprivkey(address3_privkey)
# Check only one address
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index bfca344fd1..a19a3ac2cb 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -7,7 +7,6 @@
from test_framework.address import key_to_p2wpkh
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.descriptors import descsum_create
-from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import MAX_BIP125_RBF_SEQUENCE
from test_framework.util import (
@@ -15,7 +14,7 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
from decimal import Decimal
@@ -202,10 +201,8 @@ class ListSinceBlockTest(BitcoinTestFramework):
self.sync_all()
# share utxo between nodes[1] and nodes[2]
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
- address = key_to_p2wpkh(eckey.get_pubkey().get_bytes())
+ privkey, pubkey = generate_keypair(wif=True)
+ address = key_to_p2wpkh(pubkey)
self.nodes[2].sendtoaddress(address, 10)
self.generate(self.nodes[2], 6)
self.nodes[2].importprivkey(privkey)
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index ac3ec06eec..d7bb6ab1e7 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -9,7 +9,6 @@ from itertools import product
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create
-from test_framework.key import ECKey
from test_framework.messages import (
ser_compact_size,
WITNESS_SCALE_FACTOR,
@@ -22,7 +21,8 @@ from test_framework.util import (
assert_raises_rpc_error,
count_bytes,
)
-from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet_util import generate_keypair
+
class WalletSendTest(BitcoinTestFramework):
def add_options(self, parser):
@@ -500,9 +500,7 @@ class WalletSendTest(BitcoinTestFramework):
assert res["complete"]
self.log.info("External outputs")
- eckey = ECKey()
- eckey.generate()
- privkey = bytes_to_wif(eckey.get_bytes())
+ privkey, _ = generate_keypair(wif=True)
self.nodes[1].createwallet("extsend")
ext_wallet = self.nodes[1].get_wallet_rpc("extsend")
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
index c414147c65..3e7c613e55 100755
--- a/test/functional/wallet_signer.py
+++ b/test/functional/wallet_signer.py
@@ -45,9 +45,6 @@ class WalletSignerTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- # The experimental syscall sandbox feature (-sandbox) is not compatible with -signer (which
- # invokes execve).
- self.disable_syscall_sandbox = True
self.extra_args = [
[],
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index af21e7b956..d953f48584 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -6,6 +6,7 @@
"""
from concurrent.futures import ThreadPoolExecutor, as_completed
+from pathlib import Path
import argparse
import configparser
import logging
@@ -42,6 +43,11 @@ def main():
help='If true, run fuzzing binaries under the valgrind memory error detector',
)
parser.add_argument(
+ "--empty_min_time",
+ type=int,
+ help="If set, run at least this long, if the existing fuzz inputs directory is empty.",
+ )
+ parser.add_argument(
'-x',
'--exclude',
help="A comma-separated list of targets to exclude",
@@ -76,6 +82,7 @@ def main():
)
args = parser.parse_args()
+ args.corpus_dir = Path(args.corpus_dir)
# Set up logging
logging.basicConfig(
@@ -180,6 +187,7 @@ def main():
src_dir=config['environment']['SRCDIR'],
build_dir=config["environment"]["BUILDDIR"],
use_valgrind=args.valgrind,
+ empty_min_time=args.empty_min_time,
)
@@ -251,16 +259,22 @@ def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir)
future.result()
-def run_once(*, fuzz_pool, corpus, test_list, src_dir, build_dir, use_valgrind):
+def run_once(*, fuzz_pool, corpus, test_list, src_dir, build_dir, use_valgrind, empty_min_time):
jobs = []
for t in test_list:
- corpus_path = os.path.join(corpus, t)
+ corpus_path = corpus / t
os.makedirs(corpus_path, exist_ok=True)
args = [
os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
- '-runs=1',
- corpus_path,
]
+ empty_dir = not any(corpus_path.iterdir())
+ if empty_min_time and empty_dir:
+ args += [f"-max_total_time={empty_min_time}"]
+ else:
+ args += [
+ "-runs=1",
+ corpus_path,
+ ]
if use_valgrind:
args = ['valgrind', '--quiet', '--error-exitcode=1'] + args
diff --git a/test/lint/lint-assertions.py b/test/lint/lint-assertions.py
index e7eecebce5..6da59b0d48 100755
--- a/test/lint/lint-assertions.py
+++ b/test/lint/lint-assertions.py
@@ -45,6 +45,16 @@ def main():
":(exclude)src/rpc/server.cpp",
], "CHECK_NONFATAL(condition) or NONFATAL_UNREACHABLE should be used instead of assert for RPC code.")
+ # The `BOOST_ASSERT` macro requires to `#include boost/assert.hpp`,
+ # which is an unnecessary Boost dependency.
+ exit_code |= git_grep([
+ "-E",
+ r"BOOST_ASSERT *\(.*\);",
+ "--",
+ "*.cpp",
+ "*.h",
+ ], "BOOST_ASSERT must be replaced with Assert, BOOST_REQUIRE, or BOOST_CHECK.")
+
sys.exit(exit_code)
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 8808a83e32..74703b04ec 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -23,6 +23,7 @@ implicit-integer-sign-change:crc32c/
# implicit-integer-sign-change in FuzzedDataProvider's ConsumeIntegralInRange
implicit-integer-sign-change:FuzzedDataProvider.h
implicit-integer-sign-change:minisketch/
+implicit-signed-integer-truncation:*/include/c++/
implicit-signed-integer-truncation:leveldb/
implicit-unsigned-integer-truncation:*/include/c++/
implicit-unsigned-integer-truncation:leveldb/