diff options
-rw-r--r-- | .cirrus.yml | 39 | ||||
-rwxr-xr-x | ci/test/00_setup_env_native_asan.sh | 6 | ||||
-rwxr-xr-x | ci/test/00_setup_env_native_fuzz.sh | 1 | ||||
-rwxr-xr-x | ci/test/04_install.sh | 3 | ||||
-rw-r--r-- | src/init.cpp | 6 | ||||
-rw-r--r-- | src/serialize.h | 187 | ||||
-rw-r--r-- | src/test/fuzz/addrman.cpp | 33 | ||||
-rwxr-xr-x | test/functional/mempool_datacarrier.py | 30 |
8 files changed, 131 insertions, 174 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 542bbb6ee6..4a6e73ac85 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -14,8 +14,24 @@ cirrus_ephemeral_worker_template_env: &CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV persistent_worker_template_env: &PERSISTENT_WORKER_TEMPLATE_ENV RESTART_CI_DOCKER_BEFORE_RUN: "1" +# https://cirrus-ci.org/guide/persistent-workers/ +# +# It is possible to select a specific persistent worker by label. Refer to the +# Cirrus CI docs for more details. +# +# Generally, a persistent worker must run Ubuntu 23.04+ or Debian 12+. +# Specifically, +# - apt-get is required due to PACKAGE_MANAGER_INSTALL +# - podman-docker-4.1+ is required due to the use of `podman` when +# RESTART_CI_DOCKER_BEFORE_RUN is set and 4.1+ due to the bugfix in 4.1 +# (https://github.com/bitcoin/bitcoin/pull/21652) +# - The ./ci/ depedencies should be installed: +# apt update && apt install screen python3 bash podman-docker curl -y +# +# The following specific types should exist, with the following requirements: +# - lunar: For a machine running the Linux kernel shipped with Ubuntu Lunar 23.04. The machine is recommended to have 4 CPUs and 16 GB of memory. persistent_worker_template: &PERSISTENT_WORKER_TEMPLATE - persistent_worker: {} # https://cirrus-ci.org/guide/persistent-workers/ + persistent_worker: {} # Only use this if the task does not care about the type at all # https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks filter_template: &FILTER_TEMPLATE @@ -257,22 +273,17 @@ task: task: name: '[ASan + LSan + UBSan + integer, no depends, USDT] [lunar]' + enable_bpfcc_script: + # In the image build step, no external environment variables are available, + # so any settings will need to be written to the settings env file: + - sed -i "s|\${CIRRUS_CI}|true|g" ./ci/test/00_setup_env_native_asan.sh << : *GLOBAL_TASK_TEMPLATE - # We can't use a 'container' for the USDT interface tests as the CirrusCI - # containers don't have privileges to hook into bitcoind. CirrusCI uses - # Google Compute Engine instances: https://cirrus-ci.org/guide/custom-vms/ - # Images can be found here: https://cloud.google.com/compute/docs/images/os-details - compute_engine_instance: - image_project: ubuntu-os-cloud - image: family/ubuntu-2304-amd64 # https://cirrus-ci.org/guide/custom-vms/#custom-compute-engine-vms - cpu: 4 - disk: 100 - memory: 12G + persistent_worker: + labels: + type: lunar # Must use the lunar-specific worker (needed for USDT functional tests) env: - << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV - HOME: /root/ # Only needed for compute_engine_instance + << : *PERSISTENT_WORKER_TEMPLATE_ENV FILE_ENV: "./ci/test/00_setup_env_native_asan.sh" - MAKEJOBS: "-j4" # Avoid excessive memory use task: name: '[fuzzer,address,undefined,integer, no depends] [lunar]' diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh index a5c80c2afc..dff86b1ffe 100755 --- a/ci/test/00_setup_env_native_asan.sh +++ b/ci/test/00_setup_env_native_asan.sh @@ -8,9 +8,11 @@ export LC_ALL=C.UTF-8 # Only install BCC tracing packages in Cirrus CI. if [[ "${CIRRUS_CI}" == "true" ]]; then - export BPFCC_PACKAGE="bpfcc-tools" + BPFCC_PACKAGE="bpfcc-tools linux-headers-$(uname --kernel-release)" + export CI_CONTAINER_CAP="--privileged -v /sys/kernel:/sys/kernel:rw" else - export BPFCC_PACKAGE="" + BPFCC_PACKAGE="" + export CI_CONTAINER_CAP="--cap-add SYS_PTRACE" # If run with (ASan + LSan), the container needs access to ptrace (https://github.com/google/sanitizers/issues/764) fi export CONTAINER_NAME=ci_native_asan diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh index 15785d1613..bfd51be6d0 100755 --- a/ci/test/00_setup_env_native_fuzz.sh +++ b/ci/test/00_setup_env_native_fuzz.sh @@ -14,6 +14,7 @@ export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false export RUN_FUZZ_TESTS=true export GOAL="install" +export CI_CONTAINER_CAP="--cap-add SYS_PTRACE" # If run with (ASan + LSan), the container needs access to ptrace (https://github.com/google/sanitizers/issues/764) export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined,float-divide-by-zero,integer \ CC='clang-16 -ftrivial-auto-var-init=pattern' CXX='clang++-16 -ftrivial-auto-var-init=pattern'" export CCACHE_MAXSIZE=200M diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh index ff43698994..205c79328a 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -18,9 +18,6 @@ export ASAN_OPTIONS="detect_stack_use_after_return=1:check_initialization_order= export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan" export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan" export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1" -if [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764) - CI_CONTAINER_CAP="--cap-add SYS_PTRACE" -fi if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then # Export all env vars to avoid missing some. diff --git a/src/init.cpp b/src/init.cpp index c11f100139..4d526bd0de 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -590,7 +590,11 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks<std::chrono::hours>(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); - argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-datacarriersize", + strprintf("Relay and mine transactions whose data-carrying raw scriptPubKey " + "is of this size or less (default: %u)", + MAX_OP_RETURN_RELAY), + ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", DEFAULT_MEMPOOL_FULL_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); diff --git a/src/serialize.h b/src/serialize.h index 0cda0ac7d5..39f2c0f3ae 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -642,23 +642,14 @@ template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_st * prevector * prevectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. */ -template<typename Stream, unsigned int N, typename T> void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&); -template<typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&); template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v); -template<typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&); -template<typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&); template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v); /** * vector * vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. */ -template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&); -template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&); -template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&); template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v); -template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&); -template<typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&); template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v); /** @@ -751,122 +742,82 @@ void Unserialize(Stream& is, std::basic_string<C>& str) /** * prevector */ -template<typename Stream, unsigned int N, typename T> -void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&) -{ - WriteCompactSize(os, v.size()); - if (!v.empty()) - os.write(MakeByteSpan(v)); -} - -template<typename Stream, unsigned int N, typename T, typename V> -void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&) -{ - Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v)); -} - -template<typename Stream, unsigned int N, typename T> -inline void Serialize(Stream& os, const prevector<N, T>& v) -{ - Serialize_impl(os, v, T()); -} - - -template<typename Stream, unsigned int N, typename T> -void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&) -{ - // Limit size per read so bogus size value won't cause out of memory - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - while (i < nSize) - { - unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); - v.resize_uninitialized(i + blk); - is.read(AsWritableBytes(Span{&v[i], blk})); - i += blk; +template <typename Stream, unsigned int N, typename T> +void Serialize(Stream& os, const prevector<N, T>& v) +{ + if constexpr (std::is_same_v<T, unsigned char>) { + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write(MakeByteSpan(v)); + } else { + Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v)); } } -template<typename Stream, unsigned int N, typename T, typename V> -void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&) -{ - Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v)); -} -template<typename Stream, unsigned int N, typename T> -inline void Unserialize(Stream& is, prevector<N, T>& v) +template <typename Stream, unsigned int N, typename T> +void Unserialize(Stream& is, prevector<N, T>& v) { - Unserialize_impl(is, v, T()); + if constexpr (std::is_same_v<T, unsigned char>) { + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize_uninitialized(i + blk); + is.read(AsWritableBytes(Span{&v[i], blk})); + i += blk; + } + } else { + Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v)); + } } - /** * vector */ -template<typename Stream, typename T, typename A> -void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&) -{ - WriteCompactSize(os, v.size()); - if (!v.empty()) - os.write(MakeByteSpan(v)); -} - -template<typename Stream, typename T, typename A> -void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&) -{ - // A special case for std::vector<bool>, as dereferencing - // std::vector<bool>::const_iterator does not result in a const bool& - // due to std::vector's special casing for bool arguments. - WriteCompactSize(os, v.size()); - for (bool elem : v) { - ::Serialize(os, elem); +template <typename Stream, typename T, typename A> +void Serialize(Stream& os, const std::vector<T, A>& v) +{ + if constexpr (std::is_same_v<T, unsigned char>) { + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write(MakeByteSpan(v)); + } else if constexpr (std::is_same_v<T, bool>) { + // A special case for std::vector<bool>, as dereferencing + // std::vector<bool>::const_iterator does not result in a const bool& + // due to std::vector's special casing for bool arguments. + WriteCompactSize(os, v.size()); + for (bool elem : v) { + ::Serialize(os, elem); + } + } else { + Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v)); } } -template<typename Stream, typename T, typename A, typename V> -void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&) -{ - Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v)); -} -template<typename Stream, typename T, typename A> -inline void Serialize(Stream& os, const std::vector<T, A>& v) +template <typename Stream, typename T, typename A> +void Unserialize(Stream& is, std::vector<T, A>& v) { - Serialize_impl(os, v, T()); -} - - -template<typename Stream, typename T, typename A> -void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&) -{ - // Limit size per read so bogus size value won't cause out of memory - v.clear(); - unsigned int nSize = ReadCompactSize(is); - unsigned int i = 0; - while (i < nSize) - { - unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); - v.resize(i + blk); - is.read(AsWritableBytes(Span{&v[i], blk})); - i += blk; + if constexpr (std::is_same_v<T, unsigned char>) { + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read(AsWritableBytes(Span{&v[i], blk})); + i += blk; + } + } else { + Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v)); } } -template<typename Stream, typename T, typename A, typename V> -void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&) -{ - Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v)); -} - -template<typename Stream, typename T, typename A> -inline void Unserialize(Stream& is, std::vector<T, A>& v) -{ - Unserialize_impl(is, v, T()); -} - - /** * pair @@ -1039,28 +990,16 @@ public: int GetVersion() const { return nVersion; } }; -template<typename Stream> -void SerializeMany(Stream& s) -{ -} - -template<typename Stream, typename Arg, typename... Args> -void SerializeMany(Stream& s, const Arg& arg, const Args&... args) -{ - ::Serialize(s, arg); - ::SerializeMany(s, args...); -} - -template<typename Stream> -inline void UnserializeMany(Stream& s) +template <typename Stream, typename... Args> +void SerializeMany(Stream& s, const Args&... args) { + (::Serialize(s, args), ...); } -template<typename Stream, typename Arg, typename... Args> -inline void UnserializeMany(Stream& s, Arg&& arg, Args&&... args) +template <typename Stream, typename... Args> +inline void UnserializeMany(Stream& s, Args&&... args) { - ::Unserialize(s, arg); - ::UnserializeMany(s, args...); + (::Unserialize(s, args), ...); } template<typename Stream, typename... Args> diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 8ac1330dcb..02df4590de 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -263,40 +263,21 @@ FUZZ_TARGET(addrman, .init = initialize_addrman) [&] { std::vector<CAddress> addresses; LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { - const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider); - if (!opt_address) { - break; - } - addresses.push_back(*opt_address); - } - const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider); - if (opt_net_addr) { - addr_man.Add(addresses, *opt_net_addr, std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); + addresses.push_back(ConsumeAddress(fuzzed_data_provider)); } + addr_man.Add(addresses, ConsumeNetAddr(fuzzed_data_provider), std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Good(*opt_service, NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); - } + addr_man.Good(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); - } + addr_man.Attempt(ConsumeService(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.Connected(*opt_service, NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); - } + addr_man.Connected(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); }, [&] { - const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider); - if (opt_service) { - addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); - } + addr_man.SetServices(ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); }); } const AddrMan& const_addr_man{addr_man}; @@ -334,4 +315,4 @@ FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman) data_stream << addr_man1; data_stream >> addr_man2; assert(addr_man1 == addr_man2); -} +}
\ No newline at end of file diff --git a/test/functional/mempool_datacarrier.py b/test/functional/mempool_datacarrier.py index c370d8fa91..951bf37ae8 100755 --- a/test/functional/mempool_datacarrier.py +++ b/test/functional/mempool_datacarrier.py @@ -22,16 +22,18 @@ from test_framework.wallet import MiniWallet class DataCarrierTest(BitcoinTestFramework): def set_test_params(self): - self.num_nodes = 3 + self.num_nodes = 4 self.extra_args = [ [], ["-datacarrier=0"], - ["-datacarrier=1", f"-datacarriersize={MAX_OP_RETURN_RELAY - 1}"] + ["-datacarrier=1", f"-datacarriersize={MAX_OP_RETURN_RELAY - 1}"], + ["-datacarrier=1", f"-datacarriersize=2"], ] - def test_null_data_transaction(self, node: TestNode, data: bytes, success: bool) -> None: + def test_null_data_transaction(self, node: TestNode, data, success: bool) -> None: tx = self.wallet.create_self_transfer(fee_rate=0)["tx"] - tx.vout.append(CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN, data]))) + data = [] if data is None else [data] + tx.vout.append(CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN] + data))) tx.vout[0].nValue -= tx.get_vsize() # simply pay 1sat/vbyte fee tx_hex = tx.serialize().hex() @@ -49,6 +51,8 @@ class DataCarrierTest(BitcoinTestFramework): default_size_data = random_bytes(MAX_OP_RETURN_RELAY - 3) too_long_data = random_bytes(MAX_OP_RETURN_RELAY - 2) small_data = random_bytes(MAX_OP_RETURN_RELAY - 4) + one_byte = random_bytes(1) + zero_bytes = random_bytes(0) self.log.info("Testing null data transaction with default -datacarrier and -datacarriersize values.") self.test_null_data_transaction(node=self.nodes[0], data=default_size_data, success=True) @@ -65,6 +69,24 @@ class DataCarrierTest(BitcoinTestFramework): self.log.info("Testing a null data transaction with a size smaller than accepted by -datacarriersize.") self.test_null_data_transaction(node=self.nodes[2], data=small_data, success=True) + self.log.info("Testing a null data transaction with no data.") + self.test_null_data_transaction(node=self.nodes[0], data=None, success=True) + self.test_null_data_transaction(node=self.nodes[1], data=None, success=False) + self.test_null_data_transaction(node=self.nodes[2], data=None, success=True) + self.test_null_data_transaction(node=self.nodes[3], data=None, success=True) + + self.log.info("Testing a null data transaction with zero bytes of data.") + self.test_null_data_transaction(node=self.nodes[0], data=zero_bytes, success=True) + self.test_null_data_transaction(node=self.nodes[1], data=zero_bytes, success=False) + self.test_null_data_transaction(node=self.nodes[2], data=zero_bytes, success=True) + self.test_null_data_transaction(node=self.nodes[3], data=zero_bytes, success=True) + + self.log.info("Testing a null data transaction with one byte of data.") + self.test_null_data_transaction(node=self.nodes[0], data=one_byte, success=True) + self.test_null_data_transaction(node=self.nodes[1], data=one_byte, success=False) + self.test_null_data_transaction(node=self.nodes[2], data=one_byte, success=True) + self.test_null_data_transaction(node=self.nodes[3], data=one_byte, success=False) + if __name__ == '__main__': DataCarrierTest().main() |